# Prisma Browser Management API: Developer Guide (full text) > The complete Prisma Browser Management API developer guide, concatenated into one Markdown file for use with ChatGPT, Claude, or any LLM. > Source: https://pan.dev/prisma-browser/guide/guide-overview > Generated: 2026-09-18 16:27 UTC > > How to use this file. It is the developer guide, not the API reference. > The guides teach the request shapes and the workflows, and they name only > a few controls, fields and enum values as examples. Every list of controls > or fields here is illustrative, never exhaustive, and it can lag a release. > > The OpenAPI specification is the authoritative and current catalog of every > endpoint, control, field, enum value and validation rule: > https://pan.dev/spec/prisma-browser-management.yaml > > Before telling a user that a control, field or endpoint does not exist, > check the specification. Absence from this file means only that no guide > page happens to mention it. --- # Prisma Browser API: Developer Guide _Source: https://pan.dev/prisma-browser/guide/guide-overview | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ The guide for developers integrating with the Prisma Browser Management API. It explains how the API works, the patterns you must understand to automate safely (draft and publish, delta patch), and walks through common use cases with runnable code. **Using an LLM?** [Download the full guide as one Markdown file](https://pan.dev/prisma-browser/llms-full.txt) to drop into ChatGPT, Claude, or any LLM, or point your tools at [/prisma-browser/llms.txt](https://pan.dev/prisma-browser/llms.txt). --- ## Who this is for Developers who want to manage Prisma Browser configuration programmatically instead of clicking through Strata Cloud Manager. The guide assumes you are comfortable with REST, JSON, and OAuth2, but not with Prisma Browser internals. For information about how the product works, see the [Prisma Browser User Guide](https://docs.paloaltonetworks.com/prisma-access-browser). --- ## Start here New to the API? Start with [Getting started](https://pan.dev/prisma-browser/guide/getting-started): get a token, read your live policy, create a test application, and publish it in under 10 minutes. --- ## Basics Read these to build a mental model before you write automation. | Page | Covers | |---|---| | [Authentication](https://pan.dev/prisma-browser/guide/authentication) | OAuth2 client-credentials, Bearer token, roles | | [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish) | `configurationVersion`, draft vs active, the publish action | | [Delta patch](https://pan.dev/prisma-browser/guide/delta-patch) | Scalar set vs full-list replace vs add/remove delta | | [Partial publish](https://pan.dev/prisma-browser/guide/draft-and-publish#partial-publish) | Publish only specific objects, not the whole draft | | [Direct actions](https://pan.dev/prisma-browser/guide/direct-actions) | Immediate actions on live users and devices, outside the draft model | | [Pagination](https://pan.dev/prisma-browser/guide/pagination) | Cursor pagination, the `pageInfo` envelope, and opt-in `totalCount` | | [Errors](https://pan.dev/prisma-browser/guide/errors) | The typed error envelope, error codes, and how to handle common statuses | --- ## Inventory Observed identities and endpoints in your tenant. Inventory objects are read-only apart from direct actions. | Page | Object | |---|---| | [Users](https://pan.dev/prisma-browser/guide/users) | Observed identities; list, filter, and act on users | | [Devices](https://pan.dev/prisma-browser/guide/devices) | Observed endpoints; list, filter by posture, and act on devices | --- ## Policy objects The objects you create and reference from rules. | Page | Object | |---|---| | [Applications](https://pan.dev/prisma-browser/guide/applications) | Overview of application types and shared operations | | [Custom apps](https://pan.dev/prisma-browser/guide/applications-custom) | Define a SaaS or web application by its URLs | | [Private apps](https://pan.dev/prisma-browser/guide/applications-private) | Define an internal application routed through Prisma Access | | [Non-web apps](https://pan.dev/prisma-browser/guide/applications-non-web) | Define an RDP or SSH remote connection by address, protocol, and port | | [Desktop apps](https://pan.dev/prisma-browser/guide/applications-desktop) | Define a local desktop application by its executables | | [Application groups](https://pan.dev/prisma-browser/guide/application-groups) | Group applications and reference the group from a rule | | [User groups](https://pan.dev/prisma-browser/guide/user-groups) | Local and identity provider (IdP)-synced groups; membership edits | | [Device groups](https://pan.dev/prisma-browser/guide/device-groups) | Posture-based device membership | --- ## Policy How policy is structured, and the fields and structure specific to each policy type. | Page | Covers | |---|---| | [Policy overview](https://pan.dev/prisma-browser/guide/rules) | Rules, sections, positions, scope, action vs controls, prompts, custom messages | | [Sign-in rules](https://pan.dev/prisma-browser/guide/sign-in-rules) | Mandatory fields and structure for sign-in rules | | [Access and data rules](https://pan.dev/prisma-browser/guide/access-and-data-rules) | Applications, access, login controls, data controls, Data Loss Prevention (DLP) | | [Security rules](https://pan.dev/prisma-browser/guide/security-rules) | Controls-based rules for hardening the Prisma Browser | | [Customization rules](https://pan.dev/prisma-browser/guide/customization-rules) | Controls-based rules for branding and look-and-feel | | [Assets](https://pan.dev/prisma-browser/guide/assets) | Upload logos, icons, images, and Proxy Auto-Configuration (PAC) files, then reference them by ID | --- ## User requests | Page | Covers | |---|---| | [User requests](https://pan.dev/prisma-browser/guide/user-requests) | How admin-approval prompts generate requests, and the API to list, approve, decline, and revoke them | --- ## Use cases (task walkthroughs) End-to-end, copy-pasteable recipes for real jobs. | Page | Task | |---|---| | [Create and publish a rule](https://pan.dev/prisma-browser/guide/create-and-publish-a-security-rule) | Author a rule on the draft, verify it, publish it | | [Change a rule's scope](https://pan.dev/prisma-browser/guide/change-a-rule-scope) | Add, remove, or retarget who a rule applies to | | [Reorder rules and sections](https://pan.dev/prisma-browser/guide/reorder-rules-and-sections) | Change evaluation order with full or partial position updates | | [Govern URLs with an allow/block list](https://pan.dev/prisma-browser/guide/manage-allow-block-list) | Three ways to govern a list of URLs from automation | | [Bulk-sync a rule's applications](https://pan.dev/prisma-browser/guide/bulk-update-rule-applications-with-delta) | Keep a rule's application list in sync with an external source | | [Manage login and data controls](https://pan.dev/prisma-browser/guide/manage-login-and-data-controls) | Set login and data controls on an access and data rule | | [Set a DLP profile on a rule](https://pan.dev/prisma-browser/guide/set-a-dlp-profile) | Discover DLP profile IDs and attach one to a rule | | [Require an IdP authentication factor](https://pan.dev/prisma-browser/guide/require-idp-authentication-factor) | Discover a CIE authentication profile and require it as a factor on a security rule | | [Rotate cloud storage credentials](https://pan.dev/prisma-browser/guide/rotate-cloud-storage-credentials) | Configure a OneDrive/Drive provider and rotate its credentials with no downtime | | [Manage allowed browser extensions](https://pan.dev/prisma-browser/guide/manage-allowed-extensions) | Curate the allowed-extensions list on a security rule | | [Add or remove users on a rule](https://pan.dev/prisma-browser/guide/manage-users-on-a-rule) | Add or remove users directly, or via a local user group | | [Roll out a rule gradually](https://pan.dev/prisma-browser/guide/gradual-rollout-with-user-group) | Expand a rule's scope batch by batch over time | | [Handle user access requests](https://pan.dev/prisma-browser/guide/handle-user-access-requests) | Poll, approve/decline, and revoke admin-approval requests | | [Publish a single object (partial publish)](https://pan.dev/prisma-browser/guide/automate-with-partial-publish) | Edit an object and publish only that object | | [Suspend a compromised user](https://pan.dev/prisma-browser/guide/suspend-a-compromised-user) | Suspend a user immediately (incident response, offboarding) | | [Quarantine a lost or compromised device](https://pan.dev/prisma-browser/guide/quarantine-a-device) | Suspend, archive, or remove a device immediately | --- # Getting started _Source: https://pan.dev/prisma-browser/guide/getting-started | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ This page takes you through one full loop with the Prisma Browser Management API in under 10 minutes: **get a token, read your live policy, create a test application, and publish it.** For what the API is and who it is for, see the [Introduction](https://pan.dev/prisma-browser/guide/guide-overview). We create a throwaway **custom application** rather than touching a rule, so you can see a real write and publish end to end without changing anything that is enforced on users. You delete it at the end. --- ## Prerequisites - You need a **service account** (client ID and secret) and your tenant's **TSG (Tenant Service Group) ID**. To create or publish resources, assign the **Super User** role to your service account. If you only need to read data, the **View-Only Administrator** role is sufficient. The Prisma Browser currently supports only these two roles. - To create a service account and obtain these credentials, follow the steps in the [Add a Service Account Through Common Services guide](https://docs.paloaltonetworks.com/common-services/identity-and-access-access-management/manage-identity-and-access/add-service-accounts). - You'll use the client ID, secret, and TSG ID to generate an OAuth 2.0 token for API access. See the detailed instructions at [PAN SASE API: Getting Started](https://pan.dev/sase/docs/getstarted/). ### Set your credentials as environment variables The block below is not a programming language: these are **shell environment variables** (bash/zsh on macOS or Linux). You run them once in your terminal, and every example on this page reads them with `$PB_...` so you never paste a secret into a command. ```bash export PB_CLIENT_ID='name@1234567890.iam.panserviceaccount.com' # service account client ID export PB_CLIENT_SECRET='your-secret' # service account secret export PB_TSG='1234567890' # your Tenant Service Group ID export PB_AUTH_URL='https://auth.apps.paloaltonetworks.com/oauth2/access_token' # OAuth2 token endpoint export PB_API_BASE='https://api.sase.paloaltonetworks.com/seb-api/v1' # API base URL ``` | Variable | What it is | Where it comes from | |---|---|---| | `PB_CLIENT_ID` | Service account client ID | [Service account](https://pan.dev/sase/docs/service-accounts) creation | | `PB_CLIENT_SECRET` | Service account secret (shown once) | Service account creation | | `PB_TSG` | Your Tenant Service Group ID, used as the token scope | [Your TSG](https://pan.dev/sase/docs/tenant-service-groups) | | `PB_AUTH_URL` | OAuth2 token endpoint | Fixed (prod value shown) | | `PB_API_BASE` | Prisma Browser API base URL | Fixed (prod value shown) | **Note** On **Windows PowerShell**, set variables with `$env:PB_CLIENT_ID = '...'` and reference them as `$env:PB_CLIENT_ID`. The curl examples below otherwise work the same. --- ## 1. Get a token If you completed the [Authentication](https://pan.dev/prisma-browser/guide/authentication) guide and already have `PB_TOKEN` set, skip to step 2. Exchange your service account for a short-lived Bearer token and capture it in `PB_TOKEN`: ```bash export PB_TOKEN="$(curl -sS -u "$PB_CLIENT_ID:$PB_CLIENT_SECRET" \ -d 'grant_type=client_credentials' -d "scope=tsg_id:$PB_TSG" \ "$PB_AUTH_URL" | python3 -c 'import sys,json; print(json.load(sys.stdin)["access_token"])')" echo "${PB_TOKEN:0:12}..." # sanity check: prints the first few chars ``` The token lasts about 15 minutes. See [Authentication](https://pan.dev/prisma-browser/guide/authentication) for the full flow and a reusable refresh helper. --- ## 2. Read your live policy Start with a read so you can see real data. List your **Access and Data** rules (this is a read, so View-Only Administrator is enough): ```bash curl -sS "$PB_API_BASE/policy/access-and-data?limit=5" \ -H "Authorization: Bearer $PB_TOKEN" ``` Response (`200`): ```json { "pageInfo": { "hasNextPage": false, "cursor": "", "totalCount": 2 }, "data": [ { "type": "Section", "id": "0SREXAMPLEDEFAULTXXXXXXXXXX", "position": 1, "name": "Default" }, { "type": "Rule", "id": "0RLEXAMPLEACCESSRULEXXXXXXX", "position": 2, "name": "Finance data protection", "mode": "active" } // ... more rules and sections ], "metadata": { "configurationVersion": { "id": "0CV01EXAMPLEXXXXXXXXXXXXXXXXX", "status": "draft", "number": 0 } } } ``` You get a paginated envelope: `pageInfo`, a `data` array of rule and section summaries, and `metadata` describing the configuration version you read. Two things to notice, both core concepts: - **You read the `draft`.** Reads default to the draft configuration. Pass `?configurationVersion=active` to read what is live. This draft model is central to writing safely: see [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish). - **List items are summaries.** To get a rule's full configuration, call `GET /policy/access-and-data/rules/{id}`. --- ## 3. Create a test application Now make a write. Creating a **custom application** is the safest first write: it adds an object to the draft without changing any rule that is enforced. (See [Applications](https://pan.dev/prisma-browser/guide/applications) for the full object.) ```bash curl -sS -X POST "$PB_API_BASE/applications/type/custom" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "type": "custom", "name": "API quickstart test app", "description": "Created from the getting-started guide. Safe to delete.", "urls": [ { "url": "quickstart.example.com" } ] }' ``` Response (`201`): ```json { "id": "0AP01QUICKSTARTXXXXXXXXXXXXXX" } ``` You get back `201 Created` with the new application's ID. Capture it for the next steps: ```bash export APP_ID='0AP01QUICKSTARTXXXXXXXXXXXXXX' # use the id from the response ``` This write landed in the **draft**. It is not live yet. --- ## 4. Publish One action promotes the entire draft to a new active version: ```bash curl -sS -X POST "$PB_API_BASE/configuration-management/draft/publish" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{"description": "Add API quickstart test app"}' ``` A `201` means a new active version was created and your application is now live. Publish is **all-or-nothing** for the draft: everything currently staged goes live together. To publish only this one object, see [Partial publish](https://pan.dev/prisma-browser/guide/draft-and-publish#partial-publish). --- ## 5. Verify, then clean up Read the application back on the **active** version to confirm it went live: ```bash curl -sS "$PB_API_BASE/applications/type/custom/$APP_ID?configurationVersion=active" \ -H "Authorization: Bearer $PB_TOKEN" ``` Response (`200`): ```json { "type": "custom", "id": "0AP01QUICKSTARTXXXXXXXXXXXXXX", "name": "API quickstart test app", "description": "Created from the getting-started guide. Safe to delete.", "urls": [ "*://quickstart.example.com/*" ], "metadata": { "configurationVersion": { "id": "0CV01EXAMPLEXXXXXXXXXXXXXXXXX", "status": "active", "number": 1 } } // ... } ``` Then remove the test application and publish again so you leave nothing behind: ```bash curl -sS -X DELETE "$PB_API_BASE/applications/type/custom/$APP_ID" \ -H "Authorization: Bearer $PB_TOKEN" curl -sS -X POST "$PB_API_BASE/configuration-management/draft/publish" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{"description": "Remove API quickstart test app"}' ``` The `DELETE` returns `204` with an empty body, and the publish returns `201` when the new active version is created. That is the whole loop: **authenticate, read, write to the draft, publish, verify.** Everything else in the API is a variation on it. --- ## What to read next | If you want to... | Go to | |---|---| | Understand the token, roles, and refresh | [Authentication](https://pan.dev/prisma-browser/guide/authentication) | | Understand the draft/active model you used | [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish) | | Publish only specific objects, not the whole draft | [Partial publish](https://pan.dev/prisma-browser/guide/draft-and-publish#partial-publish) | | Add/remove list items without races | [Delta patch](https://pan.dev/prisma-browser/guide/delta-patch) | | Create the objects rules reference (applications, groups) | [Building blocks](https://pan.dev/prisma-browser/guide/applications) | | List and act on users and devices | [Users](https://pan.dev/prisma-browser/guide/users), [Devices](https://pan.dev/prisma-browser/guide/devices) | | Create a rule end-to-end | [Create and publish a rule](https://pan.dev/prisma-browser/guide/create-and-publish-a-security-rule) | | Understand the policy types and structure | [Policy overview](https://pan.dev/prisma-browser/guide/rules) | | Page through large lists | [Pagination](https://pan.dev/prisma-browser/guide/pagination) | | Handle failures | [Errors](https://pan.dev/prisma-browser/guide/errors) | --- ## Troubleshooting - **409 on publish:** the draft has no changes (nothing to publish). Usually safe to ignore. - **404 with an empty body (header `via: 1.1 google`):** the path prefix is wrong. Confirm it starts under `/seb-api/v1`. For auth errors (401/403), see [Authentication - Errors](https://pan.dev/prisma-browser/guide/authentication#errors-you-might-hit). --- # Authentication _Source: https://pan.dev/prisma-browser/guide/authentication | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ Every Prisma Browser API call is authenticated with an **OAuth2 client-credentials** Bearer token. **On this page:** what you need, permissions and roles, get a token, use the token, errors you might hit, good practices. The Prisma Browser uses the **same authentication as the rest of the SASE platform**: you exchange a service account's client ID and secret for a short-lived token scoped to your Tenant Service Group (TSG), then send that token on every request. There are no per-endpoint API keys and no mTLS client certificates to manage. For the platform-wide version of this flow, see the SASE [Get Started](https://pan.dev/sase/docs/getstarted/) docs. --- ## What you need This is the standard SASE setup. If you already make calls to another SASE API, you can reuse the same service account and TSG. There are three things to put in place: | # | What | Looks like | Set it up in Strata Cloud Manager | |---|---|---|---| | 1 | **TSG ID** (token scope) | `1234567890` | [Identify or create your TSG](https://pan.dev/sase/docs/tenant-service-groups) | | 2 | **Service account** (client ID + secret) | `name@1234567890.iam.panserviceaccount.com` + a long random secret shown once | [Identify or create a service account](https://pan.dev/sase/docs/service-accounts) | | 3 | **Role** on the service account | Super User or View-Only Administrator | [Assign one or more roles](https://pan.dev/sase/docs/roles) | **Note** Service accounts, TSGs, and roles are all managed in **Strata Cloud Manager** (the SASE platform), the same place other Strata Cloud Manager API credentials live. Without at least one role assigned, the token authenticates but every call returns `403`. See [Service accounts](https://pan.dev/sase/docs/service-accounts), [TSGs](https://pan.dev/sase/docs/tenant-service-groups), and [Roles](https://pan.dev/sase/docs/roles). --- ## Permissions and roles The API works with exactly **two predefined roles**. You assign one to the service account in Strata Cloud Manager, and it determines what the token can do. | Role | Access | Use it for | |---|---|---| | **Super User** | Full read and write | Any create, update, delete, or publish | | **View-Only Administrator** | Read only (`GET` succeeds, writes return `403`) | Read-only integrations, reporting, audits | - **Custom or granular RBAC is not supported today.** You cannot scope a service account to specific endpoints, policy types, or objects; it is either full read/write (Super User) or read-only (View-Only Administrator). Finer-grained roles are planned for a future release. - **Least privilege:** give a read-only integration the View-Only Administrator role, and reserve Super User for automation that actually writes. - These roles are the same ones that gate the **Strata Cloud Manager UI**, so API access has parity with what an administrator can do in Strata Cloud Manager. For the full permission model and how roles map to Prisma Browser features, see the [Prisma Browser product documentation](https://docs.paloaltonetworks.com/prisma-access-browser). --- ## Step 1: Get a token Exchange your credentials at the auth token endpoint using HTTP Basic auth (client ID and secret) and a form body that requests the client-credentials grant scoped to your TSG. This is the same exchange documented in the SASE [Create an access token](https://pan.dev/sase/docs/access-tokens) guide. The token URL is `https://auth.apps.paloaltonetworks.com/oauth2/access_token`. Set your credentials as environment variables first (do not paste secrets into commands): ```bash export PB_CLIENT_ID='name@1234567890.iam.panserviceaccount.com' export PB_CLIENT_SECRET='your-secret' export PB_TSG='1234567890' export PB_AUTH_URL='https://auth.apps.paloaltonetworks.com/oauth2/access_token' export PB_API_BASE='https://api.sase.paloaltonetworks.com/seb-api/v1' ``` ```bash curl -sS -u "$PB_CLIENT_ID:$PB_CLIENT_SECRET" \ -d 'grant_type=client_credentials' \ -d "scope=tsg_id:$PB_TSG" \ "$PB_AUTH_URL" ``` ### Response ```json { "access_token": "eyJhbGciOi...", "token_type": "Bearer", "expires_in": 899 } ``` The token is a JWT and lasts about **15 minutes** (`expires_in` is in seconds). Cache it and re-fetch when it is close to expiring; do not request a new token per call. --- ## Step 2: Use the token Send the token in the `Authorization` header on every API request: ``` Authorization: Bearer ``` Capture it into `PB_TOKEN` for the rest of the guide: ```bash export PB_API_BASE='https://api.sase.paloaltonetworks.com/seb-api/v1' export PB_TOKEN="$(curl -sS -u "$PB_CLIENT_ID:$PB_CLIENT_SECRET" \ -d 'grant_type=client_credentials' -d "scope=tsg_id:$PB_TSG" \ "$PB_AUTH_URL" | python3 -c 'import sys,json; print(json.load(sys.stdin)["access_token"])')" ``` A reusable Python helper used throughout the guide: ```python import os, time, requests _token = {"value": None, "exp": 0} def get_token(): if _token["value"] and time.time() < _token["exp"] - 60: return _token["value"] resp = requests.post( os.environ["PB_AUTH_URL"], auth=(os.environ["PB_CLIENT_ID"], os.environ["PB_CLIENT_SECRET"]), data={"grant_type": "client_credentials", "scope": f"tsg_id:{os.environ['PB_TSG']}"}, timeout=30, ) resp.raise_for_status() body = resp.json() _token["value"] = body["access_token"] _token["exp"] = time.time() + body.get("expires_in", 900) return _token["value"] def auth_headers(): return {"Authorization": f"Bearer {get_token()}"} ``` --- ## Errors you might hit | HTTP | Cause | Fix | |---|---|---| | 401 at the token URL | Wrong client ID / secret, or wrong `scope` format | Re-check credentials; scope must be `tsg_id:` | | 401 at the API | Token missing, malformed, or expired | Fetch a fresh token and retry | | 403 at the API | Token is valid but the service account lacks the role | Use a **Super User** account for writes | | 404 with empty body, only `via: 1.1 google` | Wrong path prefix | Ensure the path is under `/seb-api/v1` | --- ## Good practices - **Store secrets outside your code** (environment variables, a secrets manager). Never commit them. - **Reuse tokens** within their ~15-minute lifetime; refresh slightly early. - **Use least privilege**: a read-only integration should use a **View-Only Administrator** account. See [Permissions and roles](#permissions-and-roles). - The same token authorizes any Strata Cloud Manager service on `api.sase.paloaltonetworks.com`, not only the Prisma Browser, subject to the service account's roles. Next: make your first call in [Getting started](https://pan.dev/prisma-browser/guide/getting-started). --- # Draft and publish _Source: https://pan.dev/prisma-browser/guide/draft-and-publish | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ Prisma Browser policy is **versioned configuration**. Your changes do not go live the moment you make them. Instead, every change lands in a **draft**, and you make the draft live by **publishing** it, which creates a new **active** version. This is the single most important concept for writing automation safely. If you have used "staging then deploy", or git "working copy then commit", this will feel familiar: | Prisma Browser | git analogy | |---|---| | draft | working copy (uncommitted edits) | | publish | commit | | active version | the committed, live revision | **On this page:** the two versions, writes always target the draft, publishing, check pending changes, typical write flow, example, verify changes before publishing, common pitfalls. --- ## The two versions Every configuration entity (policy rules, sections, controls) has two states you work with day to day, selected with the `configurationVersion` query parameter on `GET` requests: | `configurationVersion` | Reads | |---|---| | `draft` (default) | Your pending, unpublished edits | | `active` | The live configuration enforced on devices | ```bash # Read pending edits (default) curl -sS "$PB_API_BASE/policy/security/rules/$RULE_ID" -H "Authorization: Bearer $PB_TOKEN" # Read what is actually live curl -sS "$PB_API_BASE/policy/security/rules/$RULE_ID?configurationVersion=active" \ -H "Authorization: Bearer $PB_TOKEN" ``` **Reading a past version is not supported** The parameter's schema also accepts a version number (`73`) or a version ID (`0CV...`), but **previous versions do not retain their configuration**, so these values do not return the configuration as it was at that version. Do not build automation on them. Use `active` to read what is live, and `GET /configuration-management/draft/pending-changes` to see what is staged. To keep a record of past configuration, export it yourself after each publish. --- ## Writes always target the draft **All mutations (`POST`, `PATCH`, `PUT`, `DELETE`) act on the draft only.** They do not accept a `configurationVersion`; if you send one, it is ignored. There is no way to write directly to the active configuration. This means you can stage as many changes as you like, review them, and only then make them live in one atomic publish. ```mermaid flowchart LR W["Write (POST / PATCH / DELETE)"] --> D["Draft"] D --> Pub["Publish"] Pub --> A["Active (enforced on devices)"] ``` --- ## Publishing Two separate endpoints publish the draft: ``` POST /seb-api/v1/configuration-management/draft/publish POST /seb-api/v1/configuration-management/draft/partial-publish ``` **Full publish** promotes every change currently in the draft at once. It takes a `description` and nothing else; there is no way to narrow its scope. **Partial publish** promotes only the objects you name in `entityIds`. The rest of the draft stays pending. **Caution** **Partial publish is not in the public API yet.** The endpoint is implemented but gated per tenant, so it is absent from the published API reference. Confirm it is enabled on your tenant before relying on it. **Note** **Non-policy objects only.** Partial publish supports user groups, application groups, applications, device groups, and tags. Naming a rule (`0RL`) or a section (`0SR`) returns `501 PARTIAL_PUBLISH_POLICY_UNSUPPORTED`. Publish policy changes with a full publish. ### Full publish ```bash curl -sS -X POST "$PB_API_BASE/configuration-management/draft/publish" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{"description": "CHG-00123: block developer tools on extensions"}' ``` A `201` means a new active version was created and everything staged in the draft is now live. ### Partial publish ```bash curl -sS -X POST "$PB_API_BASE/configuration-management/draft/partial-publish" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "entityIds": [ "0UG01PILOTXXXXXXXXXXXXXXXXXXX" ], "description": "CHG-00124: add pilot user group" }' ``` Response (`201`): ```json { "configurationVersion": { "id": "0CV01EXAMPLEXXXXXXXXXXXXXXXXX", "number": 74 }, "publishedEntityIds": [ "0UG01PILOTXXXXXXXXXXXXXXXXXXX" ] } ``` `entityIds` takes 1 to 1000 unique IDs, each a 29-character prefixed ID whose prefix determines the object type, so no type parameter is needed. A body that carries only `description` is rejected with `400`. The publish is atomic: if any named ID is rejected, nothing is published and the draft is left unchanged. **Note** Always include a `description` that references your internal ticket or change-request ID (for example `"CHG-00124: add pilot user group"`). This populates the version history with a searchable label and provides an audit trail for compliance reviews. Where no ticket reference exists, use a stable identifier for the automation and its run, for example `"Identity sync job 2026-08-06T02:00Z"`. ### Full publish responses | HTTP | Meaning | What to do | |---|---|---| | 201 | A new active version was created | Done. Your changes are live. | | 409 | The draft has no changes | Nothing to publish; usually safe to ignore | | 403 | Service account lacks write permission | Use a Super User account | | 501 | Draft mode is not supported for this tenant | Contact your account team | ### Partial publish responses A `201` returns the new version and the IDs that went live, as shown above. Errors share one envelope. Where present, `details[]` identifies the offending items, and `field` gives the position in the request, for example `entityIds[2]`: ```json { "error": { "code": "NOT_FOUND", "message": "No pending change found for one or more of the specified entities.", "timestamp": "2026-08-06T09:15:00Z", "details": [] } } ``` | HTTP | `code` | Condition | What to do | |---|---|---|---| | 400 | `VALIDATION_ERROR` | An ID is malformed or duplicated, the list is empty or exceeds 1000 items, or the body carries only `description` | Correct the request. This is a defect in the caller; do not retry unchanged | | 403 | `FORBIDDEN` | The service account cannot publish configuration changes | Use a Super User account | | 404 | `NOT_FOUND` | A valid, supported ID has no pending change in the draft. `details[]` lists every affected ID | Usually the write made no material change, or the object was already published. Do not retry unchanged | | 409 | `CONFLICT` | A named object depends on an unpublished change you did not include. `details[].ids` lists the blockers | Include the blocking IDs and retry, or escalate. Reports the first conflict found | | 500 | `INTERNAL_ERROR` | Unexpected server-side failure | Retry with bounded exponential backoff. Nothing was published | | 501 | `PARTIAL_PUBLISH_POLICY_UNSUPPORTED` | The request named a rule or section | Publish policy objects with a full publish | | 501 | `UNSUPPORTED_OPERATION` | The request named an unsupported object type, or partial publish is not enabled for the tenant | Use a full publish | Two of these deserve explicit handling in automation: - **`404` after an apparently successful write.** A `PUT` that changes nothing, such as adding a user who is already a member, stages no pending change for the publish to promote. Decide whether your automation treats this as success or as an exception. - **`409` from an unpublished dependency.** When an administrator has an unpublished rule that references the object you are publishing, both must be published together. This most often appears when deleting an object a rule still uses. Escalation is usually more appropriate than an automated retry. --- ## Check pending changes The draft is shared per tenant. Before publishing, confirm exactly what changes are staged so nothing unexpected goes live. ``` GET /seb-api/v1/configuration-management/draft/pending-changes ``` ```bash curl -sS "$PB_API_BASE/configuration-management/draft/pending-changes" \ -H "Authorization: Bearer $PB_TOKEN" ``` Response (`200`): ```json { "pageInfo": { "hasNextPage": false, "cursor": "", "totalCount": 1 }, "data": [ { "entityId": "0UG01PILOTXXXXXXXXXXXXXXXXXXX", "entityType": "userGroup", "name": "Pilot - DevTools block", "operation": "created", "lastUpdatedBy": "0UR01EXAMPLEADMINXXXXXXXXXXXX" } // ... more pending changes ] } ``` Each item in the `data` array describes one pending change: | Field | Type | Description | |---|---|---| | `entityId` | string | ID of the changed entity | | `entityType` | string | Type of the entity: `securityRule`, `userGroup`, `applicationGroup`, `customApplication`, and so on | | `name` | string | Name of the entity (for deleted entities, the name it had before deletion) | | `operation` | string | `created`, `updated`, or `deleted` | | `lastUpdatedBy` | string | Who last modified this entity in the draft: an administrator's email or a service-account identifier | An empty draft returns `200` with an empty `data` array, not `404`. Paginate with `limit` (1-1000, default 100) and `cursor`. ### Publish patterns **Pattern 1: You know what you changed.** Your script just modified a specific object and you have its ID. Partial publish it directly, naming only that object: ```bash curl -sS -X POST "$PB_API_BASE/configuration-management/draft/partial-publish" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{"entityIds": ["0UG01PILOTXXXXXXXXXXXXXXXXXXX"], "description": "CHG-00124: add pilot user group"}' ``` Where one run modifies several objects, collect the IDs and submit them in a single partial publish rather than one call per object. **Pattern 2: You are not sure what is in the draft.** List pending changes first, review them, then publish everything or a targeted subset: ```bash # Step 1: see what is staged curl -sS "$PB_API_BASE/configuration-management/draft/pending-changes" \ -H "Authorization: Bearer $PB_TOKEN" # Step 2a: publish the entire draft if all changes look correct curl -sS -X POST "$PB_API_BASE/configuration-management/draft/publish" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{"description": "CHG-00124: weekly policy sync"}' # Step 2b: or publish only the specific objects you want curl -sS -X POST "$PB_API_BASE/configuration-management/draft/partial-publish" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{"entityIds": ["0UG01PILOTXXXXXXXXXXXXXXXXXXX"], "description": "CHG-00124: add pilot user group"}' ``` --- ## Typical write flow 1. **Read** the current state (draft or active) to know what you are changing. 2. **Write** one or more edits (they accumulate in the draft). 3. **Check** pending changes with `GET /configuration-management/draft/pending-changes` to confirm what will go live. 4. **Publish** with a description referencing your change ticket. --- ## Example: create, verify, and publish A compact version of the full loop. [Getting started](https://pan.dev/prisma-browser/guide/getting-started) runs the same loop end to end with cleanup; this is the concept in three calls. Create a rule (lands on the draft): ```bash RULE_ID="$(curl -sS -X POST "$PB_API_BASE/policy/security/rules" \ -H "Authorization: Bearer $PB_TOKEN" -H "Content-Type: application/json" \ -d '{"name":"Block developer tools","mode":"active","controls":{"developerToolsForExtensions":{"action":"block"}}}' \ | python3 -c 'import sys,json; print(json.load(sys.stdin)["id"])')" ``` Verify it exists on the **draft** but not yet on **active** (the second read returns `404` until you publish): ```bash curl -sS "$PB_API_BASE/policy/security/rules/$RULE_ID" -H "Authorization: Bearer $PB_TOKEN" curl -sS "$PB_API_BASE/policy/security/rules/$RULE_ID?configurationVersion=active" -H "Authorization: Bearer $PB_TOKEN" ``` Publish to promote the whole draft to a new active version: ```bash curl -sS -X POST "$PB_API_BASE/configuration-management/draft/publish" \ -H "Authorization: Bearer $PB_TOKEN" -H "Content-Type: application/json" \ -d '{"description": "CHG-00123: block developer tools on extensions"}' ``` --- ## Verify changes before publishing Because the draft is shared per tenant, it pays to confirm exactly what is about to go live before promoting it. `GET /configuration-management/draft/pending-changes` gives you the complete list. For a more targeted check on a specific resource, compare the draft and active versions directly: 1. **Stage** your change on the draft (create, update, or delete). 2. **Compare** draft and active by reading the same resource twice: once with `?configurationVersion=draft` and once with `?configurationVersion=active`. This shows exactly what will change for that object, including anything else staged in the shared draft. 3. **Decide.** If the changes match your intent, publish. If the result contains unexpected edits, coordinate before publishing. 4. **Publish** with a description referencing your change ticket. ```python import os, requests base = os.environ["PB_API_BASE"] headers = {"Authorization": f"Bearer {os.environ['PB_TOKEN']}"} def get_rule(rule_id, version): return requests.get(f"{base}/policy/security/rules/{rule_id}", headers=headers, params={"configurationVersion": version}, timeout=30).json() draft = get_rule(os.environ["RULE_ID"], "draft") active = get_rule(os.environ["RULE_ID"], "active") print("changed:", draft != active) # inspect the difference before publishing ``` **Note** To stage a change without enforcing it immediately, create rules with `"mode": "disabled"`, then enable them in a later publish once you have validated them. --- ## Common pitfalls - **"I created a rule but it isn't enforced."** You created it on the draft. Publish to make it active. - **"My GET doesn't show my change."** You probably read `?configurationVersion=active`. Remove it (or use `draft`) to see pending edits. - **"Publish returned 409."** The draft is identical to active. There was nothing to publish. - **"I passed a version number and got a response, but it isn't that version's configuration."** Previous versions do not retain their configuration. Only `draft` and `active` return meaningful results. - **Concurrent editors.** The draft is shared per tenant. If multiple automations (or a human in Strata Cloud Manager) edit at once, a publish promotes everyone's staged changes together. Use `GET /configuration-management/draft/pending-changes` to check what is staged before publishing. --- ## Related - Concepts: [Delta patch](https://pan.dev/prisma-browser/guide/delta-patch) - Use cases: [Publish a single object (partial publish)](https://pan.dev/prisma-browser/guide/automate-with-partial-publish), [Gradual rollout with a user group](https://pan.dev/prisma-browser/guide/gradual-rollout-with-user-group) --- # Direct actions _Source: https://pan.dev/prisma-browser/guide/direct-actions | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ Most of this API follows the [draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish) model: you stage changes, then make them live. **Direct actions** are the exception. They act on live users and devices **immediately**, with no draft and no publish step. Suspending a user takes effect at once. **On this page:** draft model vs direct actions, direct actions vs policy controls, the shipped direct actions, related. Use this page to understand the distinction. For step-by-step examples, see the remediation use cases [Suspend a compromised user](https://pan.dev/prisma-browser/guide/suspend-a-compromised-user) and [Quarantine a lost or compromised device](https://pan.dev/prisma-browser/guide/quarantine-a-device). --- ## Draft model vs direct actions | | Draft model | Direct actions | |---|---|---| | What it changes | Configuration (rules, objects) | Live identity / device state | | When it takes effect | After you publish | Immediately | | `configurationVersion` | Applies | Does not apply | | Reversible by | Publishing a new version | A paired action (for example resume) | | Examples | Create a rule, edit a group | Suspend a user, force re-auth a device | **Note** **No draft, no undo via publish.** Because direct actions are immediate, there is no "draft" to review and no publish to gate them. Treat them like operational commands: validate inputs, and reverse with the paired action (resume, restore) rather than by reverting a configuration version. --- ## Direct actions vs policy controls This trips people up, so it is worth stating plainly. Some capabilities that sound like immediate "remediation" are actually **policy controls**, which are draft-gated: | You want to... | This is... | How it works | |---|---|---| | Suspend a user right now | Direct action | `POST /users/suspend`, immediate | | Force a device to re-authenticate | Direct action | `POST /devices/force-reauth`, immediate | | Clear browser data on matching devices | Policy control (`flushBrowserData`) | Set on a security rule, takes effect on publish | --- ## The shipped direct actions | Target | Actions | |---|---| | Users | `suspend`, `resume`, `force-reauth` | | Devices | `archive`, `restore`, `suspend`, `resume`, `delete`, `force-reauth` | All are `POST` calls under `/users` and `/devices` that return `200` on success. For request bodies and ordering rules, see [Suspend a compromised user](https://pan.dev/prisma-browser/guide/suspend-a-compromised-user) and [Quarantine a lost or compromised device](https://pan.dev/prisma-browser/guide/quarantine-a-device). --- ## Related - Use cases: [Suspend a compromised user](https://pan.dev/prisma-browser/guide/suspend-a-compromised-user), [Quarantine a lost or compromised device](https://pan.dev/prisma-browser/guide/quarantine-a-device) - Concepts: [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish), [Errors](https://pan.dev/prisma-browser/guide/errors) --- # Delta patch _Source: https://pan.dev/prisma-browser/guide/delta-patch | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ When you update a rule that contains **lists** (application IDs, URLs, web classifications, scope members), you have two ways to change them: replace the whole list, or send only the items to add or remove. The second option, **delta patch**, is what makes large-scale automation safe and simple. This page explains when to use each. Delta patch is a Prisma Browser convenience: you do not have to read the current list, compute the new one, and write it back. You say "add these, remove those", and the server applies the delta. **On this page:** three ways to change a field, where delta applies, full replacement vs delta, worked example, when to use which, rules and edge cases, per-intent examples. --- ## Three ways to change a field in a PATCH `PATCH` is partial: send only the fields you want to change, and omit the rest. For the fields you do send, the semantics depend on the field type: | Field kind | How a PATCH changes it | Example | |---|---|---| | **Scalar** (string, boolean, enum) | Set directly to the new value | `accessMode: "specific"` | | **List, full replacement** | Replace the entire list with the value you send | `applicationIds: [a, b, c]` | | **List, delta** | Add or remove specific items, leaving the rest intact | `addApplicationIds: [d]`, `removeApplicationIds: [a]` | For every delta-capable list, there are three sibling fields: - `` : full replacement (the new complete list) - `add` : items to add to the current list - `remove` : items to remove from the current list You pick **one strategy per field**: send the full-replacement field, or the add/remove pair, not both. --- ## Where delta applies Delta patch is available on the **access-and-data rule** `PATCH` for its list fields, including: | List | Full replace | Add | Remove | |---|---|---|---| | SaaS application IDs | `applicationIds` | `addApplicationIds` | `removeApplicationIds` | | URLs | `urls` | `addUrls` | `removeUrls` | | Web classifications | `webClassifications` | `addWebClassifications` | `removeWebClassifications` | | Private application IDs | `applicationIds` | `addApplicationIds` | `removeApplicationIds` | These live under the application scope of the rule (for example `applications.saas.specific.*`). --- ## Full replacement vs delta: why it matters Consider an access-and-data rule whose SaaS scope currently allows applications `[A, B, C]`, and you want to add `D` and remove `A`. ### Full replacement (read-modify-write) You must first **read** the current list, compute `[B, C, D]` yourself, then **write** it back: ```json { "applications": { "saas": { "accessMode": "specific", "specific": { "applicationIds": ["B", "C", "D"] } } } } ``` Problem: between your read and your write, anyone else (another script, an administrator in Strata Cloud Manager) might have added `E`. Your write silently erases `E`. This is a classic lost-update race. ### Delta (no read needed) State your intent directly. No read, no race over the items you did not touch: ```json { "applications": { "saas": { "accessMode": "specific", "specific": { "addApplicationIds": ["D"], "removeApplicationIds": ["A"] } } } } ``` If someone else added `E` in the meantime, it stays. You only changed what you asked to change. --- ## Worked example Add application `0AP01ADD...` and remove application `0AP01OLD...` from a rule's SaaS scope. `RULE_ID` is an access-and-data rule. ```bash export RULE_ID='0RLEXAMPLERULEXXXXXXXXXXXXXX' ``` ```bash curl -sS -X PATCH "$PB_API_BASE/policy/access-and-data/rules/$RULE_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "applications": { "saas": { "accessMode": "specific", "specific": { "addApplicationIds": ["0AP01ADDXXXXXXXXXXXXXXXXXXXXX"], "removeApplicationIds": ["0AP01OLDXXXXXXXXXXXXXXXXXXXXX"] } } } }' ``` A successful `PATCH` returns `200` with the rule ID: ```json { "id": "0RLEXAMPLERULEXXXXXXXXXXXXXX" } ``` Remember: this changed the **draft**. Publish to make it live (see [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish)). --- ## When to use which | Use full replacement when... | Use delta when... | |---|---| | You are the sole owner of the list and want it to be exactly this set | Multiple sources edit the same rule | | You are recreating a rule from a known desired state (IaC apply) | You are reacting to events ("add this newly risky application") | | The list is short and you have just read it | You want to avoid read-modify-write entirely | --- ## Rules and edge cases - **Pick one strategy per field.** Sending both `applicationIds` and `addApplicationIds` for the same list is contradictory; send one. - **Set the mode too if you are turning the scope on.** Adding specific applications usually means `accessMode: "specific"`. Send it in the same PATCH. - **Idempotency.** Adding an item already present, or removing one already absent, is a safe no-op rather than an error. This makes delta patches retry-safe. - **Minimum items.** Add/remove arrays expect at least one item; omit the field entirely if you have nothing to add or remove. ## Per-intent examples Each PATCH states one intent directly, with no read-modify-write. All target an access-and-data rule at `$RULE_ID`. Expand the intent you need.
Add one SaaS application to the rule's scope ```bash curl -sS -X PATCH "$PB_API_BASE/policy/access-and-data/rules/$RULE_ID" \ -H "Authorization: Bearer $PB_TOKEN" -H "Content-Type: application/json" \ -d '{ "applications": { "saas": { "accessMode": "specific", "specific": { "addApplicationIds": ["0AP01ADDXXXXXXXXXXXXXXXXXXXXX"] } } } }' ``` Response (`200`): ```json { "id": "0RLEXAMPLERULEXXXXXXXXXXXXXX" } ```
Remove one SaaS application ```bash curl -sS -X PATCH "$PB_API_BASE/policy/access-and-data/rules/$RULE_ID" \ -H "Authorization: Bearer $PB_TOKEN" -H "Content-Type: application/json" \ -d '{ "applications": { "saas": { "accessMode": "specific", "specific": { "removeApplicationIds": ["0AP01OLDXXXXXXXXXXXXXXXXXXXXX"] } } } }' ``` Response (`200`): ```json { "id": "0RLEXAMPLERULEXXXXXXXXXXXXXX" } ```
Add and remove a user group on the rule's scope `scope.users` supports `addUsers`/`removeUsers` and `addUserGroups`/`removeUserGroups`. ```bash curl -sS -X PATCH "$PB_API_BASE/policy/access-and-data/rules/$RULE_ID" \ -H "Authorization: Bearer $PB_TOKEN" -H "Content-Type: application/json" \ -d '{ "scope": { "users": { "addUserGroups": ["0UGEXAMPLECONTRACTORSXXXXXX"], "removeUserGroups": ["0UGEXAMPLEINTERNSXXXXXXXXXX"] } } }' ``` Response (`200`): ```json { "id": "0RLEXAMPLERULEXXXXXXXXXXXXXX" } ```
Add and remove a URL URL items are objects; `addUrls`/`removeUrls` live under the same `specific` block. ```bash curl -sS -X PATCH "$PB_API_BASE/policy/access-and-data/rules/$RULE_ID" \ -H "Authorization: Bearer $PB_TOKEN" -H "Content-Type: application/json" \ -d '{ "applications": { "saas": { "accessMode": "specific", "specific": { "addUrls": [ { "url": "newsite.example.com" } ], "removeUrls": [ { "url": "oldsite.example.com" } ] } } } }' ``` Response (`200`): ```json { "id": "0RLEXAMPLERULEXXXXXXXXXXXXXX" } ```
**Note** Adding an item already present, or removing one already absent, is a safe no-op rather than an error, so these PATCHes are retry-safe. Send `accessMode: "specific"` in the same PATCH when you are turning the scope on. --- Next: see this in a full workflow in [Bulk-update rule applications with delta patch](https://pan.dev/prisma-browser/guide/bulk-update-rule-applications-with-delta). --- # Pagination _Source: https://pan.dev/prisma-browser/guide/pagination | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ List endpoints return a **cursor-paginated envelope**: a consistent wrapper with a `pageInfo` block that tells you whether more results exist and what cursor to pass for the next page. Use this page to understand the envelope shape and loop through large collections safely. **On this page:** the envelope, query parameters, loop through every page. ## The envelope Every list response has the same top-level shape: ```json { "pageInfo": { "hasNextPage": true, "cursor": "opaque-string", "totalCount": 137 }, "data": [ ], "metadata": { } } ``` - `pageInfo.hasNextPage` (boolean): whether another page exists. - `pageInfo.cursor` (string, nullable): pass it back as `?cursor=` to fetch the next page. - `pageInfo.totalCount` (integer): total number of items matching the query across all pages. Populated on the first page (no cursor) by default; see `includeTotalCount` below. ## Query parameters - `limit`: integer, 1 to 1000, default 100. - `cursor`: opaque string from the previous `pageInfo.cursor`. Do not construct or parse it. - `includeTotalCount`: optional boolean controlling whether `pageInfo.totalCount` is populated. Default: present on the first page (no cursor) and absent on subsequent pages. Set `=true` to force it on every page, or `=false` to skip it entirely. The count reflects the same filters as the list. Because the total is a property of the whole result set, the default gives it to a paginating client once up front (on the first page): read it there rather than expecting it on every page. `includeTotalCount` is supported on the object and policy list endpoints: users, devices, user groups, device groups, applications, applications-by-type, application groups, and the four policy GETs (security, customization, sign-in, access and data). **Note** `/user-requests` is cursor-paginated but its `pageInfo` carries only `hasNextPage` and `cursor`: it has no `totalCount` and does not accept `includeTotalCount`. ## Loop through every page ```bash cursor="" while : ; do resp=$(curl -sS "$PB_API_BASE/applications?limit=100&cursor=$cursor" -H "Authorization: Bearer $PB_TOKEN") echo "$resp" | jq '.data[]' cursor=$(echo "$resp" | jq -r '.pageInfo.cursor // empty') [ -z "$cursor" ] && break done ``` **Note** Policy list items are summaries. Fetch a rule's full body with `GET /policy/{type}/rules/{id}`. --- # Errors _Source: https://pan.dev/prisma-browser/guide/errors | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ Every endpoint returns a structured **error envelope** so your automation can branch on typed codes rather than parsing prose. This page covers the envelope shape, common HTTP statuses, typed error codes, and how to handle them well. **On this page:** the envelope, common statuses, error codes, handling errors well. ## The envelope ```json { "error": { "code": "FIELD_VALIDATION_ERROR", "message": "Human-readable description of what went wrong", "details": { "errors": [ { "field": "name", "error": "must be 1 to 300 characters", "value": "" } ] }, "timestamp": "2026-05-31T10:00:00Z" } } ``` `code` and `message` are always present; `timestamp` records when the error occurred. Some codes add a structured `details` object (see below). Branch on `error.code`, not on the human-readable `message`. ## Common statuses | HTTP | Meaning | What to do | |---|---|---| | 400 | Request failed validation | Inspect `error.code` and `details`, fix the offending field, retry | | 401 | Missing or expired token | Fetch a new token (see [Authentication](https://pan.dev/prisma-browser/guide/authentication)) | | 403 | Token valid but lacks permission | Use a Super User service account for writes | | 404 | Resource or configuration version not found | Verify the ID and `configurationVersion` | | 409 | Conflict (for example, publishing an empty draft, or a concurrent rule edit) | Inspect the message; often safe to ignore | | 422 | Well-formed request the server cannot act on | Inspect the message and adjust the request | | 500 | Server error | Retry with backoff; if persistent, contact support | `401` is enforced at the SASE platform (OAuth) layer, ahead of the policy handlers, so it carries the platform's own response rather than the envelope below. ## Error codes The `error.code` is a typed discriminator. Some codes add a structured `details` object you can act on programmatically: | `error.code` | HTTP | `details` | |---|---|---| | `VALIDATION_ERROR` | 400 | none (generic validation failure) | | `FIELD_VALIDATION_ERROR` | 400 | `details.errors[]`, each with `field`, `error`, optional `index` and `value` | | `CONTIGUITY_VIOLATION` | 400 | `details.sectionId`, `details.ruleIds` (rules of a section not contiguous) | | `INCOMPLETE_ARRAY` | 400 | `details.missingRules`, `details.missingSections` (positions array omits existing items) | | `NOT_FOUND` | 404 | none | | `FIELD_REFERENCE_NOT_FOUND` | 404 | `details.errors[]` (a referenced rule, section, or anchor ID does not exist) | | `FORBIDDEN` | 403 | none | | `UNSUPPORTED_OPERATION` | 400 | none (for example, draft mode not enabled for the tenant) | | `CONFLICT` | 409 | none (the request conflicts with the current state of the resource) | | `UNPROCESSABLE_ENTITY` | 422 | none (well-formed request the server cannot act on) | | `NOT_IMPLEMENTED` | 501 | none | | `PROVIDER_IN_USE` | 409 | `details` naming what still references the cloud storage provider | The bulk device and user actions add their own codes: `DEVICES_NOT_FOUND`, `USERS_NOT_FOUND`, `INVALID_DEVICE_ID`, `INVALID_USER_ID`, `INVALID_STATUS_TRANSITION`, `UNEXPECTED_DEVICE_STATUS`, `CANNOT_DELETE_SUSPENDED_DEVICE` and `MAX_DEVICES_EXCEEDED`. The code vocabulary is open: handle unknown codes by falling back to the HTTP status. ## Handling errors well - **Treat `4xx` as actionable, `5xx` as retryable.** For `400`, read `error.code` and, for field errors, `details.errors[].field`; for `401`, refresh the token; for `403`, check the service account's role. For `5xx`, retry with exponential backoff. - **Branch on `error.code` and HTTP status, not on `message`.** The human-readable text can change. **Note** Every endpoint returns this envelope, including devices, users, user groups, device groups, cloud storage and [User requests](https://pan.dev/prisma-browser/guide/user-requests). The `details` object is populated on the policy and object endpoints; elsewhere the response carries `error.code` and `error.message` only. --- # Users _Source: https://pan.dev/prisma-browser/guide/users | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ A **user** is an identity observed in your tenant: someone who has signed in to (or been synced into) the Prisma Browser. Unlike most building blocks, **you do not create or delete users through this API.** Users arrive from your identity provider (`saml`, `oidc`) or as `local` accounts, and the API lets you **list them, read one, filter them, and act on them** (suspend, resume, force re-authentication). Rules scope to users through [user groups](https://pan.dev/prisma-browser/guide/user-groups), not by referencing a user directly. **On this page:** what you can and cannot do, list and filter, get one, key fields, lifecycle actions, endpoint reference, tips and gotchas. --- ## What you can and cannot do | Operation | Supported? | How | |---|---|---| | List / get | Yes | `GET /users`, `GET /users/{id}` | | Create / update / delete | No | Users are observed, not authored here | | Suspend / resume / force re-auth | Yes, immediately | [Direct actions](https://pan.dev/prisma-browser/guide/direct-actions) (no draft, no publish) | **Note** **Users are not part of the draft.** Listing and reading users returns live state, and the lifecycle actions below take effect at once. The [draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish) model does not apply to users. --- ## List and filter ``` GET /seb-api/v1/users ``` ```bash # Active users seen in the last day, newest activity first curl -sS -G "$PB_API_BASE/users" \ -H "Authorization: Bearer $PB_TOKEN" \ --data-urlencode "user.status=active" \ --data-urlencode "sort=user.last_seen" \ --data-urlencode "order=desc" \ --data-urlencode "limit=50" ``` Response (`200`): ```json { "pageInfo": { "hasNextPage": false, "cursor": "", "totalCount": 1 }, "data": [ { "id": "0UREXAMPLEUSERXXXXXXXXXXXXX", "externalId": "3af33864-91db-bbb8-194f-b9aa5eb921c4", "email": "alice@example.com", "lastSeen": "2026-01-15T10:00:00Z", "firstSeen": "2025-11-01T09:00:00Z", "name": "Alice Example", "profilePictureURL": "", "deletedTime": "0001-01-01T00:00:00Z", "status": "active", "provider": "saml", "deviceIds": [], "userGroups": [] } // ... more users ] } ``` You get a paginated envelope: `pageInfo` plus a `data` array of `User` objects. Page through large tenants with the cursor (see [Pagination](https://pan.dev/prisma-browser/guide/pagination)). ### Query parameters | Parameter | Type | Notes | |---|---|---| | `user.name` | string | Filter by name. | | `user.email` | string | Filter by email. | | `user.status` | enum | `active` or `suspended`. | | `groupId` | string | Filter by user group. Comma-separated to match multiple groups (`groupId=id1,id2`). | | `user.first_seen_gte` | date-time | First seen at or after. | | `user.last_seen_lte` | date-time | Last seen at or before. | | `sort` | enum | `user.name`, `user.email`, `user.first_seen`, `user.last_seen`, `user.status`. | | `order` | enum | `asc` or `desc`. | | `cursor` | string | Opaque pagination cursor. | | `limit` | integer | Page size. | | `includeDeleted` | boolean | Include deleted users in the result. | --- ## Get one ``` GET /seb-api/v1/users/{id} ``` ```bash curl -sS "$PB_API_BASE/users/0UREXAMPLEUSERXXXXXXXXXXXXX" \ -H "Authorization: Bearer $PB_TOKEN" ``` Response (`200`): ```json { "id": "0UREXAMPLEUSERXXXXXXXXXXXXX", "externalId": "3af33864-91db-bbb8-194f-b9aa5eb921c4", "email": "alice@example.com", "lastSeen": "2026-01-15T10:00:00Z", "firstSeen": "2025-11-01T09:00:00Z", "name": "Alice Example", "profilePictureURL": "", "deletedTime": "0001-01-01T00:00:00Z", "status": "active", "provider": "saml", "deviceIds": [ "0DE01EXAMPLEDEVICEXXXXXXXXXXX" ], "userGroups": [ { "id": "0UG01PILOTXXXXXXXXXXXXXXXXXXX", "name": "Pilot - DevTools block" } ] } ``` A missing ID returns `404`. --- ## Key fields | Field | Type | Notes | |---|---|---| | `id` | string | Unique identifier (`0UR...`). | | `externalId` | string | Identifier from the source system. | | `email` | string | User email. | | `name` | string | Display name. | | `status` | enum | `active` or `suspended`. | | `provider` | enum | `saml`, `oidc`, or `local` (where the identity comes from). | | `deviceIds` | array | IDs of the user's known [devices](https://pan.dev/prisma-browser/guide/devices). | | `userGroups` | array | Groups the user belongs to. | | `firstSeen` | date-time | First observed (read-only). | | `lastSeen` | date-time | Last observed (read-only). | | `profilePictureURL` | string | Avatar URL. | | `deletedTime` | date-time | Set when the user is deleted (read-only). | --- ## Lifecycle actions These are [direct actions](https://pan.dev/prisma-browser/guide/direct-actions): they act on the live user immediately, with no draft or publish, and return `200`. | Goal | Endpoint | Effect | |---|---|---| | Suspend the user | `POST /seb-api/v1/users/suspend` | Revokes browser access across the user's known and future devices. | | Restore access | `POST /seb-api/v1/users/resume` | Reactivates a suspended user. | | Invalidate sessions | `POST /seb-api/v1/users/force-reauth` | Forces re-authentication on all active devices. | ```bash curl -sS -X POST "$PB_API_BASE/users/suspend" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "userIds": ["0UREXAMPLEUSERXXXXXXXXXXXXX"] }' ``` Response (`200`): ```json { "message": "Users suspended successfully" } ``` For the full incident-response and offboarding walkthrough, see [Suspend a compromised user](https://pan.dev/prisma-browser/guide/suspend-a-compromised-user). --- ## Endpoint reference | Method | Path | Purpose | |---|---|---| | `GET` | `/users` | List users (filter, paginate) | | `GET` | `/users/{id}` | Read one user | | `POST` | `/users/suspend` | Suspend users (direct action) | | `POST` | `/users/resume` | Resume users (direct action) | | `POST` | `/users/force-reauth` | Force re-authentication (direct action) | All paths are under the `/seb-api/v1` base. --- ## Tips and gotchas **Note** **To change who a rule covers, edit a group, not a user.** Rules do not reference users directly; they reference [user groups](https://pan.dev/prisma-browser/guide/user-groups). Add a user to a `local` group and publish. See [Add or remove users on a rule](https://pan.dev/prisma-browser/guide/manage-users-on-a-rule). **Note** **Reverse with the pair, not a rollback.** Undo `suspend` with `resume`; there is no configuration version to revert because these actions bypass the draft. --- ## Related - Building blocks: [User groups](https://pan.dev/prisma-browser/guide/user-groups), [Devices](https://pan.dev/prisma-browser/guide/devices) - Concepts: [Direct actions](https://pan.dev/prisma-browser/guide/direct-actions), [Pagination](https://pan.dev/prisma-browser/guide/pagination) - Use cases: [Suspend a compromised user](https://pan.dev/prisma-browser/guide/suspend-a-compromised-user), [Add or remove users on a rule](https://pan.dev/prisma-browser/guide/manage-users-on-a-rule) --- # Devices _Source: https://pan.dev/prisma-browser/guide/devices | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ A **device** is an endpoint observed in your tenant: a machine or mobile device that has connected through the Prisma Browser. Like [users](https://pan.dev/prisma-browser/guide/users), **you do not create devices through this API.** Devices are observed as they enroll and report posture, and the API lets you **list them, read one, filter by posture, and act on them** (archive, suspend, delete, force re-authentication). Each device carries a rich posture snapshot (OS, disk encryption, firewall, screen lock, endpoint protection) and an embedded `user`. **On this page:** what you can and cannot do, list and filter by posture, get one, key fields, lifecycle actions, endpoint reference, tips and gotchas. --- ## What you can and cannot do | Operation | Supported? | How | |---|---|---| | List / get | Yes | `GET /devices`, `GET /devices/{id}` | | Create / update | No | Devices are observed, not authored here | | Archive / restore / suspend / resume / delete / force re-auth | Yes, immediately | [Direct actions](https://pan.dev/prisma-browser/guide/direct-actions) (no draft, no publish) | **Note** **Devices are not part of the draft.** Reads return live state and the lifecycle actions below take effect at once. The [draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish) model does not apply to devices. To govern devices with policy, use [device groups](https://pan.dev/prisma-browser/guide/device-groups), which are draft-gated. --- ## List and filter by posture ``` GET /seb-api/v1/devices ``` ```bash # macOS devices with disk encryption disabled, most recently seen first curl -sS -G "$PB_API_BASE/devices" \ -H "Authorization: Bearer $PB_TOKEN" \ --data-urlencode "device.os_type=macOS" \ --data-urlencode "device.disk_encryption_status=DiskEncryptionStatusDisabled" \ --data-urlencode "sort=device.last_seen" \ --data-urlencode "order=desc" \ --data-urlencode "limit=50" ``` Response (`200`): ```json { "pageInfo": { "hasNextPage": false, "cursor": "", "totalCount": 1 }, "data": [ { "id": "0DEEXAMPLEDEVICEXXXXXXXXXXX", "externalId": "e3ad0cea-9dba-4d59-afd9-ae4e65b026ba", "status": "active", "hostname": "M-EXAMPLE01", "serialNumber": "C02EXAMPLE01", "osType": "macOS", "osVersion": "26.4.0", "osDisplayName": "macOS 26.4", "deviceType": "laptop", "platform": "Desktop Browser", "screenLockStatus": "ScreenLockStatusEnabled", "diskEncryptionStatus": "DiskEncryptionStatusEnabled", "firewallStatus": "FireWallStatusEnabled", "browserVersion": "150.41.0.46", "ip": "203.0.113.10", "macAddresses": [ "22:fe:8a:2b:33:b1" ], "firstSeen": "2025-11-01T09:00:00Z", "lastSeen": "2026-01-15T10:00:00Z" // ... posture details: diskEncryptionDetails, firewallDetails, deviceEPP, embedded user } // ... more devices ] } ``` You get a paginated envelope: `pageInfo` plus a `data` array of `Device` objects. Page through large tenants with the cursor (see [Pagination](https://pan.dev/prisma-browser/guide/pagination)). ### Query parameters | Parameter | Type | Notes | |---|---|---| | `device.hostname` | string | Filter by hostname. | | `device.os_type` | string | `windows`, `macOS`, `linux`, `ios`, `android`, `unknown`. | | `device.type` | string | `desktop`, `laptop`, `vm`, `smartphone`, `tablet`, `chromebook`, `unknown`. | | `device.firewall_status` | string | Firewall posture. | | `device.screen_lock_status` | string | Screen lock posture. | | `device.disk_encryption_status` | string | Disk encryption posture. | | `user.name` | string | Filter by the device owner's name. | | `device.first_seen_gte` | date-time | First seen at or after. | | `device.last_seen_gte` | date-time | Last seen at or after. | | `device.last_seen_lte` | date-time | Last seen at or before. | | `sort` | enum | `device.hostname`, `device.os_type`, `device.browser_version`, `device.first_seen`, `device.last_seen`, `user.name`. | | `order` | enum | `asc` or `desc`. | | `cursor` | string | Opaque pagination cursor. | | `limit` | integer | Page size. | --- ## Get one ``` GET /seb-api/v1/devices/{id} ``` ```bash curl -sS "$PB_API_BASE/devices/0DEEXAMPLEDEVICEXXXXXXXXXXX" \ -H "Authorization: Bearer $PB_TOKEN" ``` Response (`200`): ```json { "id": "0DEEXAMPLEDEVICEXXXXXXXXXXX", "externalId": "e3ad0cea-9dba-4d59-afd9-ae4e65b026ba", "status": "active", "hostname": "M-EXAMPLE01", "serialNumber": "C02EXAMPLE01", "osType": "macOS", "osVersion": "26.4.0", "deviceType": "laptop", "platform": "Desktop Browser", "screenLockStatus": "ScreenLockStatusEnabled", "diskEncryptionStatus": "DiskEncryptionStatusEnabled", "firewallStatus": "FireWallStatusEnabled", "diskEncryptionDetails": { "products": [ { "vendorName": "Apple Inc.", "productName": "FileVault" } ] }, "browserVersion": "150.41.0.46", "ip": "203.0.113.10", "macAddresses": [ "22:fe:8a:2b:33:b1" ], "firstSeen": "2025-11-01T09:00:00Z", "lastSeen": "2026-01-15T10:00:00Z", "user": { "id": "0UREXAMPLEUSERXXXXXXXXXXXXX", "email": "alice@example.com", "name": "Alice Example" // ... } // ... firewallDetails, deviceEPP, and other posture fields } ``` A missing ID returns `404`. --- ## Key fields | Field | Type | Notes | |---|---|---| | `id` | string | Unique identifier (`0DE...`). | | `status` | enum | `active`, `archived`, or `suspended`. | | `hostname` | string | Device hostname. | | `serialNumber` | string | Serial number. This is what [device groups](https://pan.dev/prisma-browser/guide/device-groups) pin. | | `osType` | enum | `windows`, `macOS`, `linux`, `ios`, `android`, `unknown`. | | `osVersion` | string | OS version. | | `deviceType` | enum | `desktop`, `laptop`, `vm`, `smartphone`, `tablet`, `chromebook`, `unknown`. | | `platform` | object | Device platform. | | `screenLockStatus` | enum | `ScreenLockStatusUnknown` / `Disabled` / `Enabled`. | | `diskEncryptionStatus` | enum | `DiskEncryptionStatusUnknown` / `Disabled` / `Enabled`. | | `firewallStatus` | enum | `FireWallStatusUnknown` / `Disabled` / `Enabled`. | | `deviceEPP` | object | Endpoint protection posture. | | `crowdstrikeZTA` | object | CrowdStrike Zero Trust Assessment posture. | | `browserVersion` | string | The Prisma Browser version. | | `ip` | string | Last known IP (nullable). | | `macAddresses` | array | MAC addresses. | | `user` | object | The embedded [user](https://pan.dev/prisma-browser/guide/users) associated with the device. | | `firstSeen` / `lastSeen` | date-time | Observation timestamps (read-only). | Mobile devices also report `mobileVendor`, `mobileHardware`, and `mobileIsRooted`. --- ## Lifecycle actions These are [direct actions](https://pan.dev/prisma-browser/guide/direct-actions): they act on the live device immediately, with no draft or publish, and return `200`. | Goal | Endpoint | Effect | |---|---|---| | Suspend the device | `POST /seb-api/v1/devices/suspend` | Suspends the device. | | Resume the device | `POST /seb-api/v1/devices/resume` | Resumes a suspended device. | | Archive the device | `POST /seb-api/v1/devices/archive` | Moves the device to archived state. | | Restore the device | `POST /seb-api/v1/devices/restore` | Restores an archived device. | | Remove permanently | `POST /seb-api/v1/devices/delete` | Permanently deletes the device. | | Invalidate sessions | `POST /seb-api/v1/devices/force-reauth` | Forces re-authentication on the device. | ```bash curl -sS -X POST "$PB_API_BASE/devices/suspend" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "deviceIds": ["0DEEXAMPLEDEVICEXXXXXXXXXXX"] }' ``` Response (`200`): ```json { "suspendedDeviceIds": [ "0DEEXAMPLEDEVICEXXXXXXXXXXX" ], "message": "1 devices suspended successfully" } ``` For the full lost/compromised-device walkthrough, see [Quarantine a lost or compromised device](https://pan.dev/prisma-browser/guide/quarantine-a-device). **Caution** **A suspended device must be resumed before it can be deleted, and delete is permanent.** --- ## Endpoint reference | Method | Path | Purpose | |---|---|---| | `GET` | `/devices` | List devices (filter, paginate) | | `GET` | `/devices/{id}` | Read one device | | `POST` | `/devices/suspend` | Suspend devices (direct action) | | `POST` | `/devices/resume` | Resume devices (direct action) | | `POST` | `/devices/archive` | Archive devices (direct action) | | `POST` | `/devices/restore` | Restore archived devices (direct action) | | `POST` | `/devices/delete` | Permanently delete devices (direct action) | | `POST` | `/devices/force-reauth` | Force re-authentication (direct action) | All paths are under the `/seb-api/v1` base. --- ## Tips and gotchas **Note** **Filter by posture, then group by posture.** Use the list filters above to find devices in a given posture (for example disk encryption disabled), then govern them with a posture-based [device group](https://pan.dev/prisma-browser/guide/device-groups). List filters are read-only diagnostics; device groups are what a rule references. **Note** **Reverse with the pair, not a rollback.** Undo `suspend` with `resume` and `archive` with `restore`; there is no configuration version to revert because these actions bypass the draft. --- ## Related - Building blocks: [Device groups](https://pan.dev/prisma-browser/guide/device-groups), [Users](https://pan.dev/prisma-browser/guide/users) - Concepts: [Direct actions](https://pan.dev/prisma-browser/guide/direct-actions), [Pagination](https://pan.dev/prisma-browser/guide/pagination) - Use cases: [Quarantine a lost or compromised device](https://pan.dev/prisma-browser/guide/quarantine-a-device) --- # Applications _Source: https://pan.dev/prisma-browser/guide/applications | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ An **application** is a named object that represents something you want policy to recognize and govern: a SaaS site, a private (internal) application, a non-web remote connection, or a desktop application. You create applications once, then reference them, directly or via an [application group](https://pan.dev/prisma-browser/guide/application-groups), from an [access and data rule](https://pan.dev/prisma-browser/guide/access-and-data-rules). This page covers what is common to every application: the types, tags and classification, how to list and read applications, bulk operations, and shared limits. Each type has its own page for fields, the URL object, create, update, and worked examples. **On this page:** types, tags and classification, retrieve, bulk operations, limits, endpoint reference, tips. --- ## Types The `type` determines the shape of the object and which page documents it: | Type | What it is | Details | |---|---|---| | `custom` | A SaaS or web application you define by its URLs | [Custom applications](https://pan.dev/prisma-browser/guide/applications-custom) | | `private` | A private (internal) application routed through Prisma Access | [Private applications](https://pan.dev/prisma-browser/guide/applications-private) | | `non-web` | A remote connection over RDP or SSH | [Non-web applications](https://pan.dev/prisma-browser/guide/applications-non-web) | | `localdesktopcustom` | A local desktop application you define by its executables | [Desktop applications](https://pan.dev/prisma-browser/guide/applications-desktop) | | `localdesktopcatalog` | A read-only local desktop application curated by Palo Alto Networks | [Desktop applications](https://pan.dev/prisma-browser/guide/applications-desktop) | | `catalog` | A read-only SaaS application from the Universal Application Directory (UAD) | [Application catalog](https://pan.dev/prisma-browser/guide/applications-catalog) | Four types are yours to create and edit. Two, `catalog` and `localdesktopcatalog`, are curated by Palo Alto Networks and read-only: you list them, read them, and reference them from a rule, but you cannot create or edit them. Create a dedicated application (or a focused URL list) when it is important to **identify** that traffic in events with a recognizable name, or to **assign policy** to it specifically. If you only need broad coverage, a category or an "any" application segment is simpler. --- ## Tags and classification Every application, including the read-only catalog types, carries a **classification** and a list of **tags**. They are separate fields that serve different purposes: classification records how your organization treats the application, tags are free-form labels you define for your own grouping and reporting. Both are returned on every read, for every type. You set them through this API on the four types you create: `custom`, `private`, `non-web`, and `localdesktopcustom`. The `catalog` and `localdesktopcatalog` types have no create or update endpoint, so setting them through the API is planned for a future release. Until then, set them on the Applications page in the Prisma Browser admin console in Strata Cloud Manager. ### Classification `classification` is a single value per application: | Value | Meaning | |---|---| | `Sanctioned` | Approved for use in your organization | | `Tolerated` | Allowed, but not formally approved | | `Unsanctioned` | Not approved for use | | `Unclassified` | No classification recorded (the default) | Send `classification` on create or on `PATCH`. `Unclassified` means no classification is recorded rather than a value stored against the application, so setting an application back to `Unclassified` clears it. ### Tags Tags are returned on read as objects and written by ID: ```json "tags": [ { "id": "0TG01FINANCEAPPROVEDXXXXXXXXX", "name": "Finance approved" }, { "id": "0TG01PCIINSCOPEXXXXXXXXXXXXXX", "name": "PCI in scope" } ] ``` On create, send `tagIDs` as a plain array of tag IDs. A tag ID matches the pattern `0TG` followed by 26 characters, and an application holds up to 63 tags: ```bash curl -sS -X POST "$PB_API_BASE/applications/type/custom" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Acme Wiki", "type": "custom", "urls": [ { "url": "wiki.acme.example.com" } ], "classification": "Sanctioned", "tagIDs": [ "0TG01FINANCEAPPROVEDXXXXXXXXX" ] }' ``` On `PATCH`, `tagIDs` accepts two forms, the same replace-or-delta pattern the `urls` field uses (see [Delta patch](https://pan.dev/prisma-browser/guide/delta-patch)): - **Replace** the whole set: send a plain array. The new list fully replaces the old one. - **Delta** add and remove: send `tagIDs: { "add": [...], "remove": [...] }` to adjust the set without re-sending it, which avoids overwriting concurrent edits. ```bash curl -sS -X PATCH "$PB_API_BASE/applications/type/custom/$APP_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "type": "custom", "classification": "Tolerated", "tagIDs": { "add": [ "0TG01PCIINSCOPEXXXXXXXXXXXXXX" ], "remove": [ "0TG01FINANCEAPPROVEDXXXXXXXXX" ] } }' ``` **Note** **Finding a tag ID.** This release does not expose endpoints for managing tags. Create and name tags on the Applications page in the Prisma Browser admin console in Strata Cloud Manager, then read an application that already carries the tag to get its ID from the `tags` array. Tag management endpoints (list, create, rename, delete) are coming soon, so treat this lookup as temporary. Classification and tags never overlap. Classification is stored separately and is never returned in the `tags` array, so you change it through `classification` and never by adding a tag named `Sanctioned`. --- ## Retrieve There are two ways to list applications and two ways to read a single one. The response shape depends on the application's `type`: read the `type` field first, then the type-specific fields. **List everything** returns applications of all types, so it is the right call to export or reconcile your whole inventory. It supports filtering by `type`, `name`, and `url`, pagination with `limit` and `cursor`, and `sort` / `order` (see [Pagination](https://pan.dev/prisma-browser/guide/pagination)): ```bash curl -sS -G "$PB_API_BASE/applications" \ -H "Authorization: Bearer $PB_TOKEN" \ --data-urlencode "limit=50" ``` Response (`200`). The `data` array mixes types, so read each item's `type` field first: ```json { "data": [ { "type": "custom", "id": "0AP01ACMEWIKIXXXXXXXXXXXXXXXX", "name": "Acme Wiki", "category": "Uncategorized", "metadata": { "createdTime": "2026-01-15T10:00:00Z", "lastUpdatedTime": "2026-01-15T10:00:00Z" }, "urls": [ "*://wiki.acme.example.com/*" ] }, { "type": "catalog", "id": "0AP01CATALOGSSLXXXXXXXXXXXXXX", "name": "Ssl", "category": "Encrypted Tunnel", "metadata": { "createdTime": "2024-04-26T12:48:39Z", "lastUpdatedTime": "2026-01-15T10:00:00Z" }, "urls": [ "*://www.ssllabs.com/*" ], "catalog_name": "ssl" } // ... more applications of any type ], "metadata": { "configurationVersion": { "id": "0CV01EXAMPLEXXXXXXXXXXXXXXXXX", "status": "draft", "number": 0 } }, "pageInfo": { "hasNextPage": true, "cursor": "gaFpvTBBUDAx...", "totalCount": 72518 } } ``` **List one type** narrows to a single type with the same filters: ```bash curl -sS -G "$PB_API_BASE/applications/type/custom" \ -H "Authorization: Bearer $PB_TOKEN" \ --data-urlencode "name=wiki" ``` Response (`200`). Every item is the requested `type`: ```json { "data": [ { "type": "custom", "id": "0AP01ACMEWIKIXXXXXXXXXXXXXXXX", "name": "Acme Wiki", "description": "", "category": "Uncategorized", "metadata": { "createdTime": "2026-01-15T10:00:00Z", "lastUpdatedTime": "2026-01-15T10:00:00Z" }, "urls": [ "*://wiki.acme.example.com/*" ] } // ... more custom applications ], "metadata": { "configurationVersion": { "id": "0CV01EXAMPLEXXXXXXXXXXXXXXXXX", "status": "draft", "number": 0 } }, "pageInfo": { "hasNextPage": false, "cursor": "", "totalCount": 1 } } ``` **Read one** application by ID, either directly or scoped to its type: ```bash curl -sS "$PB_API_BASE/applications/$APP_ID" \ -H "Authorization: Bearer $PB_TOKEN" ``` Response (`200`). The fields depend on `type` (this example is `custom`): ```json { "type": "custom", "id": "0AP01ACMEWIKIXXXXXXXXXXXXXXXX", "name": "Acme Wiki", "category": "Uncategorized", "metadata": { "createdTime": "2026-01-15T10:00:00Z", "lastUpdatedTime": "2026-01-15T10:00:00Z", "createdBy": "api-service-account", "lastUpdatedBy": "api-service-account", "configurationVersion": { "id": "0CV01EXAMPLEXXXXXXXXXXXXXXXXX", "status": "draft", "number": 0 } }, "urls": [ "*://wiki.acme.example.com/*" ] } ``` To discover the valid values for the optional `category` field: ```bash curl -sS "$PB_API_BASE/applications/categories" \ -H "Authorization: Bearer $PB_TOKEN" ``` Response (`200`): ```json { "data": [ "Analytics", "Artificial Intelligence", "Collaboration", "Encrypted Tunnel", "File Sharing", "Instant Messaging" // ... more categories ] } ``` --- ## Bulk operations Two bulk endpoints span all creatable types. Both take the same per-type bodies as the single-object endpoints. **Bulk create** an array of applications of one type (1 to 1000 per call): ```bash curl -sS -X POST "$PB_API_BASE/applications/bulk-create/custom" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '[ { "type": "custom", "name": "Allow - Figma", "urls": [ { "url": "figma.com" } ] }, { "type": "custom", "name": "Allow - Miro", "urls": [ { "url": "miro.com" } ] } ]' ``` Response (`201`) is an array of the created applications: ```json [ { "id": "0AP01ALLOWFIGMAXXXXXXXXXXXXXX", "name": "Allow - Figma" }, { "id": "0AP01ALLOWMIROXXXXXXXXXXXXXXX", "name": "Allow - Miro" } ] ``` **Bulk delete** by `appIds` array (1 to 2000 IDs). The operation is **atomic**: if any ID is invalid, nothing is deleted: ```bash curl -sS -X POST "$PB_API_BASE/applications/bulk-delete" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "appIds": [ "0AP01ACMEWIKIXXXXXXXXXXXXXXXX", "0AP01ACMEDOCSXXXXXXXXXXXXXXXX" ] }' ``` Returns `204` with an empty body. Because the operation is atomic, a single invalid ID returns `400` and deletes nothing. **Note** **There is no bulk-update endpoint.** To change many applications at once, send one `PATCH` per application (each can add or remove URLs with the delta form). To change the applications attached to a *rule* in bulk, see [Bulk-sync a rule's applications](https://pan.dev/prisma-browser/guide/bulk-update-rule-applications-with-delta). --- ## Limits | Limit | Value | |---|---| | URLs per application | 100 by default, raised per tenant on request, up to 15,000 | | URLs per tenant (custom + private + non-web, combined) | 15,000 | | Tags per application | 63 | | Applications per bulk-create call | 1000 | | Application IDs per bulk-delete call | 2000 | | `description` length | 2500 characters | Two separate URL limits apply, and both return `400` when exceeded: - **Per application.** The default is 100 URLs. Your tenant can be raised beyond that, up to 15,000. The same ceiling applies to the `add` and `remove` lists on a delta `PATCH`. - **Per tenant.** Across all `custom`, `private`, and `non-web` applications combined there is a fixed budget of 15,000 URLs. Catalog applications do not count against it; private application CIDRs do. Prefer broader patterns over thousands of exact URLs. Remaining per-type limits (CIDRs, executables) are documented on each type's page. --- ## Endpoint reference | Method | Path | Purpose | |---|---|---| | `GET` | `/applications` | List applications of all types (filter, paginate) | | `GET` | `/applications/type/{type}` | List applications of one type | | `GET` | `/applications/{id}` | Read one application | | `GET` | `/applications/type/{type}/{id}` | Read one application (scoped to type) | | `GET` | `/applications/categories` | List available category values | | `POST` | `/applications/type/{type}` | Create one application | | `POST` | `/applications/bulk-create/{type}` | Create up to 1000 applications | | `PATCH` | `/applications/type/{type}/{id}` | Update one application | | `DELETE` | `/applications/{id}` | Delete one application | | `DELETE` | `/applications/type/{type}/{id}` | Delete one application (scoped to type) | | `POST` | `/applications/bulk-delete` | Delete up to 2000 applications atomically | All paths are under the `/seb-api/v1` base. --- ## Tips and gotchas **Note** **Two URL limits, not one.** A `400` on a URL write can mean either that the application passed its own limit (100 by default) or that the tenant passed its combined budget of 15,000 URLs across `custom`, `private`, and `non-web`. Read the error message to tell them apart. Prefer broader patterns over thousands of exact URLs. **Note** **Check the catalog before you create.** If the application already exists in the catalog, reference the `catalog` application instead of recreating it as `custom`. The same applies to desktop applications: check `localdesktopcatalog` before defining a `localdesktopcustom` application, because a custom application that reuses a catalog executable name is rejected. See [Application catalog](https://pan.dev/prisma-browser/guide/applications-catalog) and [Desktop applications](https://pan.dev/prisma-browser/guide/applications-desktop). --- ## Related - Applications: [Application catalog](https://pan.dev/prisma-browser/guide/applications-catalog), [Custom applications](https://pan.dev/prisma-browser/guide/applications-custom), [Private applications](https://pan.dev/prisma-browser/guide/applications-private), [Non-web applications](https://pan.dev/prisma-browser/guide/applications-non-web), [Desktop applications](https://pan.dev/prisma-browser/guide/applications-desktop), [Application groups](https://pan.dev/prisma-browser/guide/application-groups) - Concepts: [Pagination](https://pan.dev/prisma-browser/guide/pagination), [Delta patch](https://pan.dev/prisma-browser/guide/delta-patch), [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish), [Partial publish](https://pan.dev/prisma-browser/guide/draft-and-publish#partial-publish) - Use cases: [Govern URLs with an allow/block list](https://pan.dev/prisma-browser/guide/manage-allow-block-list), [Bulk-sync a rule's applications](https://pan.dev/prisma-browser/guide/bulk-update-rule-applications-with-delta) --- # Application catalog _Source: https://pan.dev/prisma-browser/guide/applications-catalog | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ The **application catalog** is the Universal Application Directory (UAD): a curated library of SaaS and web applications maintained by Palo Alto Networks. Catalog applications have `type: catalog` and are updated regularly as services add domains, change URLs, or earn new certifications. You can list, read, and reference catalog applications in rules, but you cannot create, edit, or delete them. For curated desktop applications, see the `localdesktopcatalog` type on [Desktop applications](https://pan.dev/prisma-browser/guide/applications-desktop). **On this page:** what you can and cannot do, list and filter, get one, key fields, endpoint reference, tips. --- ## What you can and cannot do | Operation | Supported? | Notes | |---|---|---| | List catalog apps | Yes | GET `/applications` with `type=catalog` | | Read one catalog app | Yes | GET `/applications/{id}` | | Reference in a rule | Yes | By ID, directly or via an application group | | Read `classification` and `tags` | Yes | Returned like any other application. See [Tags and classification](https://pan.dev/prisma-browser/guide/applications#tags-and-classification) | | Set `classification` and `tagIDs` | Not yet | Catalog apps have no create or update endpoint. Setting them through the API is planned for a future release. Until then, set them on the Applications page in the Prisma Browser admin console in Strata Cloud Manager | | Create / edit / delete | No | Catalog apps are managed by Palo Alto Networks | | Draft and publish | N/A | Catalog apps are read-only. The draft model does not apply | --- ## List and filter Filter the full applications list to `type=catalog`. Supports `name` search, pagination (`limit` / `cursor`), and `sort` / `order`. ``` GET /seb-api/v1/applications ``` ```bash curl -sS -G "$PB_API_BASE/applications" \ -H "Authorization: Bearer $PB_TOKEN" \ --data-urlencode "type=catalog" \ --data-urlencode "name=figma" \ --data-urlencode "limit=50" ``` Response (`200`): ```json { "data": [ { "type": "catalog", "id": "0AP01FIGMAXXXXXXXXXXXXXXXXXXX", "name": "Figma", "description": "Collaborative interface design tool.", "category": "Design", "metadata": { "createdTime": "2024-04-26T12:48:39Z", "lastUpdatedTime": "2026-01-15T10:00:00Z" }, "urls": [ "*://www.figma.com/*", "*://figma.com/*" ], "catalog_name": "figma" } // ... more catalog applications ], "metadata": { "configurationVersion": { "id": "0CV01EXAMPLEXXXXXXXXXXXXXXXXX", "status": "draft", "number": 0 } }, "pageInfo": { "hasNextPage": true, "cursor": "gaFpvTBBUDAx...", "totalCount": 1 } } ``` To include compliance and security metadata in the response, add `includeCatalogAttributes=true`: ```bash curl -sS -G "$PB_API_BASE/applications" \ -H "Authorization: Bearer $PB_TOKEN" \ --data-urlencode "type=catalog" \ --data-urlencode "name=figma" \ --data-urlencode "includeCatalogAttributes=true" ``` Response (`200`). Each item gains a `catalog_attributes` object; unknown values are `null`: ```json { "data": [ { "type": "catalog", "id": "0AP01FIGMAXXXXXXXXXXXXXXXXXXX", "name": "Figma", "category": "Design", "urls": [ "*://figma.com/*" ], "catalog_name": "figma", "catalog_attributes": { "encryption_in_transit": true, "encryption_at_rest": true, "soc2": true, "iso_27001": true, "gdpr": true, "hipaa": null // ... many more compliance attributes, null when unknown } } // ... more catalog applications ], "pageInfo": { "hasNextPage": true, "cursor": "gaFpvTBBUDAx...", "totalCount": 1 } } ``` --- ## Get one ``` GET /seb-api/v1/applications/{id} ``` ```bash curl -sS "$PB_API_BASE/applications/$APP_ID" \ -H "Authorization: Bearer $PB_TOKEN" ``` Response (`200`): ```json { "type": "catalog", "id": "0AP01FIGMAXXXXXXXXXXXXXXXXXXX", "name": "Figma", "description": "Collaborative interface design tool.", "category": "Design", "metadata": { "createdTime": "2024-04-26T12:48:39Z", "lastUpdatedTime": "2026-01-15T10:00:00Z" }, "urls": [ "*://figma.com/*" ], "catalog_name": "figma" } ``` To include catalog attributes on a single-app read, append `?includeCatalogAttributes=true` to the URL. --- ## Key fields | Field | Type | Notes | |---|---|---| | `id` | string | Prefixed with `0AP...` | | `type` | string | Always `catalog` for these objects | | `name` | string | Display name as it appears in the directory | | `catalog_name` | string | Canonical catalog identifier (may differ from `name`) | | `urls` | array | The URL patterns that identify this application. Maintained by Palo Alto Networks | | `classification` | string | Read-only through this API. See [Tags and classification](https://pan.dev/prisma-browser/guide/applications#tags-and-classification) | | `tags` | array | Read-only through this API. Objects of `{ id, name }` | ### catalog_attributes Returned when `includeCatalogAttributes=true` is set. Contains security and compliance metadata for the application: | Attribute | Type | What it indicates | |---|---|---| | `encryption_in_transit` | boolean | Data is encrypted in transit | | `encryption_at_rest` | boolean | Data is encrypted at rest | | `data_retention` | string | Data retention policy or period | | `soc1` | boolean | SOC 1 certified | | `soc2` | boolean | SOC 2 certified | | `iso_27001` | boolean | ISO 27001 certified | | `gdpr` | boolean | GDPR compliant | | `hipaa` | boolean | HIPAA compliant | | `pci` | boolean | PCI DSS compliant | | `finra` | boolean | FINRA compliant | Not every application has values for every attribute. A missing or `null` value means the information is not available in the catalog for that app. --- ## Endpoint reference | Method | Path | Purpose | |---|---|---| | `GET` | `/applications` | List applications; filter by `type=catalog` | | `GET` | `/applications/{id}` | Read one catalog application | All paths are under the `/seb-api/v1` base. --- ## Tips and gotchas **Note** **Check the catalog before creating a SaaS application.** If the application already exists in the catalog, reference the `catalog` entry instead of recreating it as `custom`. You inherit its maintained URL list and compliance metadata, and Palo Alto Networks keeps it up to date. Only create a `custom` application when the catalog has no match. **Note** **Catalog apps are maintained and updated regularly.** You do not need to manage their URLs. When a SaaS application adds or changes domains, the catalog entry is updated automatically. This is the main advantage of referencing a catalog app over a custom app for well-known services. **Note** **Request `?includeCatalogAttributes=true` only when you need it.** The compliance metadata is not included by default to keep response payloads small. Add the parameter when you are auditing or building a catalog browser. **Note** **Classification and tags are returned, but you cannot set them through this API yet.** A catalog application returns `classification` and `tags` like any other application, and those values are scoped to your tenant. Setting them through the API is planned for a future release. Until then, set them on the Applications page in the Prisma Browser admin console in Strata Cloud Manager. --- ## Related - Applications: [Applications overview](https://pan.dev/prisma-browser/guide/applications), [Tags and classification](https://pan.dev/prisma-browser/guide/applications#tags-and-classification), [Custom applications](https://pan.dev/prisma-browser/guide/applications-custom), [Desktop applications](https://pan.dev/prisma-browser/guide/applications-desktop), [Application groups](https://pan.dev/prisma-browser/guide/application-groups) - Concepts: [Pagination](https://pan.dev/prisma-browser/guide/pagination) --- # Custom applications _Source: https://pan.dev/prisma-browser/guide/applications-custom | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ A **custom application** (`type: custom`) is a SaaS or web application you define by a list of URL patterns. Use it when the application is not already in the [application catalog](https://pan.dev/prisma-browser/guide/applications-catalog) and you want policy to recognize that traffic by a name of your choosing. Reference it from an [access and data rule](https://pan.dev/prisma-browser/guide/access-and-data-rules) directly or through an [application group](https://pan.dev/prisma-browser/guide/application-groups). **On this page:** key fields, limits, retrieve, create, update, delete, endpoint reference, tips, examples. **Note** Check the [application catalog](https://pan.dev/prisma-browser/guide/applications-catalog) first. If the application already exists there, reference the `catalog` application instead of recreating it as `custom`. --- ## Key fields | Field | Type | Required | Notes | |---|---|---|---| | `name` | string | yes | Display name shown in policy and events | | `type` | string | yes | Must be `custom` | | `urls` | array of URL objects | yes | Each is `{ "url": "...", "strict_mode": false }`. See Limits for the count | | `description` | string | no | Up to 2500 characters | | `category` | string | no | One of the values from `GET /applications/categories` | | `classification` | string | no | See [Tags and classification](https://pan.dev/prisma-browser/guide/applications#tags-and-classification) | | `tagIDs` | array or object | no | See [Tags and classification](https://pan.dev/prisma-browser/guide/applications#tags-and-classification) | Each `urls` entry is an object with a `url` pattern and an optional `strict_mode` flag: ```json { "url": "wiki.acme.example.com", "strict_mode": false } ``` | `strict_mode` | Behavior | |---|---| | `false` (default) | Protocol defaults to `*://`; `www` subdomain logic is applied; a trailing `/*` is added if no path is given. So `wiki.acme.example.com` becomes `*://wiki.acme.example.com/*`. | | `true` | The URL is saved exactly as entered. No normalization. Use this when you need an exact path match (for example `https://app.example.com/admin` and nothing else). | --- ## Limits | Limit | Value | |---|---| | URLs per application | 100 by default, raised per tenant on request, up to 15,000 | | `description` length | 2500 characters | | Tags per application | 63 | | Counts against the tenant URL budget (custom + private + non-web) | 15,000 total | The per-application URL limit starts at 100 and can be raised for your tenant, up to 15,000. The same ceiling applies to the `add` and `remove` lists on a delta `PATCH`. Both limits are described in [Limits](https://pan.dev/prisma-browser/guide/applications#limits) on the applications overview. --- ## Retrieve List custom applications (supports `name`, `url`, pagination, `sort` / `order`): ``` GET /seb-api/v1/applications/type/custom ``` ```bash curl -sS -G "$PB_API_BASE/applications/type/custom" \ -H "Authorization: Bearer $PB_TOKEN" \ --data-urlencode "name=wiki" \ --data-urlencode "limit=50" ``` Response (`200`): ```json { "data": [ { "type": "custom", "id": "0AP01ACMEWIKIXXXXXXXXXXXXXXXX", "name": "Acme Wiki", "description": "", "category": "Uncategorized", "metadata": { "createdTime": "2026-01-15T10:00:00Z", "lastUpdatedTime": "2026-01-15T10:00:00Z" }, "urls": [ "*://wiki.acme.example.com/*" ] } // ... more applications ], "metadata": { "configurationVersion": { "id": "0CV01EXAMPLEXXXXXXXXXXXXXXXXX", "status": "draft", "number": 0 } }, "pageInfo": { "hasNextPage": false, "cursor": "", "totalCount": 1 } } ``` Read one: ``` GET /seb-api/v1/applications/type/custom/{id} ``` ```bash curl -sS "$PB_API_BASE/applications/type/custom/$APP_ID" \ -H "Authorization: Bearer $PB_TOKEN" ``` Response (`200`). Note that `urls` are returned in their normalized string form: ```json { "type": "custom", "id": "0AP01ACMEWIKIXXXXXXXXXXXXXXXX", "name": "Acme Wiki", "category": "Uncategorized", "metadata": { "createdTime": "2026-01-15T10:00:00Z", "lastUpdatedTime": "2026-01-15T10:00:00Z", "createdBy": "api-service-account", "lastUpdatedBy": "api-service-account", "configurationVersion": { "id": "0CV01EXAMPLEXXXXXXXXXXXXXXXXX", "status": "draft", "number": 0 } }, "urls": [ "*://wiki.acme.example.com/*" ] } ``` --- ## Create ``` POST /seb-api/v1/applications/type/custom ``` ```bash curl -sS -X POST "$PB_API_BASE/applications/type/custom" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Acme Wiki", "type": "custom", "urls": [ { "url": "wiki.acme.example.com" } ] }' ``` Response (`201`): ```json { "id": "0AP01ACMEWIKIXXXXXXXXXXXXXXXX" } ``` Capture the ID for later snippets: ```bash export APP_ID='0AP01ACMEWIKIXXXXXXXXXXXXXXXX' ``` To create many at once, use bulk create (1 to 1000 per call): `POST /seb-api/v1/applications/bulk-create/custom` with a JSON array of the same bodies. See [Bulk operations](https://pan.dev/prisma-browser/guide/applications#bulk-operations). --- ## Update `PATCH` updates only the fields you send. The `urls` field accepts two forms: - **Replace** the whole list: send a `urls` array. The new list fully replaces the old one. - **Delta** add/remove: send `urls: { "add": [...], "remove": [...] }`. Use this to add or remove specific URLs without re-sending the entire list, which avoids overwriting concurrent edits. Either way, the total after the change cannot exceed your tenant's per-application URL limit. `classification` and `tagIDs` accept the same replace-or-delta treatment; see [Tags and classification](https://pan.dev/prisma-browser/guide/applications#tags-and-classification). ``` PATCH /seb-api/v1/applications/type/custom/{id} ``` ```bash curl -sS -X PATCH "$PB_API_BASE/applications/type/custom/$APP_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "type": "custom", "urls": { "add": [ { "url": "docs.acme.example.com" } ] } }' ``` Response (`200`): ```json { "id": "0AP01ACMEWIKIXXXXXXXXXXXXXXXX" } ``` Remember: this edits the **draft**. Publish to make it live (see [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish)). With [partial publish](https://pan.dev/prisma-browser/guide/draft-and-publish#partial-publish) you can publish just this object. --- ## Delete ``` DELETE /seb-api/v1/applications/type/custom/{id} ``` ```bash curl -sS -X DELETE "$PB_API_BASE/applications/type/custom/$APP_ID" \ -H "Authorization: Bearer $PB_TOKEN" ``` Returns `204` with an empty body. To delete many at once, use [bulk delete](https://pan.dev/prisma-browser/guide/applications#bulk-operations) (`POST /applications/bulk-delete`, up to 2000 IDs, atomic). --- ## Endpoint reference | Method | Path | Purpose | |---|---|---| | `GET` | `/applications/type/custom` | List custom applications (filter, paginate) | | `GET` | `/applications/type/custom/{id}` | Read one custom application | | `POST` | `/applications/type/custom` | Create one custom application | | `POST` | `/applications/bulk-create/custom` | Create up to 1000 custom applications | | `PATCH` | `/applications/type/custom/{id}` | Update one custom application | | `DELETE` | `/applications/type/custom/{id}` | Delete one custom application | All paths are under the `/seb-api/v1` base. --- ## Tips and gotchas **Note** **One application, many URLs.** A single custom application holds 100 URLs by default, and more if your tenant limit is raised. For an allow/block list, keep one application and add or remove URLs on it rather than creating an application per URL. See [Govern URLs with an allow/block list](https://pan.dev/prisma-browser/guide/manage-allow-block-list). **Caution** **Strict mode changes matching.** With `strict_mode: false` the pattern is expanded (host-wide match). With `strict_mode: true` it is matched exactly as entered. Use strict mode only when you need a single exact path. --- ## Examples
Create with mixed strict and non-strict URLs `wiki...` is expanded host-wide; the admin path is matched exactly. ```bash curl -sS -X POST "$PB_API_BASE/applications/type/custom" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Acme Wiki", "type": "custom", "description": "Internal wiki", "urls": [ { "url": "wiki.acme.example.com", "strict_mode": false }, { "url": "https://wiki.acme.example.com/admin", "strict_mode": true } ] }' ``` Response (`201`): ```json { "id": "0AP01ACMEWIKIXXXXXXXXXXXXXXXX" } ```
Add and remove URLs in one call (delta) Adds one URL and removes another without re-sending the full list. ```bash curl -sS -X PATCH "$PB_API_BASE/applications/type/custom/$APP_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "type": "custom", "urls": { "add": [ { "url": "docs.acme.example.com" } ], "remove": [ { "url": "old.acme.example.com" } ] } }' ``` Response (`200`): ```json { "id": "0AP01ACMEWIKIXXXXXXXXXXXXXXXX" } ```
Replace the entire URL list Sending a `urls` array replaces the whole list. ```bash curl -sS -X PATCH "$PB_API_BASE/applications/type/custom/$APP_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "type": "custom", "urls": [ { "url": "wiki.acme.example.com" }, { "url": "docs.acme.example.com" } ] }' ``` Response (`200`): ```json { "id": "0AP01ACMEWIKIXXXXXXXXXXXXXXXX" } ```
--- ## Related - Applications: [Applications overview](https://pan.dev/prisma-browser/guide/applications), [Tags and classification](https://pan.dev/prisma-browser/guide/applications#tags-and-classification), [Private applications](https://pan.dev/prisma-browser/guide/applications-private), [Application groups](https://pan.dev/prisma-browser/guide/application-groups) - Concepts: [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish), [Partial publish](https://pan.dev/prisma-browser/guide/draft-and-publish#partial-publish), [Pagination](https://pan.dev/prisma-browser/guide/pagination) - Use cases: [Govern URLs with an allow/block list](https://pan.dev/prisma-browser/guide/manage-allow-block-list) --- # Private applications _Source: https://pan.dev/prisma-browser/guide/applications-private | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ A **private application** (`type: private`) is an internal (private) application that is reached through Prisma Access rather than the public internet. You define it by its URLs plus a primary URL, and optionally by CIDR ranges and a DNS domain suffix. Reference it from an [access and data rule](https://pan.dev/prisma-browser/guide/access-and-data-rules) directly or through an [application group](https://pan.dev/prisma-browser/guide/application-groups). **On this page:** key fields, limits, retrieve, create, update, delete, endpoint reference, tips, examples. --- ## Key fields | Field | Type | Required | Notes | |---|---|---|---| | `name` | string | yes | Display name shown in policy and events | | `type` | string | yes | Must be `private` | | `urls` | array of URL objects | yes | Each is `{ "url": "...", "strict_mode": false }`. See Limits for the count | | `primaryUrl` | string | yes | The main URL used to represent the application | | `routeToPrisma` | boolean | yes | Whether traffic is routed through Prisma Access | | `cidrs` | array of strings | no | IPv4 CIDR ranges, /8 to /32; up to 100 | | `domainSuffix` | string (nullable) | no | DNS suffix appended to single-label hostnames (for example `corp.example.com`). Required when any URL or the primary URL uses a short hostname | | `description` | string | no | Up to 2500 characters | | `category` | string | no | One of the values from `GET /applications/categories` | | `classification` | string | no | See [Tags and classification](https://pan.dev/prisma-browser/guide/applications#tags-and-classification) | | `tagIDs` | array or object | no | See [Tags and classification](https://pan.dev/prisma-browser/guide/applications#tags-and-classification) | Each `urls` entry is an object with a `url` pattern and an optional `strict_mode` flag: ```json { "url": "hr.corp.example.com", "strict_mode": false } ``` | `strict_mode` | Behavior | |---|---| | `false` (default) | Protocol defaults to `https://` (wildcards are **not** allowed); no automatic `www` handling (`www` is a distinct prefix); IPv4 and port numbers are supported; a trailing `*` is added to paths. | | `true` | The URL is saved exactly as entered. No normalization. | **Caution** **No wildcards in private URLs.** Unlike custom applications, the protocol and host do not accept `*`. The protocol always defaults to `https://`. --- ## Limits | Limit | Value | |---|---| | URLs per application | 100 by default, raised per tenant on request, up to 15,000 | | `cidrs` per application | up to 100 (IPv4 only, /8 to /32) | | Tags per application | 63 | | `description` length | 2500 characters | | Counts against the tenant URL budget (custom + private + non-web) | 15,000 total | The per-application URL limit starts at 100 and can be raised for your tenant, up to 15,000. The same ceiling applies to the `add` and `remove` lists on a delta `PATCH`. The `cidrs` cap stays at 100 either way, and CIDRs count against the tenant URL budget alongside URLs. Both budgets are described in [Limits](https://pan.dev/prisma-browser/guide/applications#limits) on the applications overview. --- ## Retrieve List private applications (supports `name`, `url`, pagination, `sort` / `order`): ``` GET /seb-api/v1/applications/type/private ``` ```bash curl -sS -G "$PB_API_BASE/applications/type/private" \ -H "Authorization: Bearer $PB_TOKEN" \ --data-urlencode "limit=50" ``` Response (`200`). `urls` are returned in their normalized string form: ```json { "data": [ { "type": "private", "id": "0AP01HRPORTALXXXXXXXXXXXXXXXX", "name": "Internal HR Portal", "description": "", "category": "Uncategorized", "metadata": { "createdTime": "2026-01-15T10:00:00Z", "lastUpdatedTime": "2026-01-15T10:00:00Z" }, "urls": [ "https://hr.corp.example.com/*" ], "primaryUrl": "hr.corp.example.com", "routeToPrisma": true, "domainSuffix": null } // ... more private applications ], "metadata": { "configurationVersion": { "id": "0CV01EXAMPLEXXXXXXXXXXXXXXXXX", "status": "draft", "number": 0 } }, "pageInfo": { "hasNextPage": false, "cursor": "", "totalCount": 1 } } ``` Read one: ``` GET /seb-api/v1/applications/type/private/{id} ``` ```bash curl -sS "$PB_API_BASE/applications/type/private/$APP_ID" \ -H "Authorization: Bearer $PB_TOKEN" ``` Response (`200`): ```json { "type": "private", "id": "0AP01HRPORTALXXXXXXXXXXXXXXXX", "name": "Internal HR Portal", "category": "Uncategorized", "metadata": { "createdTime": "2026-01-15T10:00:00Z", "lastUpdatedTime": "2026-01-15T10:00:00Z" }, "urls": [ "https://hr.corp.example.com/*" ], "primaryUrl": "hr.corp.example.com", "routeToPrisma": true, "domainSuffix": null } ``` --- ## Create ``` POST /seb-api/v1/applications/type/private ``` ```bash curl -sS -X POST "$PB_API_BASE/applications/type/private" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Internal HR Portal", "type": "private", "primaryUrl": "hr.corp.example.com", "routeToPrisma": true, "urls": [ { "url": "hr.corp.example.com" } ] }' ``` Response (`201`): ```json { "id": "0AP01HRPORTALXXXXXXXXXXXXXXXX" } ``` Capture the ID for later snippets: ```bash export APP_ID='0AP01HRPORTALXXXXXXXXXXXXXXXX' ``` To create many at once, use bulk create: `POST /seb-api/v1/applications/bulk-create/private` with a JSON array. See [Bulk operations](https://pan.dev/prisma-browser/guide/applications#bulk-operations). --- ## Update `PATCH` updates only the fields you send (`type` is always required). The `urls` field accepts **replace** (send an array) or **delta** (`urls: { "add": [...], "remove": [...] }`). The `cidrs` and `tagIDs` fields support the same delta form. For `domainSuffix`, send a string to set it, JSON `null` to clear it, or omit it to leave it unchanged. To set `classification` or `tagIDs`, see [Tags and classification](https://pan.dev/prisma-browser/guide/applications#tags-and-classification). ``` PATCH /seb-api/v1/applications/type/private/{id} ``` ```bash curl -sS -X PATCH "$PB_API_BASE/applications/type/private/$APP_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "type": "private", "urls": { "add": [ { "url": "hr-api.corp.example.com" } ] } }' ``` Response (`200`): ```json { "id": "0AP01HRPORTALXXXXXXXXXXXXXXXX" } ``` Remember: this edits the **draft**. Publish to make it live (see [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish)). With [partial publish](https://pan.dev/prisma-browser/guide/draft-and-publish#partial-publish) you can publish just this object. --- ## Delete ``` DELETE /seb-api/v1/applications/type/private/{id} ``` ```bash curl -sS -X DELETE "$PB_API_BASE/applications/type/private/$APP_ID" \ -H "Authorization: Bearer $PB_TOKEN" ``` Returns `204` with an empty body. To delete many at once, use [bulk delete](https://pan.dev/prisma-browser/guide/applications#bulk-operations) (`POST /applications/bulk-delete`, up to 2000 IDs, atomic). --- ## Endpoint reference | Method | Path | Purpose | |---|---|---| | `GET` | `/applications/type/private` | List private applications (filter, paginate) | | `GET` | `/applications/type/private/{id}` | Read one private application | | `POST` | `/applications/type/private` | Create one private application | | `POST` | `/applications/bulk-create/private` | Create up to 1000 private applications | | `PATCH` | `/applications/type/private/{id}` | Update one private application | | `DELETE` | `/applications/type/private/{id}` | Delete one private application | All paths are under the `/seb-api/v1` base. --- ## Tips and gotchas **Note** **Domain suffix for short hostnames.** If a URL or the primary URL is a single-label hostname (no dots), set `domainSuffix` so it resolves (for example `corp.example.com`). Without it, short hostnames are rejected. --- ## Examples
Create a private application with CIDRs and a domain suffix Covers a short hostname (`hr`) via `domainSuffix` and an internal IP range via `cidrs`. ```bash curl -sS -X POST "$PB_API_BASE/applications/type/private" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Internal HR Portal", "type": "private", "primaryUrl": "hr.corp.example.com", "routeToPrisma": true, "domainSuffix": "corp.example.com", "urls": [ { "url": "hr" }, { "url": "hr.corp.example.com" } ], "cidrs": [ "10.10.0.0/16" ] }' ``` Response (`201`): ```json { "id": "0AP01HRPORTALXXXXXXXXXXXXXXXX" } ```
Add a URL and a CIDR in one call (delta) ```bash curl -sS -X PATCH "$PB_API_BASE/applications/type/private/$APP_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "type": "private", "urls": { "add": [ { "url": "hr-api.corp.example.com" } ] }, "cidrs": { "add": [ "10.20.0.0/16" ] } }' ``` Response (`200`): ```json { "id": "0AP01HRPORTALXXXXXXXXXXXXXXXX" } ```
--- ## Related - Applications: [Applications overview](https://pan.dev/prisma-browser/guide/applications), [Tags and classification](https://pan.dev/prisma-browser/guide/applications#tags-and-classification), [Custom applications](https://pan.dev/prisma-browser/guide/applications-custom), [Application groups](https://pan.dev/prisma-browser/guide/application-groups) - Concepts: [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish), [Partial publish](https://pan.dev/prisma-browser/guide/draft-and-publish#partial-publish), [Pagination](https://pan.dev/prisma-browser/guide/pagination) --- # Non-web applications _Source: https://pan.dev/prisma-browser/guide/applications-non-web | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ A **non-web application** (`type: non-web`) represents a remote connection over RDP or SSH, defined by its network address, protocol, and port rather than by URLs. The only protocols supported are `rdp` and `ssh`. Reference it from an [access and data rule](https://pan.dev/prisma-browser/guide/access-and-data-rules) directly or through an [application group](https://pan.dev/prisma-browser/guide/application-groups). **On this page:** key fields, limits, retrieve, create, update, delete, endpoint reference, tips, examples. --- ## Key fields | Field | Type | Required | Notes | |---|---|---|---| | `name` | string | yes | Display name shown in policy and events | | `type` | string | yes | Must be `non-web` | | `address` | string | yes | Host or IP address of the target host. Accepted on write and returned on read | | `protocol` | string | yes | Remote-access protocol: `rdp` or `ssh` | | `port` | string | yes | Port or port range | | `routeToPrisma` | boolean | no | Whether traffic is routed through Prisma Access | | `description` | string | no | Up to 2500 characters | | `classification` | string | no | See [Tags and classification](https://pan.dev/prisma-browser/guide/applications#tags-and-classification) | | `tagIDs` | array or object | no | See [Tags and classification](https://pan.dev/prisma-browser/guide/applications#tags-and-classification) | `address`, `protocol`, and `port` together form the connection tuple, and that tuple identifies the application: two non-web applications cannot share all three. You do not supply a `urls` list, but reads return one. The Prisma Browser generates a single `chrome-extension://` entry that the Prisma Browser Extension uses to hand the connection off to the right remote-connection handler. It is derived from the application ID and the protocol, not from `address`, so it looks unrelated to what you sent. Treat it as read-only. --- ## Limits | Limit | Value | |---|---| | `description` length | 2500 characters | --- ## Retrieve List non-web applications (supports `name`, pagination, `sort` / `order`): ``` GET /seb-api/v1/applications/type/non-web ``` ```bash curl -sS -G "$PB_API_BASE/applications/type/non-web" \ -H "Authorization: Bearer $PB_TOKEN" \ --data-urlencode "limit=50" ``` Response (`200`). The application is returned with its `address`, `protocol`, `port`, and `routeToPrisma`, plus a generated `urls` entry: ```json { "data": [ { "type": "non-web", "id": "0AP01SSHBASTIONXXXXXXXXXXXXXX", "name": "Internal SSH", "description": "Bastion host SSH", "metadata": { "createdTime": "2026-01-15T10:00:00Z", "lastUpdatedTime": "2026-01-15T10:00:00Z" }, "urls": [ "chrome-extension:///internal-ssh.html?appid=0AP01SSHBASTIONXXXXXXXXXXXXXX" ], "address": "bastion.corp.example.com", "protocol": "ssh", "port": "22", "routeToPrisma": true } // ... more non-web applications ], "metadata": { "configurationVersion": { "id": "0CV01EXAMPLEXXXXXXXXXXXXXXXXX", "status": "draft", "number": 0 } }, "pageInfo": { "hasNextPage": false, "cursor": "", "totalCount": 1 } } ``` Read one: ``` GET /seb-api/v1/applications/type/non-web/{id} ``` ```bash curl -sS "$PB_API_BASE/applications/type/non-web/$APP_ID" \ -H "Authorization: Bearer $PB_TOKEN" ``` Response (`200`): ```json { "type": "non-web", "id": "0AP01SSHBASTIONXXXXXXXXXXXXXX", "name": "Internal SSH", "description": "Bastion host SSH", "metadata": { "createdTime": "2026-01-15T10:00:00Z", "lastUpdatedTime": "2026-01-15T10:00:00Z" }, "urls": [ "chrome-extension:///internal-ssh.html?appid=0AP01SSHBASTIONXXXXXXXXXXXXXX" ], "address": "bastion.corp.example.com", "protocol": "ssh", "port": "22", "routeToPrisma": true } ``` --- ## Create ``` POST /seb-api/v1/applications/type/non-web ``` ```bash curl -sS -X POST "$PB_API_BASE/applications/type/non-web" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Internal SSH", "type": "non-web", "address": "bastion.corp.example.com", "protocol": "ssh", "port": "22" }' ``` Response (`201`): ```json { "id": "0AP01SSHBASTIONXXXXXXXXXXXXXX" } ``` Capture the ID for later snippets: ```bash export APP_ID='0AP01SSHBASTIONXXXXXXXXXXXXXX' ``` To create many at once, use bulk create: `POST /seb-api/v1/applications/bulk-create/non-web` with a JSON array. See [Bulk operations](https://pan.dev/prisma-browser/guide/applications#bulk-operations). --- ## Update `PATCH` updates only the fields you send (`type` is always required). Send any of `address`, `protocol`, `port`, or `routeToPrisma` to change them, or `classification` and `tagIDs` (see [Tags and classification](https://pan.dev/prisma-browser/guide/applications#tags-and-classification)). ``` PATCH /seb-api/v1/applications/type/non-web/{id} ``` ```bash curl -sS -X PATCH "$PB_API_BASE/applications/type/non-web/$APP_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "type": "non-web", "port": "2222" }' ``` Response (`200`): ```json { "id": "0AP01SSHBASTIONXXXXXXXXXXXXXX" } ``` Remember: this edits the **draft**. Publish to make it live (see [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish)). With [partial publish](https://pan.dev/prisma-browser/guide/draft-and-publish#partial-publish) you can publish just this object. --- ## Delete ``` DELETE /seb-api/v1/applications/type/non-web/{id} ``` ```bash curl -sS -X DELETE "$PB_API_BASE/applications/type/non-web/$APP_ID" \ -H "Authorization: Bearer $PB_TOKEN" ``` Returns `204` with an empty body. To delete many at once, use [bulk delete](https://pan.dev/prisma-browser/guide/applications#bulk-operations) (`POST /applications/bulk-delete`, up to 2000 IDs, atomic). --- ## Endpoint reference | Method | Path | Purpose | |---|---|---| | `GET` | `/applications/type/non-web` | List non-web applications (filter, paginate) | | `GET` | `/applications/type/non-web/{id}` | Read one non-web application | | `POST` | `/applications/type/non-web` | Create one non-web application | | `POST` | `/applications/bulk-create/non-web` | Create up to 1000 non-web applications | | `PATCH` | `/applications/type/non-web/{id}` | Update one non-web application | | `DELETE` | `/applications/type/non-web/{id}` | Delete one non-web application | All paths are under the `/seb-api/v1` base. --- ## Tips and gotchas **Note** **You do not set URLs, but reads return one.** Non-web applications are matched by `address`, `protocol`, and `port`, not by URL patterns. The single `urls` entry in the response is a generated `chrome-extension://` handoff link built from the application ID and protocol, not from the address you sent. Do not try to write it or match on it. If you need URL-based matching, use a [custom](https://pan.dev/prisma-browser/guide/applications-custom) or [private](https://pan.dev/prisma-browser/guide/applications-private) application. **Caution** **The connection tuple must be unique.** Creating a second non-web application with the same `address`, `protocol`, and `port` as an existing one is rejected. Change at least one of the three. --- ## Examples
Create a non-web SSH connection ```bash curl -sS -X POST "$PB_API_BASE/applications/type/non-web" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Internal SSH", "type": "non-web", "description": "Bastion host SSH", "address": "bastion.corp.example.com", "protocol": "ssh", "port": "22", "routeToPrisma": true }' ``` Response (`201`): ```json { "id": "0AP01SSHBASTIONXXXXXXXXXXXXXX" } ```
--- ## Related - Applications: [Applications overview](https://pan.dev/prisma-browser/guide/applications), [Tags and classification](https://pan.dev/prisma-browser/guide/applications#tags-and-classification), [Private applications](https://pan.dev/prisma-browser/guide/applications-private), [Application groups](https://pan.dev/prisma-browser/guide/application-groups) - Concepts: [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish), [Partial publish](https://pan.dev/prisma-browser/guide/draft-and-publish#partial-publish), [Pagination](https://pan.dev/prisma-browser/guide/pagination) --- # Desktop applications _Source: https://pan.dev/prisma-browser/guide/applications-desktop | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ A **desktop application** represents a native application installed on the endpoint, identified by the executable file names it runs under. Use it when policy needs to recognize a program outside the browser. Reference it from an [access and data rule](https://pan.dev/prisma-browser/guide/access-and-data-rules) directly or through an [application group](https://pan.dev/prisma-browser/guide/application-groups). There are two types. You define a `localdesktopcustom` application yourself. A `localdesktopcatalog` application comes from the catalog that Palo Alto Networks curates, and is read-only. **On this page:** custom vs catalog, key fields, limits, retrieve, create, update, delete, endpoint reference, tips, examples. --- ## Custom vs catalog | | `localdesktopcustom` | `localdesktopcatalog` | |---|---|---| | Who defines it | You | Palo Alto Networks | | Scope | Your tenant only | Shared by all tenants | | Create, update, delete | Yes | No | | List and read | Yes | Yes | | Reference from a rule | Yes | Yes | | Set `classification` and `tagIDs` | Yes | Not yet. Setting them through the API is planned for a future release. Until then, set them on the Applications page in the Prisma Browser admin console in Strata Cloud Manager | | Executables | 1 to 50, you choose them | Fixed, curated | Both types are Windows-only, and both are matched the same way: the Prisma Browser compares the running process against the `originalFileName` entries in `executables`, case-insensitively. Check the catalog before you define your own application. If a catalog entry already covers the program, reference it instead of recreating it, and note that a custom application cannot reuse an executable name that a catalog entry already claims. --- ## Key fields For a `localdesktopcustom` application you send: | Field | Type | Required | Notes | |---|---|---|---| | `name` | string | yes | Display name shown in policy and events | | `type` | string | yes | Must be `localdesktopcustom` | | `executables` | object | yes | `{ "data": [ { "originalFileName": "app.exe" } ] }`; 1 to 50 entries | | `description` | string | no | Up to 2500 characters | | `category` | string | no | One of the values from `GET /applications/categories` | | `classification` | string | no | See [Tags and classification](https://pan.dev/prisma-browser/guide/applications#tags-and-classification) | | `tagIDs` | array or object | no | See [Tags and classification](https://pan.dev/prisma-browser/guide/applications#tags-and-classification) | A `localdesktopcatalog` application returns the same fields, including `classification` and `tags`, but accepts none of them: it has no create or update endpoint. Desktop applications have no `urls` list, so the URL object and strict mode do not apply. **Executable names** identify the program. They follow Windows file-name rules: up to 255 characters, no leading space, no trailing dot or space, none of the characters `<>:"/\|?*`, and no Windows reserved device name (`CON`, `PRN`, `AUX`, `NUL`, `COM1` to `COM9`, `LPT1` to `LPT9`). A name that contains "Prisma Access Browser" or "Prisma Browser" is rejected. --- ## Limits | Limit | Value | |---|---| | Executables per application | 1 to 50 | | Executable file name length | 255 characters | | `description` length | 2500 characters | --- ## Retrieve ### Your desktop applications List the `localdesktopcustom` applications you defined (supports `name`, pagination, `sort` / `order`): ``` GET /seb-api/v1/applications/type/localdesktopcustom ``` ```bash curl -sS -G "$PB_API_BASE/applications/type/localdesktopcustom" \ -H "Authorization: Bearer $PB_TOKEN" \ --data-urlencode "limit=50" ``` Response (`200`): ```json { "data": [ { "type": "localdesktopcustom", "id": "0AP01ACMEDESKTOPXXXXXXXXXXXXX", "name": "Acme Desktop Client", "description": "", "category": "Uncategorized", "metadata": { "createdTime": "2026-01-15T10:00:00Z", "lastUpdatedTime": "2026-01-15T10:00:00Z" }, "executables": { "data": [ { "originalFileName": "acme-client.exe" } ] } } // ... more desktop applications ], "metadata": { "configurationVersion": { "id": "0CV01EXAMPLEXXXXXXXXXXXXXXXXX", "status": "draft", "number": 0 } }, "pageInfo": { "hasNextPage": false, "cursor": "", "totalCount": 1 } } ``` Read one: ``` GET /seb-api/v1/applications/type/localdesktopcustom/{id} ``` ```bash curl -sS "$PB_API_BASE/applications/type/localdesktopcustom/$APP_ID" \ -H "Authorization: Bearer $PB_TOKEN" ``` Response (`200`): ```json { "type": "localdesktopcustom", "id": "0AP01ACMEDESKTOPXXXXXXXXXXXXX", "name": "Acme Desktop Client", "description": "Acme native client and helper", "category": "Uncategorized", "metadata": { "createdTime": "2026-01-15T10:00:00Z", "lastUpdatedTime": "2026-01-15T10:00:00Z" }, "executables": { "data": [ { "originalFileName": "acme-client.exe" }, { "originalFileName": "acme-helper.exe" } ] } } ``` ### The desktop catalog The catalog holds roughly 80 well-known Windows applications curated by Palo Alto Networks, covering browsers (Google Chrome, Microsoft Edge, Mozilla Firefox, Brave, Vivaldi), developer tools (Visual Studio Code, IntelliJ, Git, Docker Desktop, MobaXterm), Office programs (Word, Excel, PowerPoint, Outlook, Teams), AI assistants (ChatGPT, Claude Desktop, Copilot, Perplexity Comet), file sharing (Box, Dropbox, Google Drive, OneDrive), collaboration (Slack, Zoom, Webex, WhatsApp), and Windows utilities (Windows PowerShell, Windows Terminal, Task Manager, Registry Editor). The list grows over time, so query it rather than hard-coding entries. List the catalog the same way you list your own applications: ``` GET /seb-api/v1/applications/type/localdesktopcatalog ``` ```bash curl -sS -G "$PB_API_BASE/applications/type/localdesktopcatalog" \ -H "Authorization: Bearer $PB_TOKEN" \ --data-urlencode "name=Zoom" ``` Response (`200`): ```json { "data": [ { "type": "localdesktopcatalog", "id": "0APYWYTF8R2NS54S1V3NRT7GXBYAF", "name": "Zoom", "description": "Video conferencing tool providing high-quality meetings, webinars, and screen sharing.", "category": "Internet Conferencing", "metadata": { "createdTime": "2026-03-26T16:12:00Z", "lastUpdatedTime": "2026-03-26T16:12:00Z" }, "executables": { "data": [ { "originalFileName": "Zoom" } ] } } ], "metadata": { "configurationVersion": { "id": "0CV01EXAMPLEXXXXXXXXXXXXXXXXX", "status": "draft", "number": 0 } }, "pageInfo": { "hasNextPage": false, "cursor": "", "totalCount": 1 } } ``` To browse the whole catalog, drop the `name` filter and page through with `limit` and `cursor` (see [Pagination](https://pan.dev/prisma-browser/guide/pagination)). To read a single entry, call `GET /seb-api/v1/applications/type/localdesktopcatalog/{id}`. ### Reference a desktop application from a rule Both types are referenced the same way, by ID, in the `localDesktop` segment of an [access and data rule](https://pan.dev/prisma-browser/guide/access-and-data-rules). Take the `id` from the list call above: ```json { "applications": { "localDesktop": { "accessMode": "specific", "specific": { "applicationIds": [ "0APYWYTF8R2NS54S1V3NRT7GXBYAF" ] } } } } ``` `accessMode` takes three values: `none` for no desktop applications, `any` for every desktop application defined in the tenant, and `specific` for the IDs you list. The `localDesktop` segment applies to Windows devices only. --- ## Create ``` POST /seb-api/v1/applications/type/localdesktopcustom ``` ```bash curl -sS -X POST "$PB_API_BASE/applications/type/localdesktopcustom" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Acme Desktop Client", "type": "localdesktopcustom", "executables": { "data": [ { "originalFileName": "acme-client.exe" } ] } }' ``` Response (`201`): ```json { "id": "0AP01ACMEDESKTOPXXXXXXXXXXXXX" } ``` Capture the ID for later snippets: ```bash export APP_ID='0AP01ACMEDESKTOPXXXXXXXXXXXXX' ``` Only `localdesktopcustom` applications can be created. There is no create endpoint for `localdesktopcatalog`. To create many at once, use bulk create: `POST /seb-api/v1/applications/bulk-create/localdesktopcustom` with a JSON array. See [Bulk operations](https://pan.dev/prisma-browser/guide/applications#bulk-operations). --- ## Update `PATCH` updates only the fields you send (`type` is always required). Sending `executables` replaces the whole list, so include every executable you want to keep. To set `classification` or `tagIDs`, see [Tags and classification](https://pan.dev/prisma-browser/guide/applications#tags-and-classification). Only `localdesktopcustom` applications can be updated; there is no update endpoint for `localdesktopcatalog`. ``` PATCH /seb-api/v1/applications/type/localdesktopcustom/{id} ``` ```bash curl -sS -X PATCH "$PB_API_BASE/applications/type/localdesktopcustom/$APP_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "type": "localdesktopcustom", "executables": { "data": [ { "originalFileName": "acme-client.exe" }, { "originalFileName": "acme-helper.exe" } ] } }' ``` Response (`200`): ```json { "id": "0AP01ACMEDESKTOPXXXXXXXXXXXXX" } ``` Remember: this edits the **draft**. Publish to make it live (see [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish)). With [partial publish](https://pan.dev/prisma-browser/guide/draft-and-publish#partial-publish) you can publish just this object. --- ## Delete ``` DELETE /seb-api/v1/applications/type/localdesktopcustom/{id} ``` ```bash curl -sS -X DELETE "$PB_API_BASE/applications/type/localdesktopcustom/$APP_ID" \ -H "Authorization: Bearer $PB_TOKEN" ``` Returns `204` with an empty body. To delete many at once, use [bulk delete](https://pan.dev/prisma-browser/guide/applications#bulk-operations) (`POST /applications/bulk-delete`, up to 2000 IDs, atomic). --- ## Endpoint reference | Method | Path | Purpose | |---|---|---| | `GET` | `/applications/type/localdesktopcustom` | List your desktop applications (filter, paginate) | | `GET` | `/applications/type/localdesktopcustom/{id}` | Read one of your desktop applications | | `POST` | `/applications/type/localdesktopcustom` | Create one desktop application | | `POST` | `/applications/bulk-create/localdesktopcustom` | Create up to 1000 desktop applications | | `PATCH` | `/applications/type/localdesktopcustom/{id}` | Update one desktop application | | `DELETE` | `/applications/type/localdesktopcustom/{id}` | Delete one desktop application | | `GET` | `/applications/type/localdesktopcatalog` | List the desktop catalog (filter, paginate) | | `GET` | `/applications/type/localdesktopcatalog/{id}` | Read one catalog desktop application | All paths are under the `/seb-api/v1` base. --- ## Tips and gotchas **Note** **Executables replace, not merge.** A `PATCH` that includes `executables` replaces the entire list. To add or remove one executable, send the full intended list. **Caution** **Executable names are unique across custom applications and the catalog.** A name can belong to only one desktop application. Reusing a name that another custom application or a catalog entry already claims returns `409`, and the error names the application that holds it. The comparison is case-insensitive, so `Acme.exe` and `acme.exe` collide. Search the catalog before you create, and reference the catalog entry rather than trying to redefine it. **Note** **The catalog is read-only through this API.** There is no create, update, or delete endpoint for `localdesktopcatalog`. A catalog entry still returns `classification` and `tags` scoped to your tenant. Setting them through the API is planned for a future release. Until then, set them on the Applications page in the Prisma Browser admin console in Strata Cloud Manager. --- ## Examples
Create a desktop application with two executables ```bash curl -sS -X POST "$PB_API_BASE/applications/type/localdesktopcustom" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Acme Desktop Client", "type": "localdesktopcustom", "description": "Acme native client and helper", "executables": { "data": [ { "originalFileName": "acme-client.exe" }, { "originalFileName": "acme-helper.exe" } ] } }' ``` Response (`201`): ```json { "id": "0AP01ACMEDESKTOPXXXXXXXXXXXXX" } ```
--- ## Related - Applications: [Applications overview](https://pan.dev/prisma-browser/guide/applications), [Tags and classification](https://pan.dev/prisma-browser/guide/applications#tags-and-classification), [Custom applications](https://pan.dev/prisma-browser/guide/applications-custom), [Application groups](https://pan.dev/prisma-browser/guide/application-groups) - Policy: [Access and data rules](https://pan.dev/prisma-browser/guide/access-and-data-rules) - Concepts: [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish), [Partial publish](https://pan.dev/prisma-browser/guide/draft-and-publish#partial-publish), [Pagination](https://pan.dev/prisma-browser/guide/pagination) --- # Application groups _Source: https://pan.dev/prisma-browser/guide/application-groups | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ An **application group** is a named bundle of [applications](https://pan.dev/prisma-browser/guide/applications). Attach the group to an [access and data rule](https://pan.dev/prisma-browser/guide/access-and-data-rules) once, then edit its membership to change every rule that references it. Use a group when you would otherwise attach dozens of applications to a rule one by one. **On this page:** membership model, managed groups, limits, examples, endpoint reference, tips. --- ## Membership model A group's membership is a flat list of application IDs (any type: custom, private, non-web, catalog, or desktop). Members are referenced by ID; the group stores no per-member settings. | Field | Type | Notes | |---|---|---| | `name` | string | Required. Display name | | `description` | string | Optional | | `applications` | array | Member application IDs. 0 to 2000 entries | On update, membership can be edited two ways: - **Delta** (`add` / `remove`): change specific members and leave the rest intact. Preferred for automation because it does not clobber members another process added between your read and write. Same idea as [delta patch](https://pan.dev/prisma-browser/guide/delta-patch) on rules. - **Replace**: send `applications` as a plain array to overwrite the whole list. --- ## Managed groups Prisma Browser ships two **system-managed application groups** out of the box, one for **Google Workspace** and one for **Microsoft Office 365**. These groups are pre-populated with the corresponding catalog applications for each suite and are maintained by Palo Alto Networks: as each suite adds or changes applications, the group membership is updated automatically. You can reference these groups in a rule exactly like any custom group. You cannot edit their membership. To discover the managed groups in your tenant, list all application groups and look for the Google Workspace and Office 365 entries: ``` GET /seb-api/v1/application-groups ``` ```bash curl -sS -G "$PB_API_BASE/application-groups" \ -H "Authorization: Bearer $PB_TOKEN" \ --data-urlencode "limit=100" ``` Response (`200`). The system-managed suites appear alongside your custom groups: ```json { "data": [ { "id": "0AG01GOOGLEWORKSPACEXXXXXXXXX", "name": "Google Workspace", "description": "Automatically controlled and kept up-to-date collection of Google productivity tools.", "applications": [ { "id": "0AP01GMAILXXXXXXXXXXXXXXXXXXX", "name": "Gmail", "type": "catalog" }, { "id": "0AP01GDRIVEXXXXXXXXXXXXXXXXXX", "name": "Google Drive", "type": "catalog" } // ... maintained by Palo Alto Networks ] } // ... your custom groups ], "pageInfo": { "hasNextPage": true, "cursor": "gaFpvTBBRzAx...", "totalCount": 12 } } ``` The managed groups appear alongside your custom groups in the response. You can also find them in the console under the application groups section. **Note** **Use managed groups to cover a whole suite without maintaining a URL list.** Instead of individually tracking every Google Workspace or Office 365 application, attach the managed group to a rule once. Policy automatically covers new apps added to the suite. --- ## Limits | Limit | Value | |---|---| | Member applications per group | 2000 | | Number of groups | No documented limit | --- ## Examples ### Retrieve #### List and filter Supports `name` search, pagination (`limit` / `cursor`), and `sort` / `order`. ``` GET /seb-api/v1/application-groups ``` ```bash curl -sS -G "$PB_API_BASE/application-groups" \ -H "Authorization: Bearer $PB_TOKEN" \ --data-urlencode "name=saas" \ --data-urlencode "limit=50" ``` Response (`200`): ```json { "data": [ { "id": "0AG01APPROVEDSAASXXXXXXXXXXXX", "name": "Approved SaaS", "description": "Applications allowed for the whole company", "applications": [ { "id": "0AP01ACMEWIKIXXXXXXXXXXXXXXXX", "name": "Acme Wiki", "type": "custom" } // ... more members ] } // ... more groups ], "metadata": { "configurationVersion": { "id": "0CV01EXAMPLEXXXXXXXXXXXXXXXXX", "status": "draft", "number": 0 } }, "pageInfo": { "hasNextPage": false, "cursor": "", "totalCount": 1 } } ``` #### Get one ``` GET /seb-api/v1/application-groups/{id} ``` ```bash curl -sS "$PB_API_BASE/application-groups/$GROUP_ID" \ -H "Authorization: Bearer $PB_TOKEN" ``` Response (`200`): ```json { "id": "0AG01APPROVEDSAASXXXXXXXXXXXX", "name": "Approved SaaS", "description": "Applications allowed for the whole company", "applications": [ { "id": "0AP01ACMEWIKIXXXXXXXXXXXXXXXX", "name": "Acme Wiki", "type": "custom" }, { "id": "0AP01SLACKXXXXXXXXXXXXXXXXXXX", "name": "Slack", "type": "catalog" } // ... more members ] } ``` ### Create ``` POST /seb-api/v1/application-groups ``` ```bash curl -sS -X POST "$PB_API_BASE/application-groups" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Approved SaaS", "description": "Applications allowed for the whole company", "applications": [ "0AP01ACMEWIKIXXXXXXXXXXXXXXXX", "0AP01SLACKXXXXXXXXXXXXXXXXXXX" ] }' ``` Response (`201`): ```json { "id": "0AG01APPROVEDSAASXXXXXXXXXXXX" } ``` Capture the ID for later snippets: ```bash export GROUP_ID='0AG01APPROVEDSAASXXXXXXXXXXXX' ``` #### Key fields | Field | Type | Required | Notes | |---|---|---|---| | `name` | string | yes | Display name | | `description` | string | no | Free text | | `applications` | array | no | Member application IDs; 0 to 2000 entries | ### Update `PATCH` the group. Use the delta form to add or remove specific applications without touching the rest: ``` PATCH /seb-api/v1/application-groups/{id} ``` ```bash curl -sS -X PATCH "$PB_API_BASE/application-groups/$GROUP_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "applications": { "add": [ "0AP01NEWTOOLXXXXXXXXXXXXXXXXX" ], "remove": [ "0AP01OLDTOOLXXXXXXXXXXXXXXXXX" ] } }' ``` Response (`200`): ```json { "id": "0AG01APPROVEDSAASXXXXXXXXXXXX" } ``` To replace the entire membership instead, send `applications` as a plain array. Remember: this edits the **draft**. Publish to make it live (see [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish)). With [partial publish](https://pan.dev/prisma-browser/guide/draft-and-publish#partial-publish) you can publish just this group, which is the basis of the [allow/block list](https://pan.dev/prisma-browser/guide/manage-allow-block-list) automation pattern. ### Delete ``` DELETE /seb-api/v1/application-groups/{id} ``` ```bash curl -sS -X DELETE "$PB_API_BASE/application-groups/$GROUP_ID" \ -H "Authorization: Bearer $PB_TOKEN" ``` Returns `204` with an empty body. --- ## Endpoint reference | Method | Path | Purpose | |---|---|---| | `GET` | `/application-groups` | List application groups (filter, paginate) | | `GET` | `/application-groups/{id}` | Read one application group | | `POST` | `/application-groups` | Create one application group | | `PATCH` | `/application-groups/{id}` | Update one application group (delta or replace) | | `DELETE` | `/application-groups/{id}` | Delete one application group | All paths are under the `/seb-api/v1` base. --- ## Tips and gotchas **Caution** **Attach the group once, edit it forever.** The point of a group is indirection: link it to a rule a single time, then make all ongoing changes on the group. The rule never has to be touched again, which keeps it stable and isolates membership churn. **Note** **2000-member ceiling.** A group holds at most 2000 applications. If you model an allow/block list as "one application per URL", you hit this ceiling at 2000 entries; prefer one application holding many URLs instead (see the [allow/block list](https://pan.dev/prisma-browser/guide/manage-allow-block-list) comparison). **Note** **Delta vs replace.** Prefer add/remove for automation: it does not clobber members another process added between your read and write. --- ## Related - Applications: [Applications overview](https://pan.dev/prisma-browser/guide/applications), [Application catalog](https://pan.dev/prisma-browser/guide/applications-catalog), [Custom applications](https://pan.dev/prisma-browser/guide/applications-custom), [Rules](https://pan.dev/prisma-browser/guide/rules) - Concepts: [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish), [Partial publish](https://pan.dev/prisma-browser/guide/draft-and-publish#partial-publish), [Delta patch](https://pan.dev/prisma-browser/guide/delta-patch) - Use cases: [Govern URLs with an allow/block list](https://pan.dev/prisma-browser/guide/manage-allow-block-list) --- # User groups _Source: https://pan.dev/prisma-browser/guide/user-groups | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ A **user group** scopes a rule to a set of users. Groups come in two flavors: **local** groups you create and manage directly through this API, and **sso** groups synced from your identity provider (IdP). A rule references a group via its scope, and you control who the rule applies to by editing the group's membership. Use a **local** group when you want a rule's audience driven by your own logic (a script, a CMDB, a rollout schedule). **On this page:** membership model, limits, examples, endpoint reference, tips. --- ## Membership model The `provider` field tells you where a group comes from: | `provider` | Origin | Can you edit membership via the API? | |---|---|---| | `local` | Created through this API (or the console) | Yes, you own it | | `sso` | Synced from your IdP using the Cloud Identity Engine | No, membership is governed upstream | **Note** **Use local groups for automation.** When you want a rule's audience to be driven by your own logic (a script, a CMDB, a rollout schedule), create a **local** user group and manage its members through the API. SSO groups mirror the IdP; change them there, not here. --- ## Examples ### Retrieve #### List and filter ``` GET /seb-api/v1/user-groups ``` ```bash curl -sS -G "$PB_API_BASE/user-groups" \ -H "Authorization: Bearer $PB_TOKEN" \ --data-urlencode "limit=50" ``` Response (`200`): ```json { "metadata": { "configurationVersion": { "id": "0CV01EXAMPLEXXXXXXXXXXXXXXXXX", "status": "draft", "number": 0 } }, "pageInfo": { "hasNextPage": false, "cursor": "", "totalCount": 1 }, "data": [ { "id": "0UG01PILOTXXXXXXXXXXXXXXXXXXX", "name": "Pilot - DevTools block", "lastUpdated": "2026-01-15T10:00:00Z", "createdAt": "2026-01-15T10:00:00Z", "provider": "local" } // ... more groups ] } ``` Results are cursor-paginated: the response carries a `pageInfo` object. Pass the next `cursor` value to page through large result sets. | Query param | Notes | |---|---| | `limit` | Number of groups to return. | | `cursor` | Opaque string marking where to resume listing. | #### Get one ``` GET /seb-api/v1/user-groups/{id} ``` ```bash curl -sS "$PB_API_BASE/user-groups/$UG_ID" \ -H "Authorization: Bearer $PB_TOKEN" ``` Response (`200`): ```json { "id": "0UG01PILOTXXXXXXXXXXXXXXXXXXX", "name": "Pilot - DevTools block", "lastUpdated": "2026-01-15T10:00:00Z", "createdAt": "2026-01-15T10:00:00Z", "provider": "local", "metadata": { "configurationVersion": { "id": "0CV01EXAMPLEXXXXXXXXXXXXXXXXX", "status": "draft", "number": 0 } } } ``` ### Create ``` POST /seb-api/v1/user-groups ``` ```bash curl -sS -X POST "$PB_API_BASE/user-groups" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Pilot - DevTools block", "userIds": [ "0UR01ALICEXXXXXXXXXXXXXXXXXXX", "0UR01BOBXXXXXXXXXXXXXXXXXXXXX" ] }' ``` Response (`201`). The `userGroupId` field is a deprecated alias for `id`: ```json { "id": "0UG01PILOTXXXXXXXXXXXXXXXXXXX", "userGroupId": "0UG01PILOTXXXXXXXXXXXXXXXXXXX" } ``` Capture the ID for later snippets: ```bash export UG_ID='0UG01PILOTXXXXXXXXXXXXXXXXXXX' ``` #### Key fields | Field | Type | Required | Notes | |---|---|---|---| | `name` | string | Yes | Display name. | | `userIds` | array | Yes | Member user IDs (`0UR...`). | ### Update Membership edits use **PUT** with a list of per-user actions. This is incremental: you send only the users to add or remove, not the full roster. **Note** **PUT behaves like a PATCH here.** It accepts only incremental per-user membership actions, not a complete replacement of the full roster. You send a list of users to add or remove, and only those changes are applied. ``` PUT /seb-api/v1/user-groups/{id} ``` ```bash # Add one user, remove another curl -sS -X PUT "$PB_API_BASE/user-groups/$UG_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "users": [ { "userId": "0UR01CAROLXXXXXXXXXXXXXXXXXXX", "action": "add" }, { "userId": "0UR01BOBXXXXXXXXXXXXXXXXXXXXX", "action": "remove" } ] }' ``` Response (`200`): ```json { "id": "0UG01PILOTXXXXXXXXXXXXXXXXXXX", "userGroupId": "0UG01PILOTXXXXXXXXXXXXXXXXXXX" } ``` **Caution** **Draft mode for local user groups is rolling out.** On most tenants, changes to a user group (including membership edits) land in the **draft** and go live only when you publish. On a tenant that has not received the rollout yet, the same edits apply directly to the **active** version and there is nothing to publish. To confirm which mode a tenant is in, open the user groups page in Strata Cloud Manager. If you can switch between the Published and Draft versions there, the tenant is draft-gated and your automation must publish after every write. ### Delete ``` DELETE /seb-api/v1/user-groups/{id} ``` ```bash curl -sS -X DELETE "$PB_API_BASE/user-groups/$UG_ID" \ -H "Authorization: Bearer $PB_TOKEN" ``` Response (`200`): ```json { "id": "0UG01PILOTXXXXXXXXXXXXXXXXXXX", "userGroupId": "0UG01PILOTXXXXXXXXXXXXXXXXXXX" } ``` ### Publish with partial publish The typical [gradual rollout](https://pan.dev/prisma-browser/guide/gradual-rollout-with-user-group) loop is: edit group membership, then partial publish only that group so unrelated draft edits are left untouched. Partial publish is available through the API (not yet in the UI). The `id` to publish comes back in the response of the write itself, so no extra lookup is needed. ```bash curl -sS -X POST "$PB_API_BASE/configuration-management/draft/partial-publish" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "entityIds": ["'"$UG_ID"'"], "description": "ServiceNow INC0042311: contractor offboarding" }' ``` Returns `201` with the new configuration version and the IDs that went live. Creating and deleting groups follow the same pattern: perform the write, then publish the `id` it returned. If the group has no pending change, for example because the write added a user who was already a member, the call returns `404`: ```json { "error": { "code": "NOT_FOUND", "message": "No pending change found for one or more of the specified entities.", "timestamp": "2026-08-06T09:15:00Z", "details": [] } } ``` See [Publish a single object (partial publish)](https://pan.dev/prisma-browser/guide/automate-with-partial-publish) for the full error matrix. ### Read back what is live Reads default to the draft, so a group you have edited but not published still comes back from a plain `GET`. To verify what is actually enforced, ask for the active version: ```bash curl -sS "$PB_API_BASE/user-groups/$UG_ID?configurationVersion=active" \ -H "Authorization: Bearer $PB_TOKEN" ``` --- ## Endpoint reference | Method | Path | Purpose | |---|---|---| | `GET` | `/user-groups` | List user groups (paginate) | | `GET` | `/user-groups/{id}` | Read one user group | | `POST` | `/user-groups` | Create a user group | | `PUT` | `/user-groups/{id}` | Add or remove members (incremental) | | `DELETE` | `/user-groups/{id}` | Delete a user group | All paths are under the `/seb-api/v1` base. --- ## Tips and gotchas **Note** **One call changes who a rule covers.** Editing a group's membership changes the audience of every rule that references it, without touching the rules themselves. Publish just that group with partial publish (API only today) so the membership change does not ship unrelated draft edits. See [Publish a single object (partial publish)](https://pan.dev/prisma-browser/guide/automate-with-partial-publish). **Note** **`userGroupId` is deprecated.** Responses still return a `userGroupId` field for backward compatibility; use `id`. **Note** **Reference from a rule's scope.** A rule scopes to a group via `scope.users.userGroups`. Add or remove groups on a rule with `addUserGroups` / `removeUserGroups` (see [Rules](https://pan.dev/prisma-browser/guide/rules) and [Manage users on a rule](https://pan.dev/prisma-browser/guide/manage-users-on-a-rule)). --- ## Related - Building blocks: [Rules](https://pan.dev/prisma-browser/guide/rules) - Concepts: [Partial publish](https://pan.dev/prisma-browser/guide/draft-and-publish#partial-publish) - Use cases: [Add or remove users on a rule](https://pan.dev/prisma-browser/guide/manage-users-on-a-rule), [Roll out a rule gradually](https://pan.dev/prisma-browser/guide/gradual-rollout-with-user-group), [Change a rule's scope](https://pan.dev/prisma-browser/guide/change-a-rule-scope) --- # Device groups _Source: https://pan.dev/prisma-browser/guide/device-groups | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ A **device group** scopes a rule to a set of devices. Attach the group to a rule once, then edit its membership to change which devices the rule covers. Unlike user groups, device-group membership is usually defined by **posture attributes** (disk encryption on, screen lock set, a minimum OS version) rather than an explicit roster, though you can also pin specific serial numbers. **On this page:** membership model, limits, examples, endpoint reference, tips. --- ## Membership model A device group's membership is usually computed from device posture rather than an explicit roster. You define the criteria with `attributes`, and you can also pin specific devices by serial number. | Field | Type | Notes | |---|---|---| | `name` | string | Required. 1 to 255 characters. | | `platform` | enum | Required. One of `Desktop Browser`, `Mobile Browser`, `Browser Extension`, `Chromebook`. | | `attributes` | object | Posture-based membership rules. | `attributes` defines which devices belong to the group by their posture. Available checks include screen lock, endpoint protection, firewall, disk encryption, OS version, and serial number. Each check supports a `negate` flag so you can express "does not have" conditions. **Note** **Posture rules, not a fixed list.** A device joins or leaves the group automatically as its posture changes. This is the main difference from user groups: membership is computed, not enumerated. --- ## Limits | Limit | Value | |---|---| | `name` length | 1 to 255 characters | | Devices per group | No limit | --- ## Examples ### Retrieve #### List and filter Supports filtering by `deviceGroup.name` and `deviceGroup.platform`, created and updated time ranges (`deviceGroup.created_at_gte` / `_lte`, `deviceGroup.updated_at_gte` / `_lte`), `sort` (`deviceGroup.name`, `deviceGroup.platform`, `deviceGroup.created_at`, `deviceGroup.updated_at`) and `order`, and pagination (`limit` / `cursor`). ``` GET /seb-api/v1/device-groups ``` ```bash curl -sS -G "$PB_API_BASE/device-groups" \ -H "Authorization: Bearer $PB_TOKEN" \ --data-urlencode "limit=50" ``` Response (`200`): ```json { "pageInfo": { "hasNextPage": false, "cursor": "", "totalCount": 1 }, "data": [ { "id": "0DG01ENCRYPTEDXXXXXXXXXXXXXXX", "name": "Encrypted desktops", "platform": "Desktop Browser", "createdBy": "0UR01EXAMPLEADMINXXXXXXXXXXXX", "updatedBy": "0UR01EXAMPLEADMINXXXXXXXXXXXX", "createdAt": "2026-01-15T10:00:00Z", "updatedAt": "2026-01-15T10:00:00Z", "attributes": { "diskEncryption": { "enabled": true } }, "devices": [] } ] } ``` #### Get one ``` GET /seb-api/v1/device-groups/{id} ``` ```bash curl -sS "$PB_API_BASE/device-groups/$DG_ID" \ -H "Authorization: Bearer $PB_TOKEN" ``` Response (`200`): ```json { "id": "0DG01ENCRYPTEDXXXXXXXXXXXXXXX", "name": "Encrypted desktops", "platform": "Desktop Browser", "createdBy": "0UR01EXAMPLEADMINXXXXXXXXXXXX", "updatedBy": "0UR01EXAMPLEADMINXXXXXXXXXXXX", "createdAt": "2026-01-15T10:00:00Z", "updatedAt": "2026-01-15T10:00:00Z", "attributes": { "diskEncryption": { "enabled": true } }, "devices": [] } ``` **Note** **The `devices` array is populated only on the active configuration version.** Pass `?configurationVersion=active` to retrieve the list of included devices. On the draft (the default), `devices` is returned empty. ### Create ``` POST /seb-api/v1/device-groups ``` ```bash curl -sS -X POST "$PB_API_BASE/device-groups" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Encrypted desktops", "platform": "Desktop Browser", "attributes": { "diskEncryption": { "enabled": true } } }' ``` Response (`201`): ```json { "id": "0DG01ENCRYPTEDXXXXXXXXXXXXXXX", "deviceGroupId": "0DG01ENCRYPTEDXXXXXXXXXXXXXXX" } ``` Capture the ID for later snippets: ```bash export DG_ID='0DG01ENCRYPTEDXXXXXXXXXXXXXXX' ``` #### Key fields | Field | Type | Required | Notes | |---|---|---|---| | `name` | string | yes | 1 to 255 characters | | `platform` | enum | yes | One of `Desktop Browser`, `Mobile Browser`, `Browser Extension`, `Chromebook` | | `attributes` | object | no | Posture-based membership rules | ### Update `PATCH` the group. To pin or unpin specific devices by serial number, send `serialsToAdd` / `serialsToRemove` (idempotent): ``` PATCH /seb-api/v1/device-groups/{id} ``` ```bash curl -sS -X PATCH "$PB_API_BASE/device-groups/$DG_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "serialsToAdd": [ "C02XK1ABJGH5" ], "serialsToRemove": [ "C02XK0OLDSER" ] }' ``` Response (`200`): ```json { "id": "0DG01ENCRYPTEDXXXXXXXXXXXXXXX", "deviceGroupId": "0DG01ENCRYPTEDXXXXXXXXXXXXXXX" } ``` **Note** **Enable the serial-number attribute first.** `serialsToAdd` / `serialsToRemove` only work when the group has the serial-number attribute enabled (`attributes.serialNumber.enabled: true`). Otherwise the request returns `400` with `serial number attribute is not enabled for this device group`. Enable it at create time, or with a `PATCH` that sends `attributes.serialNumber`: ```bash curl -sS -X PATCH "$PB_API_BASE/device-groups/$DG_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "attributes": { "serialNumber": { "enabled": true } } }' ``` To change posture-based membership, send updated `attributes`: ```bash curl -sS -X PATCH "$PB_API_BASE/device-groups/$DG_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "attributes": { "diskEncryption": { "enabled": true } } }' ``` Response (`200`): ```json { "id": "0DG01ENCRYPTEDXXXXXXXXXXXXXXX", "deviceGroupId": "0DG01ENCRYPTEDXXXXXXXXXXXXXXX" } ``` To replace the **entire** group definition in one call, use `PUT`. Send the complete object: any attribute you omit is disabled, not left unchanged. ``` PUT /seb-api/v1/device-groups/{id} ``` ```bash curl -sS -X PUT "$PB_API_BASE/device-groups/$DG_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Encrypted desktops", "platform": "Desktop Browser", "attributes": { "diskEncryption": { "enabled": true } } }' ``` Response (`200`): ```json { "id": "0DG01ENCRYPTEDXXXXXXXXXXXXXXX", "deviceGroupId": "0DG01ENCRYPTEDXXXXXXXXXXXXXXX" } ``` **Caution** **Prefer `PATCH` for membership edits.** `PUT` replaces the whole group and disables any attribute you leave out, so it is easy to wipe posture rules by accident. Use `PUT` only when you intend to overwrite the entire definition; for add/remove serials or a single posture change, use `PATCH`. Remember: this edits the **draft**. Publish to make it live (see [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish)). With [partial publish](https://pan.dev/prisma-browser/guide/draft-and-publish#partial-publish) you can publish just this object. ### Delete ``` DELETE /seb-api/v1/device-groups/{id} ``` ```bash curl -sS -X DELETE "$PB_API_BASE/device-groups/$DG_ID" \ -H "Authorization: Bearer $PB_TOKEN" ``` Returns `204` with an empty body. --- ## Endpoint reference | Method | Path | Purpose | |---|---|---| | `GET` | `/device-groups` | List device groups (paginate) | | `GET` | `/device-groups/{id}` | Read one device group | | `POST` | `/device-groups` | Create one device group | | `PATCH` | `/device-groups/{id}` | Update one device group (posture attributes or pinned serials) | | `PUT` | `/device-groups/{id}` | Replace one device group in full (omitted attributes are disabled) | | `DELETE` | `/device-groups/{id}` | Delete one device group | All paths are under the `/seb-api/v1` base. --- ## Tips and gotchas **Caution** **No configuration-version envelope.** Device-group reads accept the `configurationVersion` query parameter but do not return the configuration-version metadata block that policy reads do. Do not rely on `metadata.configurationVersion` for device groups. This metadata block will be added soon. **Note** **Idempotent serial edits.** Adding a serial already in the group, or removing one not present, is a safe no-op, which makes serial-pinning automation retry-safe. --- ## Related - Building blocks: [Devices](https://pan.dev/prisma-browser/guide/devices), [Rules](https://pan.dev/prisma-browser/guide/rules) - Concepts: [Direct actions](https://pan.dev/prisma-browser/guide/direct-actions), [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish), [Partial publish](https://pan.dev/prisma-browser/guide/draft-and-publish#partial-publish) --- # Policy _Source: https://pan.dev/prisma-browser/guide/rules | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ Policy is how the Prisma Browser decides what users can do: who a rule applies to (scope) and what happens (an action or a set of controls). Policy is organized into separate **policy types**, each an ordered list of **rules** grouped into **sections** and evaluated top to bottom. This page covers what is common to all policy types. For the fields, structure, and mandatory inputs specific to each type, see its page. ## The policy types | Policy type | Page | What it governs | |---|---|---| | Sign-in | [Sign-in rules](https://pan.dev/prisma-browser/guide/sign-in-rules) | Whether and how users can sign in to the Prisma Browser | | Access and data | [Access and data rules](https://pan.dev/prisma-browser/guide/access-and-data-rules) | Which applications and URLs are reachable, and how data moves (login and data controls, DLP) | | Security | [Security rules](https://pan.dev/prisma-browser/guide/security-rules) | Browser hardening controls (extensions, cookies, certificates, developer tools) | | Customization | [Customization rules](https://pan.dev/prisma-browser/guide/customization-rules) | Branding and look-and-feel controls (logo, theme, shortcuts, start pages) | Each policy type has the same shape of endpoints (substitute the type): ``` GET /seb-api/v1/policy/{type} list rules + sections (paginated) POST /seb-api/v1/policy/{type}/rules create a rule GET /seb-api/v1/policy/{type}/rules/{id} get one rule (full detail) PATCH /seb-api/v1/policy/{type}/rules/{id} update a rule DELETE /seb-api/v1/policy/{type}/rules/{id} delete a rule ``` **Note** **No "list rules" endpoint.** You list a policy's rules by reading the whole policy (`GET /policy/{type}`), which returns rules and sections interleaved in evaluation order. List items are summaries; fetch a rule's full body with `GET /policy/{type}/rules/{id}`. ## Scope: who a rule applies to `scope` selects users, user groups, device groups, IPs, and locations. Every segment supports `isAny` (apply to all) plus add/remove deltas so you can change membership without read-modify-write: ```json { "scope": { "users": { "addUsers": ["0UREXAMPLEUSERXXXXXXXXXXXXX"], "removeUsers": ["0UREXAMPLEOLDUSERXXXXXXXXXX"], "addUserGroups": ["0UGEXAMPLEGROUPXXXXXXXXXXXX"] } } } ``` | Scope segment | Add / remove fields | |---|---| | Users and user groups | `addUsers` / `removeUsers`, `addUserGroups` / `removeUserGroups` | | Device groups | `addDeviceGroups` / `removeDeviceGroups` | | Public / private IPs | `addPublicIps` / `removePublicIps`, `addPrivateIps` / `removePrivateIps` | | Locations | `addLocations` / `removeLocations` | `scope` is optional on create for every policy type, but leaving it off means the rule applies to everyone. Always set a scope unless you intend a tenant-wide rule. See [Change a rule's scope](https://pan.dev/prisma-browser/guide/change-a-rule-scope). ## Sections and positions Rules are organized into **sections** and evaluated in order. Sections are identical across all policy types: create one with `POST /policy/{type}/sections` (only `name` is required), and manage one with `GET / PUT / PATCH / DELETE /policy/{type}/sections/{id}`. Ordering is managed through the positions sub-resource of each type: - `PUT /policy/{type}/positions` submits the full ordered list of rules and sections. Index 0 is evaluated first; rules in a section must be contiguous. - `PATCH /policy/{type}/positions` applies partial moves (`top` / `bottom` / `before` / `after` an anchor, with a target section), applied atomically, without resubmitting the whole order. See [Reorder rules and sections](https://pan.dev/prisma-browser/guide/reorder-rules-and-sections) for both approaches. ## Action vs controls What a rule "does" depends on its type: | Policy type | What it carries | |---|---| | Sign-in | A single `action`: `allow`, `block`, or `prompt` | | Access and data | An `action` plus application scope, login controls, and data controls | | Security, Customization | A `controls` map (many named controls, each with its own settings) | **Note** **Controls are replaced whole, not delta-patched.** In a `PATCH`, omitted controls are preserved and a control you include fully replaces its previous body. Sending `null` removes a control (at least one must remain). There is no per-element delta inside a control. To add one item to a control's list (for example, an allowed extension), read the control, append, and PATCH the whole control back. See [Manage allowed browser extensions](https://pan.dev/prisma-browser/guide/manage-allowed-extensions). ## Prompts and custom messages When an action or control prompts the user, a `userPrompt` object chooses how: | `mode` | What the user sees | |---|---| | `none` | No prompt | | `warnAndAllow` | A warning they can dismiss to continue | | `proceedWithReason` | They must type a justification to continue | | `adminApproval` | They submit a request an administrator must approve | The custom message text shown to the user (typically on a block) is a separate `customDialogText` object with `title` (1-80 chars, required), `description` (up to 360 chars), and an optional `externalLink` (`{ label, url }`). See each type's page for which actions and controls support prompts and custom text. ## Patch semantics summary | Field kind | PATCH behavior | |---|---| | Scalars (`name`, `mode`) | Set to the new value | | Scope lists | Delta via `add…` / `remove…`, or full replace | | Application scope (access and data) | Delta via `addApplicationIds` / `addUrls` / etc., or full replace | | Controls (security, customization) | Whole-control replace; `null` removes; omitted preserved | See [Delta patch](https://pan.dev/prisma-browser/guide/delta-patch) for the full model. Every write here targets the **draft**; publish to make it live (see [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish)). ## Related - Policy types: [Sign-in rules](https://pan.dev/prisma-browser/guide/sign-in-rules), [Access and data rules](https://pan.dev/prisma-browser/guide/access-and-data-rules), [Security rules](https://pan.dev/prisma-browser/guide/security-rules), [Customization rules](https://pan.dev/prisma-browser/guide/customization-rules) - Use cases: [Create and publish a rule](https://pan.dev/prisma-browser/guide/create-and-publish-a-security-rule), [Change a rule's scope](https://pan.dev/prisma-browser/guide/change-a-rule-scope), [Reorder rules and sections](https://pan.dev/prisma-browser/guide/reorder-rules-and-sections) --- # Sign-in rules _Source: https://pan.dev/prisma-browser/guide/sign-in-rules | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ Sign-in rules decide whether and how a user can sign in to the Prisma Browser. They are the first policy a session is evaluated against. Rules are evaluated top to bottom within their sections; the first matching rule wins. See [Policy overview](https://pan.dev/prisma-browser/guide/rules) for concepts shared across all policy types (scope, sections, positions, publishing). Base path: `/seb-api/v1/policy/sign-in`. ## Mandatory fields | Field | Required | Notes | |---|---|---| | `name` | Yes | 1-300 characters | | `mode` | Yes | `active` or `disabled` (case-insensitive) | | `action` | Yes | `allow`, `block`, or `prompt` | | `scope` | No (recommended) | Who the rule applies to. Omitting it applies the rule to everyone. | | `description` | No | Up to 300 characters | | `positioning` | No | Where to place the new rule (section + position) | A sign-in rule's only behavior is its `action`: let the user in (`allow`), keep them out (`block`), or make them confirm before continuing (`prompt`). There are no `controls` on sign-in rules. ## Structure ```json { "name": "Block sign-in from unmanaged devices", "mode": "active", "action": "block", "scope": { "deviceGroups": { "deviceGroups": ["0DGEXAMPLEUNMANAGEDXXXXXXXX"] } } } ``` ## Create a sign-in rule ``` POST /seb-api/v1/policy/sign-in/rules ``` ```bash curl -sS -X POST "$PB_API_BASE/policy/sign-in/rules" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Allow sign-in for employees", "mode": "active", "action": "allow", "scope": { "users": { "userGroups": ["0UGEXAMPLEEMPLOYEESXXXXXXXX"] } } }' ``` ### Response The resolved rule: the fields you sent, every server-side default filled in, and the new `id`. The rule is created in the **draft**. Publish to enforce it (see [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish)). ## Sections Sign-in sections are created with `POST /seb-api/v1/policy/sign-in/sections` (only `name` is required) and ordered with the [positions endpoints](https://pan.dev/prisma-browser/guide/reorder-rules-and-sections). ## Related - [Policy overview](https://pan.dev/prisma-browser/guide/rules) - [Change a rule's scope](https://pan.dev/prisma-browser/guide/change-a-rule-scope) - [Reorder rules and sections](https://pan.dev/prisma-browser/guide/reorder-rules-and-sections) --- # Access and data rules _Source: https://pan.dev/prisma-browser/guide/access-and-data-rules | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ Access and data rules control which applications and URLs a user can reach, and how data moves once they are there: login controls, data controls (clipboard, file transfer, watermarking, and more), and Enterprise Data Loss Prevention (DLP). This is the richest policy type. See [Policy overview](https://pan.dev/prisma-browser/guide/rules) for the shared concepts. Base path: `/seb-api/v1/policy/access-and-data`. ## Mandatory fields | Field | Required | Notes | |---|---|---| | `name` | Yes | 1-300 characters | | `mode` | Yes | `active` or `disabled` | | `applications` | Yes | Which applications/URLs the rule targets. At least one segment must be configured. | | `access` | Yes | The access decision: `{ "action": "allow" \| "block" \| "prompt" \| "redirect" }`, plus optional access controls. | | `tracking` | Yes | Logging and evidence. Must include `logLevel` (`off`, `anonymized`, `on`, `enhanced`). | | `scope` | No (recommended) | Who the rule applies to. Omitting it applies to everyone. | | `loginControls` | No | Login behavior, keyed by type (`formLogin`, `passkeyLogin`, `idpLogin`, `socialLogin`). | | `dataControls` | No | Data protection controls + optional `dlpProfileId`. | ## Structure The four building blocks of every access and data rule: | Block | Purpose | |---|---| | `applications` | Defines the target: `saas`, `private`, `nonWeb`, `localDesktop`, and `applicationGroups` segments, each with an `accessMode` of `none`, `any`, or `specific`. | | `access` | The decision (`allow`/`block`/`prompt`/`redirect`) plus access controls like `label`, `requireAuthentication`, and `openInPrismaBrowser`. | | `loginControls` | How users may authenticate to the target application (form login, passkeys, IdP, social). | | `dataControls` | What users may do with data: clipboard, file upload/download, watermarking, printing, screenshots, and DLP. | A complete rule with all five application segments, the access decision, login controls, data controls, and tracking: ```json { "name": "Finance SaaS - protected access", "mode": "active", "scope": { "users": { "userGroups": ["0UGEXAMPLEFINANCEXXXXXXXXXX"] } }, "applications": { "saas": { "accessMode": "specific", "specific": { "applicationIds": ["0APEXAMPLEWORKDAYXXXXXXXXXX"] } }, "private": { "accessMode": "none" }, "nonWeb": { "accessMode": "none" }, "localDesktop": { "accessMode": "none" }, "applicationGroups": { "accessMode": "none" } }, "access": { "action": "allow" }, "loginControls": { "formLogin": { "action": "allowSpecificDomains", "domains": ["@company.com"], "mfaRequired": true } }, "dataControls": { "clipboard": { "dataIn": { "active": true, "action": "block" }, "dataOut": { "active": true, "action": "block" } }, "fileProtection": { "fileDownload": { "action": "block" } } }, "tracking": { "logLevel": "on", "sessionRecording": false } } ``` `applicationGroups` accepts only `none` or `specific` (there is no `any`). `nonWeb` accepts `none`, `anyAdminDefined`, or `specific`. The remaining three segments (`saas`, `private`, `localDesktop`) accept `none`, `any`, or `specific`. ## Create an access and data rule ``` POST /seb-api/v1/policy/access-and-data/rules ```
Show request and response ```bash curl -sS -X POST "$PB_API_BASE/policy/access-and-data/rules" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Finance SaaS - protected access", "mode": "active", "scope": { "users": { "userGroups": ["0UGEXAMPLEFINANCEXXXXXXXXXX"] } }, "applications": { "saas": { "accessMode": "specific", "specific": { "applicationIds": ["0APEXAMPLEWORKDAYXXXXXXXXXX"] } }, "private": { "accessMode": "none" }, "nonWeb": { "accessMode": "none" } }, "access": { "action": "allow" }, "dataControls": { "clipboard": { "dataIn": {"active": true, "action": "block"}, "dataOut": {"active": true, "action": "block"} } }, "tracking": { "logLevel": "on", "sessionRecording": false } }' ``` Response: The resolved rule: the fields you sent, every server-side default filled in, and the new `id`.
**Note** **Applications is required, and at least one segment must be set.** Configure at least one of `saas`, `private`, `nonWeb`, `localDesktop`, or `applicationGroups`. Use `accessMode: "any"` for "all applications of this kind" or `accessMode: "specific"` with the matching IDs/URLs. See [Applications](https://pan.dev/prisma-browser/guide/applications). ## Login and data controls - Login controls (`loginControls`) gate how users authenticate to the target application. See [Manage login and data controls](https://pan.dev/prisma-browser/guide/manage-login-and-data-controls). - Data controls (`dataControls`) protect data in the session, and can attach an Enterprise DLP profile via `dlpProfileId`. See [Set a DLP profile on a rule](https://pan.dev/prisma-browser/guide/set-a-dlp-profile). **Note** **Controls are replaced whole on PATCH.** A control you include replaces its previous body; omitted controls are preserved; `null` removes a control. There is no per-element delta inside a control. ## Sections Access-and-data sections are created with `POST /seb-api/v1/policy/access-and-data/sections` (only `name` is required) and ordered with the [positions endpoints](https://pan.dev/prisma-browser/guide/reorder-rules-and-sections). ## Examples Expand an example to see the request, response, and notes.
Deny-all baseline A catch-all rule that blocks all web access. Put it last (lowest priority) as a default-deny floor, then add higher-priority allow rules above it. Omitting `scope` applies it to everyone. ```bash curl -sS -X POST "$PB_API_BASE/policy/access-and-data/rules" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Default deny - all web access", "mode": "active", "applications": { "saas": { "accessMode": "any" }, "private": { "accessMode": "any" }, "nonWeb": { "accessMode": "anyAdminDefined" } }, "access": { "action": "block" }, "tracking": { "logLevel": "on", "sessionRecording": false } }' ``` Response (`201`): The resolved rule: the fields you sent, every server-side default filled in, and the new `id`. After creating it, move it to the bottom of the list with the [positions endpoints](https://pan.dev/prisma-browser/guide/reorder-rules-and-sections).
Allow a specific application, but prompt the user first Set `access.action` to `prompt` and configure `access.userPrompt`. Use `warnAndAllow` for a simple warning, or `proceedWithReason` to make the user type a justification. `durationMinutes` (a string enum, for example `"240"`) controls how long their choice is remembered. ```bash curl -sS -X POST "$PB_API_BASE/policy/access-and-data/rules" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Generative AI - allow with justification", "mode": "active", "applications": { "saas": { "accessMode": "specific", "specific": { "applicationIds": ["0APEXAMPLEGENAIXXXXXXXXXXXX"] } } }, "access": { "action": "prompt", "userPrompt": { "mode": "proceedWithReason", "durationMinutes": "240" } }, "tracking": { "logLevel": "on", "sessionRecording": false } }' ``` Response (`201`): The resolved rule: the fields you sent, every server-side default filled in, and the new `id`.
Lock down a sensitive application Allow one high-value application, but clamp the data controls: block clipboard in both directions, block downloads, and attach an Enterprise DLP profile. See [Set a DLP profile on a rule](https://pan.dev/prisma-browser/guide/set-a-dlp-profile) for how to discover the profile ID. ```bash curl -sS -X POST "$PB_API_BASE/policy/access-and-data/rules" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Workday - sensitive access", "mode": "active", "scope": { "users": { "userGroups": ["0UGEXAMPLEFINANCEXXXXXXXXXX"] } }, "applications": { "saas": { "accessMode": "specific", "specific": { "applicationIds": ["0APEXAMPLEWORKDAYXXXXXXXXXX"] } } }, "access": { "action": "allow" }, "dataControls": { "clipboard": { "dataIn": { "active": true, "action": "block" }, "dataOut": { "active": true, "action": "block" } }, "fileProtection": { "fileDownload": { "action": "block" } }, "dlpProfileId": "11995044" }, "tracking": { "logLevel": "enhanced", "sessionRecording": false } }' ``` Response (`201`): The resolved rule: the fields you sent, every server-side default filled in, and the new `id`.
Business Applications group with DLP (two-service flow) Scope a rule to an existing application group (for example your "Business Applications" group), then discover a DLP profile ID from the separate Enterprise DLP API and attach it. **Step 1: list DLP profiles** (Enterprise DLP, a different host) ```bash curl -sS "https://api.dlp.paloaltonetworks.com/v2/api/data-profiles?page=0&size=50" \ -H "Authorization: Bearer $PB_TOKEN" ``` The profile ID is `content[].id`: ```json { "content": [ { "id": "11995044", "name": "PII - strict" } ] } ``` **Step 2: attach it to a rule scoped to the application group** ```bash curl -sS -X POST "$PB_API_BASE/policy/access-and-data/rules" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Business apps - DLP", "mode": "active", "applications": { "applicationGroups": { "accessMode": "specific", "specific": { "applicationGroupIds": ["0AGEXAMPLEBUSINESSAPPSXXXXX"] } } }, "access": { "action": "allow" }, "dataControls": { "dlpProfileId": "11995044", "fileProtection": { "fileUpload": { "action": "block" } } }, "tracking": { "logLevel": "on", "sessionRecording": false } }' ``` Response (`201`): The resolved rule: the fields you sent, every server-side default filled in, and the new `id`. **Note:** `dlpProfileId` cannot stand alone. It must accompany at least one inline data control (here, `fileProtection`). See [Set a DLP profile on a rule](https://pan.dev/prisma-browser/guide/set-a-dlp-profile).
File protection (block uploads and downloads) `fileProtection` configures the download and upload directions independently. Each direction has its own `action`. ```bash curl -sS -X PATCH "$PB_API_BASE/policy/access-and-data/rules/$RULE_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "dataControls": { "fileProtection": { "fileDownload": { "action": "block" }, "fileUpload": { "action": "block" } } } }' ``` Response (`200`): ```json { "id": "0RLEXAMPLEACCESSRULEXXXXXXX" } ``` **Note:** to keep downloaded files usable only inside the Prisma Browser, set `fileDownload.action` to `useInPrismaBrowserOnly`; pair it with `fileUpload.action: "blockOnlyProtected"` so protected files cannot leave. (`allowProtectedBetweenRuleApps` is a legacy, read-only value: it may appear on pre-existing rules but is rejected on create/update.)
Clipboard only within the rule's applications The clipboard control governs data crossing the boundary of the rule's applications. Block both directions so users can copy and paste **within** the protected applications, but cannot carry clipboard data in from, or out to, anything else. ```bash curl -sS -X PATCH "$PB_API_BASE/policy/access-and-data/rules/$RULE_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "dataControls": { "clipboard": { "dataIn": { "active": true, "action": "block" }, "dataOut": { "active": true, "action": "block" } } } }' ``` Response (`200`): ```json { "id": "0RLEXAMPLEACCESSRULEXXXXXXX" } ``` Set `excludeOmnibox: true` on a direction to leave the address bar out of the restriction.
## Related - [Policy overview](https://pan.dev/prisma-browser/guide/rules) - [Manage login and data controls](https://pan.dev/prisma-browser/guide/manage-login-and-data-controls), [Set a DLP profile on a rule](https://pan.dev/prisma-browser/guide/set-a-dlp-profile) - [Govern URLs with an allow/block list](https://pan.dev/prisma-browser/guide/manage-allow-block-list), [Applications](https://pan.dev/prisma-browser/guide/applications) --- # Security rules _Source: https://pan.dev/prisma-browser/guide/security-rules | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ Security rules harden the Prisma Browser: they govern extensions, developer tools, cookies, network APIs, certificate trust, and dozens of other protections. A security rule carries a `controls` map, where each key is a named control and its value is that control's settings. See [Policy overview](https://pan.dev/prisma-browser/guide/rules) for the concepts shared by all policy types. Base path: `/seb-api/v1/policy/security`. ## Mandatory fields | Field | Required | Notes | |---|---|---| | `name` | Yes | 1 to 300 characters | | `mode` | Yes | `active` or `disabled` | | `controls` | Yes | A map of control name to settings. Add at least one control to enforce policy. | | `scope` | No (recommended) | Who the rule applies to. Omitting it applies the rule to everyone. | | `description` | No | Up to 300 characters | ## How controls work Each control is addressed by name and **replaced as a whole**. Most controls take a single `action` (typically `allow`/`block` or `enable`/`disable`); some carry richer settings (for example the extensions control has a `mode` and an `extensions[]` list). ```json { "name": "Browser hardening - all users", "mode": "active", "scope": { "users": { "isAny": true } }, "controls": { "developerToolsForExtensions": { "action": "block" }, "allowedOrBlockedExtensions": { "mode": "blockByListOrRisk", "riskLevel": "high" } } } ``` ## Create a security rule ``` POST /seb-api/v1/policy/security/rules ```
Show request and response ```bash curl -sS -X POST "$PB_API_BASE/policy/security/rules" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Block developer tools on extensions", "mode": "active", "scope": { "users": { "isAny": true } }, "controls": { "developerToolsForExtensions": { "action": "block" } } }' ``` Response: The resolved rule: the fields you sent, every server-side default filled in, and the new `id`.
## Delete a security rule ```bash curl -sS -X DELETE "$PB_API_BASE/policy/security/rules/$RULE_ID" \ -H "Authorization: Bearer $PB_TOKEN" ``` Returns `204` with an empty body. ## Sections Security sections are created with `POST /seb-api/v1/policy/security/sections` (only `name` is required) and ordered with the [positions endpoints](https://pan.dev/prisma-browser/guide/reorder-rules-and-sections). **Note** Every write edits the **draft**. Publish to make it live (see [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish)). ## Examples Expand an example to see the request, response, and notes.
Baseline browser hardening for everyone A single rule that turns on a handful of common protections. Omitting `scope` (or using `isAny`) applies it to all users. Each control is a spec-defined key: `developerToolsForExtensions` and `basicAuthenticationOverHttp` take `allow`/`block`; `cookiesProtection` takes `enable`/`disable`. ```bash curl -sS -X POST "$PB_API_BASE/policy/security/rules" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Browser hardening - all users", "mode": "active", "scope": { "users": { "isAny": true } }, "controls": { "developerToolsForExtensions": { "action": "block" }, "cookiesProtection": { "action": "enable" }, "pagesWithSslErrors": { "action": "block" }, "basicAuthenticationOverHttp": { "action": "block" } } }' ``` Response (`201`): The resolved rule: the fields you sent, every server-side default filled in, and the new `id`.
Allow only specific extensions (allow-list) Use the `allowedOrBlockedExtensions` control with `mode: "allowByList"` and one entry per Chrome extension ID. Each ID is 32 lowercase letters `a` through `p`. ```bash curl -sS -X POST "$PB_API_BASE/policy/security/rules" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Approved extensions only", "mode": "active", "scope": { "users": { "isAny": true } }, "controls": { "allowedOrBlockedExtensions": { "mode": "allowByList", "extensions": [ { "id": "cjpalhdlnbpafiamejdnhcphjbkeiagm" } ] } } }' ``` Response (`201`): The resolved rule: the fields you sent, every server-side default filled in, and the new `id`. **Note:** to add or remove one extension later, GET the rule, edit the `extensions` array, and PATCH the whole `allowedOrBlockedExtensions` control back (controls have no per-item delta). See [Manage allowed browser extensions](https://pan.dev/prisma-browser/guide/manage-allowed-extensions).
Block risky extensions by risk score Block extensions at or above a risk threshold with `mode: "blockByListOrRisk"` and a `riskLevel` of `malicious`, `medium`, or `high`. The other valid modes are `allowAll` and `blockAll`. ```bash curl -sS -X POST "$PB_API_BASE/policy/security/rules" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Block high-risk extensions", "mode": "active", "controls": { "allowedOrBlockedExtensions": { "mode": "blockByListOrRisk", "riskLevel": "high" } } }' ``` Response (`201`): The resolved rule: the fields you sent, every server-side default filled in, and the new `id`.
Switch a control on an existing rule (PATCH replaces the whole control) PATCH replaces the named control whole. Here the extensions control is switched to an allow-list; controls you do not mention are left untouched. Because there is no per-item delta, send the complete `extensions` array you want. ```bash curl -sS -X PATCH "$PB_API_BASE/policy/security/rules/$RULE_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "controls": { "allowedOrBlockedExtensions": { "mode": "allowByList", "extensions": [ { "id": "hdokiejnpimakedhajhdlcegeplioahd" }, { "id": "cjpalhdlnbpafiamejdnhcphjbkeiagm" } ] } } }' ``` Response (`200`): ```json { "id": "0RLEXAMPLESECURITYRULEXXXXX" } ```
## Related - [Policy overview](https://pan.dev/prisma-browser/guide/rules), [Customization rules](https://pan.dev/prisma-browser/guide/customization-rules) - [Create and publish a rule](https://pan.dev/prisma-browser/guide/create-and-publish-a-security-rule), [Manage allowed browser extensions](https://pan.dev/prisma-browser/guide/manage-allowed-extensions) --- # Customization rules _Source: https://pan.dev/prisma-browser/guide/customization-rules | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ Customization rules change the look, feel, and end-user experience of the Prisma Browser: the logo, theme color, home page, new-tab page, pinned shortcuts, and more. A customization rule has the **same shape as a [security rule](https://pan.dev/prisma-browser/guide/security-rules)**: a `controls` map keyed by control name. Only the path (`/policy/customization`) and the available control names differ. See [Policy overview](https://pan.dev/prisma-browser/guide/rules) for the shared concepts. Base path: `/seb-api/v1/policy/customization`. ## Mandatory fields | Field | Required | Notes | |---|---|---| | `name` | Yes | 1 to 300 characters | | `mode` | Yes | `active` or `disabled` | | `controls` | Yes | A map of control name to settings. Add at least one control to enforce policy. | | `scope` | No (recommended) | Who the rule applies to. Omitting it applies the rule to everyone. | | `description` | No | Up to 300 characters | ## Branding assets come from the Assets API Controls that display a file (logo, browser icon, background image, PAC file) do not take the binary directly. You first upload the file with the [Assets API](https://pan.dev/prisma-browser/guide/assets), which returns an asset ID, then set that ID on the control. For example, `companyLogo.companyLogoId` is the value returned by `POST /assets/company-logo`. ```json { "name": "Corporate branding", "mode": "active", "scope": { "users": { "isAny": true } }, "controls": { "companyLogo": { "mode": "setCompanyLogo", "companyLogoId": "0ASEXAMPLECOMPANYLOGOXXXXXX" }, "themeColor": { "mode": "custom", "color": "#485BFF" } } } ``` ## Create a customization rule ``` POST /seb-api/v1/policy/customization/rules ```
Show request and response ```bash curl -sS -X POST "$PB_API_BASE/policy/customization/rules" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Corporate branding", "mode": "active", "scope": { "users": { "isAny": true } }, "controls": { "themeColor": { "mode": "custom", "color": "#485BFF" } } }' ``` Response: The resolved rule: the fields you sent, every server-side default filled in, and the new `id`.
## Delete a customization rule ```bash curl -sS -X DELETE "$PB_API_BASE/policy/customization/rules/$RULE_ID" \ -H "Authorization: Bearer $PB_TOKEN" ``` Returns `204` with an empty body. ## Sections Customization sections are created with `POST /seb-api/v1/policy/customization/sections` (only `name` is required) and ordered with the [positions endpoints](https://pan.dev/prisma-browser/guide/reorder-rules-and-sections). **Note** Every write edits the **draft**. Publish to make it live (see [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish)). ## Examples Expand an example to see the request, response, and notes.
Upload a logo and apply it (Assets API + companyLogo control) The flagship two-API flow: upload the PNG to get a `companyLogoId`, then set it on the `companyLogo` control. See [Assets](https://pan.dev/prisma-browser/guide/assets) for all four asset types and their limits. **Step 1: upload the logo** (multipart, the only file upload in the API) ```bash curl -sS -X POST "$PB_API_BASE/assets/company-logo" \ -H "Authorization: Bearer $PB_TOKEN" \ -F "file=@./company-logo.png" ``` Response (`201 Created`): ```json { "companyLogoId": "0ASEXAMPLECOMPANYLOGOXXXXXX" } ``` **Step 2: set the returned ID on a customization rule** ```bash curl -sS -X POST "$PB_API_BASE/policy/customization/rules" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Company logo", "mode": "active", "controls": { "companyLogo": { "mode": "setCompanyLogo", "companyLogoId": "0ASEXAMPLECOMPANYLOGOXXXXXX" } } }' ``` Response (`201`): The resolved rule: the fields you sent, every server-side default filled in, and the new `id`. **Note:** to remove the custom logo later, set `companyLogo` to `{ "mode": "noCompanyLogo" }`. The company logo accepts only `.png` via the API. See [Assets](https://pan.dev/prisma-browser/guide/assets).
Set a fixed theme color Force one theme color for everyone with `mode: "custom"` and a `#RRGGBB` hex value. ```bash curl -sS -X POST "$PB_API_BASE/policy/customization/rules" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Brand theme color", "mode": "active", "controls": { "themeColor": { "mode": "custom", "color": "#485BFF" } } }' ``` Response (`201`): The resolved rule: the fields you sent, every server-side default filled in, and the new `id`.
Pin managed shortcuts to the new-tab page The `managedShortcuts` control sets the applications that appear on the new-tab page and in bookmarks. Use `mode: "setShortcuts"` and provide a `shortcuts` list. ```bash curl -sS -X POST "$PB_API_BASE/policy/customization/rules" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Company shortcuts", "mode": "active", "scope": { "users": { "isAny": true } }, "controls": { "managedShortcuts": { "mode": "setShortcuts", "shortcuts": [ { "name": "Workday", "url": "https://workday.example.com", "pinned": true }, { "name": "Wiki", "url": "https://wiki.example.com", "pinned": true }, { "name": "Support", "url": "https://support.example.com" } ] } } }' ``` Response (`201`): The resolved rule: the fields you sent, every server-side default filled in, and the new `id`.
Set the home page and new-tab page Point the Home button at a fixed URL (`homePage.mode: "customUrl"`) and open new tabs to a custom page (`newTabPage.pageType: "customUrl"`). ```bash curl -sS -X POST "$PB_API_BASE/policy/customization/rules" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Company start pages", "mode": "active", "controls": { "homePage": { "mode": "customUrl", "customUrl": "https://intranet.example.com", "showHomePageButton": true }, "newTabPage": { "pageType": "customUrl", "customUrl": "https://intranet.example.com/start" } } }' ``` Response (`201`): The resolved rule: the fields you sent, every server-side default filled in, and the new `id`.
## Related - [Policy overview](https://pan.dev/prisma-browser/guide/rules), [Security rules](https://pan.dev/prisma-browser/guide/security-rules) - Building blocks: [Assets](https://pan.dev/prisma-browser/guide/assets) --- # Assets _Source: https://pan.dev/prisma-browser/guide/assets | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ **Assets** are the binary files that back the Prisma Browser branding and network configuration: a company logo, a browser icon, a new-tab background image, and a PAC (proxy auto-configuration) file. You upload a file once, the API stores it and returns a short **asset ID**, and you then reference that ID from a [customization rule](https://pan.dev/prisma-browser/guide/customization-rules) control. The flow is always two steps: **upload the file, then reference the returned ID from a control**. Assets are the only multipart (file) uploads in the API. Every other call is JSON. **On this page:** asset types, upload-then-reference flow, limits and validation, endpoint reference, tips, examples. --- ## Asset types Each asset has its own upload endpoint and returns one ID field. That ID is what you set on the matching customization control. | Asset | Upload endpoint | Max size | Allowed files | Returns | Referenced by control | |---|---|---|---|---|---| | Company logo | `POST /assets/company-logo` | 1 MB | `.png` | `companyLogoId` | `companyLogo` (`companyLogoId`) | | Browser icon | `POST /assets/browser-icon` | 3 MB | `.png` | `browserIconId` | `browserIcon` (`browserIconId`) | | Background image | `POST /assets/background-image` | 3 MB | `.png`, `.svg`, `.jpg`, `.jpeg` | `backgroundImageId` | `backgroundImage` (`backgroundImageId`) | | PAC file | `POST /assets/pac-file` | 2 MB | `.pac`, `.dat`, `.js`, or no extension | `pacFileId` | `proxyAutoConfigurationPacFile` (`pacFileId`) | --- ## How the upload-then-reference flow works ```mermaid flowchart LR a["POST /assets/company-logo (file)"] --> b["201 -> companyLogoId"] b --> c["PATCH customization rule: companyLogo.companyLogoId"] c --> d["Publish"] ``` 1. **Upload** the file with a `multipart/form-data` request whose single `file` part is the binary. 2. **Capture** the returned ID (for example `companyLogoId`). 3. **Reference** it from a customization rule control (for example the `companyLogo` control). 4. **Publish** to make it live. Like all writes, the upload and the control change land in the [draft](https://pan.dev/prisma-browser/guide/draft-and-publish) first. The upload response is intentionally minimal: only the ID you reference. ```json { "companyLogoId": "0ASEXAMPLECOMPANYLOGOXXXXXX" } ``` --- ## Limits and validation | Asset | Max size | Allowed files | Notes | |---|---|---|---| | Company logo | 1 MB | `.png` | SVG must be uploaded from Strata Cloud Manager | | Browser icon | 3 MB | `.png` | | | Background image | 3 MB | `.png`, `.svg`, `.jpg`, `.jpeg` | SVG is sanitized | | PAC file | 2 MB | `.pac`, `.dat`, `.js`, extensionless | | --- ## Endpoint reference | Method | Path | Purpose | |---|---|---| | `POST` | `/assets/company-logo` | Upload a company logo, returns `companyLogoId` | | `POST` | `/assets/browser-icon` | Upload a browser icon, returns `browserIconId` | | `POST` | `/assets/background-image` | Upload a background image, returns `backgroundImageId` | | `POST` | `/assets/pac-file` | Upload a PAC file, returns `pacFileId` | All paths are under the `/seb-api/v1` base. --- ## Tips and gotchas **Caution** **Validation errors.** A wrong extension or a failed validation returns `400`. A file over the size limit returns `413`. **Note** **An upload edits the draft.** Publish to make it live (see [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish)). --- ## Examples Expand an example to see the request, response, and the control that references the returned ID. **Note** Asset uploads are the exception to the usual rule of not printing a create response: the response is a single ID field, shown in each example because you need the exact field name to reference the asset.
Upload a company logo and use it in a rule Upload the PNG, then set the returned ID on the `companyLogo` control of a customization rule. See [Customization rules](https://pan.dev/prisma-browser/guide/customization-rules) for the full control catalog. **Step 1: upload the file** ```bash curl -sS -X POST "$PB_API_BASE/assets/company-logo" \ -H "Authorization: Bearer $PB_TOKEN" \ -F "file=@./company-logo.png" ``` Response (`201 Created`): ```json { "companyLogoId": "0ASEXAMPLECOMPANYLOGOXXXXXX" } ``` **Step 2: reference the ID on a customization rule** ```bash curl -sS -X PATCH "$PB_API_BASE/policy/customization/rules/$RULE_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "controls": { "companyLogo": { "mode": "setCompanyLogo", "companyLogoId": "0ASEXAMPLECOMPANYLOGOXXXXXX" } } }' ``` Response (`200`): ```json { "id": "0RL01CUSTOMIZATIONXXXXXXXXXXX" } ```
Upload a browser icon ```bash curl -sS -X POST "$PB_API_BASE/assets/browser-icon" \ -H "Authorization: Bearer $PB_TOKEN" \ -F "file=@./browser-icon.png" ``` Response (`201 Created`): ```json { "browserIconId": "0ASEXAMPLEBROWSERICONXXXXXX" } ``` Reference it with the `browserIcon` control (`mode: "setBrowserIcon"`, `browserIconId`).
Upload a new-tab background image Background images accept `.png`, `.svg`, `.jpg`, and `.jpeg` (SVG is sanitized on upload). ```bash curl -sS -X POST "$PB_API_BASE/assets/background-image" \ -H "Authorization: Bearer $PB_TOKEN" \ -F "file=@./background.jpg" ``` Response (`201 Created`): ```json { "backgroundImageId": "0ASEXAMPLEBACKGROUNDXXXXXXX" } ``` Reference it with the `backgroundImage` control (`mode: "setImage"`, `backgroundImageId`).
Upload a PAC file ```bash curl -sS -X POST "$PB_API_BASE/assets/pac-file" \ -H "Authorization: Bearer $PB_TOKEN" \ -F "file=@./proxy.pac" ``` Response (`201 Created`): ```json { "pacFileId": "0ASEXAMPLEPACFILEXXXXXXXXXX" } ``` Reference it with the `proxyAutoConfigurationPacFile` control (`pacFileId`).
--- ## Related - Policy: [Customization rules](https://pan.dev/prisma-browser/guide/customization-rules) (the controls that reference these assets) - Concepts: [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish) --- # User requests _Source: https://pan.dev/prisma-browser/guide/user-requests | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ A **user request** is created when a user matches a rule whose prompt is set to **administrator approval**. **On this page:** how requests are generated, managing requests through the API, request lifecycle, request types, filtering and sorting, approval timeframes, related. Instead of being allowed or blocked outright, the user submits a request, and an administrator decides. You configure the trigger on a rule, and you manage the resulting requests through the user-requests API. --- ## How requests are generated On a [rule](https://pan.dev/prisma-browser/guide/rules) (or a control that supports prompting), set the prompt mode to `adminApproval`: ```json { "userPrompt": { "mode": "adminApproval", "durationMinutes": 540 } } ``` | `mode` | Behavior | |---|---| | `warnAndAllow` | User dismisses a warning and continues. No request created. | | `proceedWithReason` | User types a justification and continues. No administrator step. | | `adminApproval` | User submits a request an administrator must approve. A **request** is created. | `durationMinutes` controls how long an approval is remembered before the user is challenged again (default 540, that is 9 hours). See [Rules: prompts and custom messages](https://pan.dev/prisma-browser/guide/rules#prompts-and-custom-messages). --- ## Managing requests through the API The user-requests API lets you list pending requests, inspect one, approve or decline it, and revoke an approval that is no longer wanted. These are **direct operations**: they take effect immediately and do not go through draft and publish. | Operation | Endpoint | |---|---| | List requests | `GET /seb-api/v1/user-requests` | | Get one request | `GET /seb-api/v1/user-requests/{id}` | | Approve or decline | `POST /seb-api/v1/user-requests/{id}/action` | | Revoke an approval | `POST /seb-api/v1/user-requests/{id}/revoke` | ### Request lifecycle A request moves through these statuses: | Status | Meaning | |---|---| | `Pending` | Submitted by the user, awaiting an administrator decision. | | `Approved` | An administrator approved it; the user is allowed for the bypass timeframe. | | `Declined` | An administrator declined it; the user stays blocked. | | `Revoked` | A previously approved request was withdrawn. | ### Request types | `request.type` | Triggered by | |---|---| | `WebAccess` | A web access (URL) that a rule blocked or prompted on. | | `AppLogin` | A sign-in to an application that a rule blocked or prompted on. | ### Filtering and sorting `GET /user-requests` supports filtering by `request.type`, `request.user_id`, `request.device_id`, `request.rule_id`, `request.url`, `request.responded_by`, and `request.status`, plus `sort` (for example `request.created_at`, `request.status`) and `order`. It is cursor-paginated (`limit` + `cursor`); see [Pagination](https://pan.dev/prisma-browser/guide/pagination). ### Approval timeframes When you approve a request, `adminBypassTimeframe` sets how long the approval holds before the user is challenged again. Allowed values: `Once`, `10m`, `1h`, `4h`, `9h`, `12h`, `24h`, `3d`, `7d`, `14d`, `30d`, `60d`, `90d`. --- ## Related - Use case: [Handle user access requests](https://pan.dev/prisma-browser/guide/handle-user-access-requests) (poll, approve/decline, revoke) - Building blocks: [Rules](https://pan.dev/prisma-browser/guide/rules) (prompts and custom messages) - Concepts: [Direct actions](https://pan.dev/prisma-browser/guide/direct-actions) for immediate user/device remediation --- # Create and publish a rule _Source: https://pan.dev/prisma-browser/guide/create-and-publish-a-security-rule | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ A complete, copy-pasteable walkthrough: authenticate, create a rule on the draft, verify it, then publish it so it goes live. The example creates a security rule, but the same create -> verify -> publish flow applies to every policy type (see [Policy overview](https://pan.dev/prisma-browser/guide/rules)). By the end you will have an enforced rule that blocks developer tools on browser extensions for all users. **Prerequisites:** a Super User service account (writes require it) and the environment variables from [Getting started](https://pan.dev/prisma-browser/guide/getting-started): `PB_API_BASE`, `PB_TOKEN`. New here? Read [Authentication](https://pan.dev/prisma-browser/guide/authentication) and [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish) first. ```mermaid flowchart LR create["POST rule (draft)"] --> verify["GET rule (draft)"] verify --> publish["POST publish"] publish --> live["Rule is active"] ``` --- ## 1. Create the rule (on the draft) A security rule needs a `name` and a `mode` (`active` or `disabled`); add a `controls` object with at least one control to make it enforce something. `scope` (who it applies to) is optional, but you should always set it: a rule with no scope applies to everyone. Here we deliberately apply to all users (`scope.users.isAny = true`) and block DevTools on extensions. The create endpoint: ``` POST /seb-api/v1/policy/security/rules ``` ```bash curl -sS -X POST "$PB_API_BASE/policy/security/rules" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Block developer tools on extensions", "description": "Prevent inspection of browser extensions via DevTools", "mode": "active", "scope": { "users": { "isAny": true } }, "controls": { "developerToolsForExtensions": { "action": "block" } } }' ``` ### Response `201 Created` with the resolved rule: the scope, mode and controls you sent, plus every server-side default filled in, and the new rule's `id`. References come back as IDs rather than enriched with names. **Note** The rule now exists in the **draft**. It is not enforced yet. Capture the ID for the next steps: ```bash export RULE_ID='0RLEXAMPLERULEXXXXXXXXXXXXXX' ``` --- ## 2. Verify it (read the draft) Fetch the full rule back from the draft to confirm it looks right. (Reads default to `draft`, so no version parameter is needed.) ```bash curl -sS "$PB_API_BASE/policy/security/rules/$RULE_ID" \ -H "Authorization: Bearer $PB_TOKEN" ``` You should see your `name`, `mode: "active"`, and the `developerToolsForExtensions` control set to `block`. The `metadata.configurationVersion.status` will be `draft`. --- ## 3. Publish (make it live) Promote the draft to a new active version. Add a description so the version history is readable. ```bash curl -sS -X POST "$PB_API_BASE/configuration-management/draft/publish" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{"description": "Add: block DevTools on extensions"}' ``` `201` means a new active version was created and your rule is now enforced. If you get `409`, the draft had no changes (did step 1 succeed?). See [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish#full-publish-responses) for all publish responses. --- ## 4. Confirm it is live Read the rule from the **active** version: ```bash curl -sS "$PB_API_BASE/policy/security/rules/$RULE_ID?configurationVersion=active" \ -H "Authorization: Bearer $PB_TOKEN" ``` `metadata.configurationVersion.status` should now be `active`. --- ## Full script (Python) ```python import os, requests base = os.environ["PB_API_BASE"] headers = {"Authorization": f"Bearer {os.environ['PB_TOKEN']}"} # 1. Create on the draft body = { "name": "Block developer tools on extensions", "description": "Prevent inspection of browser extensions via DevTools", "mode": "active", "scope": {"users": {"isAny": True}}, "controls": {"developerToolsForExtensions": {"action": "block"}}, } rule_id = requests.post(f"{base}/policy/security/rules", headers=headers, json=body, timeout=30).json()["id"] # 2. Verify rule = requests.get(f"{base}/policy/security/rules/{rule_id}", headers=headers, timeout=30).json() assert rule["controls"]["developerToolsForExtensions"]["action"] == "block" # 3. Publish pub = requests.post( f"{base}/configuration-management/draft/publish", headers=headers, json={"description": "Add: block DevTools on extensions"}, timeout=30, ) print("publish:", pub.status_code, "rule:", rule_id) ``` --- ## Variations - **Stage without enforcing:** create with `"mode": "disabled"`, or do not publish yet. - **Scope to specific groups:** replace `scope.users.isAny` with `{"users": {"userGroups": ["0UG..."]}}`. - **Other controls:** the `controls` object accepts many keys (for example `cast`, `thirdPartyCookies`, `printPreview`), each typically `{"action": "allow" | "block"}` or `{"action": "enable" | "disable"}`. - **Update later:** use `PATCH /policy/security/rules/{id}` with only the fields to change. ## Cleanup ```bash curl -sS -X DELETE "$PB_API_BASE/policy/security/rules/$RULE_ID" \ -H "Authorization: Bearer $PB_TOKEN" # then publish again to make the deletion live ``` The `DELETE` returns `204` with an empty body. --- # Govern URLs with an allow/block list _Source: https://pan.dev/prisma-browser/guide/manage-allow-block-list | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ Keep a list of URLs that an [access and data rule](https://pan.dev/prisma-browser/guide/access-and-data-rules) allows or blocks, and let automation add or remove entries as your source of truth changes. There are three ways to model this; the right one depends on how the list grows and how much you want to touch the rule. **Use this when:** - You maintain an allow list or block list of URLs that changes over time. - You want a script (CMDB, risk feed, ticket workflow) to own the membership. **Prerequisites:** a Super User service account and the environment variables from [Getting started](https://pan.dev/prisma-browser/guide/getting-started). Read [Applications](https://pan.dev/prisma-browser/guide/applications), [Application groups](https://pan.dev/prisma-browser/guide/application-groups), and [Delta patch](https://pan.dev/prisma-browser/guide/delta-patch) first. --- ## Approaches | Approach | How it works | Best when | Tradeoffs | |---|---|---|---| | **A. One custom application holds all URLs** | Create one [custom application](https://pan.dev/prisma-browser/guide/applications), link it to the rule once, then add/remove URLs on the application | The list is a flat set of URLs you manage as a whole | Up to 100 URLs per application; individual URLs are not separately named | | **B. One application per URL, grouped** | Create one custom application per URL (named), put them in an [application group](https://pan.dev/prisma-browser/guide/application-groups), link the group to the rule once | You want each URL individually named/identifiable (reporting, audit) | More objects to manage; group caps at 2000 applications | | **C. URLs directly on the rule** | Add/remove URLs in the rule's application scope via delta patch | A small, rule-specific list you do not reuse elsewhere | The rule object churns; not reusable across rules | **Recommendation:** use **A** for a simple managed list, **B** when each URL must be individually named or reused across rules, and **C** only for small one-off lists. A and B share a key advantage: you link the object to the rule **once** and never touch the rule again, all churn happens on the application or group. --- ## Approach A: one custom application holds all URLs ### 1. Create the application and link it to the rule (once) ```bash curl -sS -X POST "$PB_API_BASE/applications/type/custom" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "type": "custom", "name": "Blocklist - marketing trackers", "urls": [ { "url": "tracker.example.com" } ] }' ``` ```json { "id": "0AP01BLOCKLISTXXXXXXXXXXXXXXX" } ``` ```bash export APP_ID='0AP01BLOCKLISTXXXXXXXXXXXXXXX' export RULE_ID='0RLEXAMPLERULEXXXXXXXXXXXXXX' # an access-and-data rule ``` Attach the application to the rule's SaaS scope once: ```bash curl -sS -X PATCH "$PB_API_BASE/policy/access-and-data/rules/$RULE_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "applications": { "saas": { "accessMode": "specific", "specific": { "addApplicationIds": ["'"$APP_ID"'"] } } } }' ``` Response (`200`): ```json { "id": "0RLEXAMPLERULEXXXXXXXXXXXXXX" } ``` ### 2. From now on, edit the URLs on the application ```bash curl -sS -X PATCH "$PB_API_BASE/applications/type/custom/$APP_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "type": "custom", "urls": { "add": [ { "url": "newtracker.example.com" } ], "remove": [ { "url": "tracker.example.com" } ] } }' ``` Response (`200`): ```json { "id": "0AP01BLOCKLISTXXXXXXXXXXXXXXX" } ``` The rule never changes again. Publish (or [partial-publish](https://pan.dev/prisma-browser/guide/draft-and-publish#partial-publish) only the application). **Note** One application holds up to 100 URLs. If your list can exceed that, split across multiple applications or use approach B. --- ## Approach B: one application per URL, grouped ### 1. Create one named application per URL ```bash curl -sS -X POST "$PB_API_BASE/applications/type/custom" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "type": "custom", "name": "Allow - Figma", "urls": [ { "url": "figma.com" } ] }' ``` Response (`201`): ```json { "id": "0AP01FIGMAXXXXXXXXXXXXXXXXXXX" } ``` Each application gets its own ID and a human-readable name, so it shows up identifiably in reporting. ### 2. Group them and link the group to the rule (once) ```bash curl -sS -X POST "$PB_API_BASE/application-groups" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Approved design tools", "applications": [ "0AP01FIGMAXXXXXXXXXXXXXXXXXXX" ] }' ``` ```json { "id": "0AG01DESIGNXXXXXXXXXXXXXXXXXX" } ``` ```bash export GROUP_ID='0AG01DESIGNXXXXXXXXXXXXXXXXXX' ``` Attach the group to the rule once: ```bash curl -sS -X PATCH "$PB_API_BASE/policy/access-and-data/rules/$RULE_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "applications": { "applicationGroups": { "accessMode": "specific", "specific": { "addApplicationGroupIds": ["'"$GROUP_ID"'"] } } } }' ``` Response (`200`): ```json { "id": "0RLEXAMPLERULEXXXXXXXXXXXXXX" } ``` ### 3. From now on, add/remove applications on the group ```bash curl -sS -X PATCH "$PB_API_BASE/application-groups/$GROUP_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "applications": { "add": [ "0AP01MIROXXXXXXXXXXXXXXXXXXXX" ], "remove": [ "0AP01FIGMAXXXXXXXXXXXXXXXXXXX" ] } }' ``` Response (`200`): ```json { "id": "0AG01DESIGNXXXXXXXXXXXXXXXXXX" } ``` **Note** A group holds up to 2000 applications. Approach B is the most granular, but it creates the most objects. --- ## Approach C: URLs directly on the rule For a small list you do not reuse, skip the application entirely and patch URLs onto the rule's SaaS scope: ```bash curl -sS -X PATCH "$PB_API_BASE/policy/access-and-data/rules/$RULE_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "applications": { "saas": { "accessMode": "specific", "specific": { "addUrls": [ { "url": "https://newtool.example.com" } ], "removeUrls": [ { "url": "https://oldtool.example.com" } ] } } } }' ``` Response (`200`): ```json { "id": "0RLEXAMPLERULEXXXXXXXXXXXXXX" } ``` This is the simplest to start but churns the rule object on every change and cannot be reused by another rule. --- ## Verify and publish ```bash curl -sS "$PB_API_BASE/policy/access-and-data/rules/$RULE_ID" -H "Authorization: Bearer $PB_TOKEN" curl -sS -X POST "$PB_API_BASE/configuration-management/draft/publish" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{"description": "Update allow/block list"}' ``` The `GET` returns the full rule (confirm the applications), and the publish returns `201` when a new active version is created. --- ## Full script (Python, approach A) ```python import os, requests base = os.environ["PB_API_BASE"] headers = {"Authorization": f"Bearer {os.environ['PB_TOKEN']}"} rule_id = os.environ["RULE_ID"] # 1. Create the list-holder app (once) app_id = requests.post( f"{base}/applications/type/custom", headers=headers, json={"type": "custom", "name": "Blocklist - marketing trackers", "urls": [{"url": "tracker.example.com"}]}, timeout=30, ).json()["id"] # 2. Link it to the rule (once) requests.patch( f"{base}/policy/access-and-data/rules/{rule_id}", headers=headers, json={"applications": {"saas": {"accessMode": "specific", "specific": {"addApplicationIds": [app_id]}}}}, timeout=30, ).raise_for_status() # 3. Ongoing: sync URLs on the app def sync_urls(to_add, to_remove): urls = {} if to_add: urls["add"] = [{"url": u} for u in to_add] if to_remove: urls["remove"] = [{"url": u} for u in to_remove] if not urls: return requests.patch(f"{base}/applications/type/custom/{app_id}", headers=headers, json={"type": "custom", "urls": urls}, timeout=30).raise_for_status() sync_urls(["newtracker.example.com"], ["tracker.example.com"]) # 4. Publish just the app requests.post(f"{base}/configuration-management/draft/partial-publish", headers=headers, json={"entityIds": [app_id], "description": "Update blocklist"}, timeout=30) ``` --- ## Related - Building blocks: [Applications](https://pan.dev/prisma-browser/guide/applications), [Application groups](https://pan.dev/prisma-browser/guide/application-groups), [Rules](https://pan.dev/prisma-browser/guide/rules) - Concepts: [Delta patch](https://pan.dev/prisma-browser/guide/delta-patch), [Partial publish](https://pan.dev/prisma-browser/guide/draft-and-publish#partial-publish) - Related use cases: [Bulk-update rule applications with delta patch](https://pan.dev/prisma-browser/guide/bulk-update-rule-applications-with-delta), [Manage users on a rule](https://pan.dev/prisma-browser/guide/manage-users-on-a-rule) --- # Publish a single object (partial publish) _Source: https://pan.dev/prisma-browser/guide/automate-with-partial-publish | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ **Caution** **Partial publish is not in the public API yet.** The endpoint is implemented but gated per tenant, so it is absent from the published API reference. Confirm it is enabled on your tenant before relying on it. When automation changes one object, you usually want to publish only that object, not whatever else happens to be sitting in the shared draft. [Partial publish](https://pan.dev/prisma-browser/guide/draft-and-publish#partial-publish) makes this a clean two-call pattern: edit the object, then publish only that object by ID. **Use this when:** - A script makes frequent, narrow changes (add a user to a group, add an application) and must not ship unrelated draft edits. - Multiple processes or an administrator share the tenant draft. **Prerequisites:** a Super User service account and the environment variables from [Getting started](https://pan.dev/prisma-browser/guide/getting-started). Read [Partial publish](https://pan.dev/prisma-browser/guide/draft-and-publish#partial-publish) and [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish) first. **Note** **Non-policy objects only.** This phase supports user groups (`0UG`), application groups (`0AG`), applications (`0AP`), device groups (`0DG`), and tags (`0TG`). Naming a rule or section returns `501`. For policy changes, use a normal full publish. --- ## The two-call pattern ```mermaid flowchart LR edit["1. Edit object (PUT/PATCH)"] --> pub["2. Partial publish that object"] pub --> live["Only that object goes live"] ``` ### 1. Edit the object For example, add a user to a group: ```bash export UG_ID='0UG01TEAMXXXXXXXXXXXXXXXXXXXX' curl -sS -X PUT "$PB_API_BASE/user-groups/$UG_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "users": [ { "userId": "0UR01NEWXXXXXXXXXXXXXXXXXXXXX", "action": "add" } ] }' ``` Response (`200`): ```json { "id": "0UG01TEAMXXXXXXXXXXXXXXXXXXXX", "userGroupId": "0UG01TEAMXXXXXXXXXXXXXXXXXXXX" } ``` The `id` you need for the next call comes back in this response, so no extra lookup is required. ### 2. Publish only that object ```bash curl -sS -X POST "$PB_API_BASE/configuration-management/draft/partial-publish" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "entityIds": ["'"$UG_ID"'"], "description": "ServiceNow INC0042311: contractor offboarding" }' ``` Response (`201`): ```json { "configurationVersion": { "id": "0CV01EXAMPLEXXXXXXXXXXXXXXXXX", "number": 73 }, "publishedEntityIds": ["0UG01TEAMXXXXXXXXXXXXXXXXXXXX"] } ``` Everything else still pending in the draft stays pending. Only the group you named went live. Creating and deleting objects follow the same pattern: perform the write, then publish the `id` it returned. **Note** Put the originating ticket, request, or event reference in `description`. It is stored with the configuration version and shown as the change reason in the configuration log, which lets you trace any published change back to what triggered it. Where no ticket reference exists, use a stable identifier for the automation and its run, for example `"Identity sync job 2026-08-06T02:00Z"`. --- ## Publish several objects at once Where one automation run modifies several objects, collect the IDs and submit them in a single request rather than one call per object. The publish is atomic: all named entities are promoted, or none are. ```bash curl -sS -X POST "$PB_API_BASE/configuration-management/draft/partial-publish" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "entityIds": [ "0UG01TEAMXXXXXXXXXXXXXXXXXXXX", "0AG01DESIGNXXXXXXXXXXXXXXXXXX" ], "description": "ServiceNow INC0042311: contractor offboarding" }' ``` `entityIds` takes 1 to 1000 unique IDs. The object type comes from the ID prefix, so no type parameter is needed. --- ## Check what is pending before you publish To see what your process (or others) have staged, list pending changes: ```bash curl -sS "$PB_API_BASE/configuration-management/draft/pending-changes" \ -H "Authorization: Bearer $PB_TOKEN" ``` Response (`200`): ```json { "pageInfo": { "hasNextPage": false, "cursor": "", "totalCount": 1 }, "data": [ { "entityId": "0UG01TEAMXXXXXXXXXXXXXXXXXXXX", "entityType": "userGroup", "name": "Team group", "lastUpdatedBy": "0UR01EXAMPLEADMINXXXXXXXXXXXX", "operation": "updated" } // ... more pending changes ] } ``` Each row reports `entityId`, `entityType`, `name`, `lastUpdatedBy`, and `operation` (`created` / `updated` / `deleted`). Filter by `lastUpdatedBy` client-side to see only your own edits. An empty draft returns `200` with an empty list. **Note** In a multi-writer environment, publish by the exact IDs you changed rather than doing a full publish. This is the safest way to avoid shipping a colleague's half-finished edit. --- ## Full script (Python) ```python import os, requests base = os.environ["PB_API_BASE"] headers = {"Authorization": f"Bearer {os.environ['PB_TOKEN']}"} def edit_and_publish_group(ug_id, add=(), remove=(), reason=None): # 1. Edit the object users = [{"userId": u, "action": "add"} for u in add] + \ [{"userId": u, "action": "remove"} for u in remove] if users: requests.put(f"{base}/user-groups/{ug_id}", headers=headers, json={"users": users}, timeout=30).raise_for_status() # 2. Publish only that object resp = requests.post(f"{base}/configuration-management/draft/partial-publish", headers=headers, json={"entityIds": [ug_id], "description": reason or f"Update {ug_id}"}, timeout=30) resp.raise_for_status() return resp.json() result = edit_and_publish_group("0UG01TEAMXXXXXXXXXXXXXXXXXXXX", add=["0UR01NEWXXXXXXXXXXXXXXXXXXXXX"], reason="ServiceNow INC0042311: contractor offboarding") print("published:", result["publishedEntityIds"], "version:", result["configurationVersion"]["number"]) ``` --- ## Error cases Errors share one envelope. Where present, `details[]` identifies the offending items, and `field` gives the position in the request, for example `entityIds[2]`: ```json { "error": { "code": "NOT_FOUND", "message": "No pending change found for one or more of the specified entities.", "timestamp": "2026-08-06T09:15:00Z", "details": [] } } ``` | HTTP | `code` | Condition | What to do | |---|---|---|---| | 400 | `VALIDATION_ERROR` | An ID is malformed or duplicated, the list is empty or exceeds 1000 items, or the body carries only `description` | Correct the request. This is a defect in the caller; do not retry unchanged | | 403 | `FORBIDDEN` | The service account cannot publish configuration changes | Use a Super User account | | 404 | `NOT_FOUND` | A valid, supported ID has no pending change in the draft. `details[]` lists every affected ID | Usually the write made no material change, or the object was already published. Do not retry unchanged | | 409 | `CONFLICT` | A named object depends on an unpublished change you did not include. `details[].ids` lists the blockers | Include the blocking IDs and retry, or escalate. Reports the first conflict found | | 500 | `INTERNAL_ERROR` | Unexpected server-side failure | Retry with bounded exponential backoff. Nothing was published | | 501 | `PARTIAL_PUBLISH_POLICY_UNSUPPORTED` | You named a rule or section (`0RL` / `0SR`) | Publish policy changes with a full publish | | 501 | `UNSUPPORTED_OPERATION` | The entity type is not partially publishable, or partial publish is not enabled for the tenant | Use a full publish | Two of these deserve explicit handling: - **`404` after an apparently successful write.** A `PUT` that changes nothing, such as adding a user who is already a member or removing one who is not, stages no pending change for the publish to promote. Decide whether your automation treats this as success or as an exception. - **`409` from an unpublished dependency.** When an administrator has an unpublished rule that references the object you are publishing, both must be published together. This most often appears when deleting an object a rule still uses. Escalation is usually more appropriate than an automated retry. --- ## Related - Concepts: [Partial publish](https://pan.dev/prisma-browser/guide/draft-and-publish#partial-publish), [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish) - Building blocks: [User groups](https://pan.dev/prisma-browser/guide/user-groups), [Application groups](https://pan.dev/prisma-browser/guide/application-groups) - Related use cases: [Gradual rollout with a user group](https://pan.dev/prisma-browser/guide/gradual-rollout-with-user-group), [Manage an allow / block list](https://pan.dev/prisma-browser/guide/manage-allow-block-list) --- # Suspend a compromised user _Source: https://pan.dev/prisma-browser/guide/suspend-a-compromised-user | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ When an account is compromised or an employee is offboarded, you often need to revoke access to the Prisma Browser **right now**, not at the next policy publish. User actions do exactly that: they act on the live user immediately, with no draft to stage and no publish to run. See [Direct actions](https://pan.dev/prisma-browser/guide/direct-actions) for why these bypass the draft model. **Use this when:** a SOC or IT workflow must lock a person out of the Prisma Browser immediately (incident response, offboarding). **Prerequisites:** a Super User service account and the environment variables from [Getting started](https://pan.dev/prisma-browser/guide/getting-started). ## Suspend the user Suspending a user revokes access to the Prisma Browser across all of their known and future devices. It takes effect at once and returns `200`. ``` POST /seb-api/v1/users/suspend ``` ```bash curl -sS -X POST "$PB_API_BASE/users/suspend" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "userIds": ["0UREXAMPLECOMPROMISEDXXXXXX"] }' ``` Response (`200`): ```json { "message": "Users suspended successfully" } ``` ## Force re-authentication (a lighter option) If you only need to invalidate active sessions (for example, after rotating credentials) rather than lock the account, force the user to re-authenticate on all active devices instead of suspending: ```bash curl -sS -X POST "$PB_API_BASE/users/force-reauth" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "userIds": ["0UREXAMPLECOMPROMISEDXXXXXX"] }' ``` Response (`200`): ```json { "message": "Re-authentication forced successfully" } ``` ## Reverse it when the incident is resolved There is no "undo via publish" for direct actions. Resume the user to restore access: ```bash curl -sS -X POST "$PB_API_BASE/users/resume" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "userIds": ["0UREXAMPLECOMPROMISEDXXXXXX"] }' ``` Response (`200`): ```json { "message": "Users resumed successfully" } ``` ## Things to keep in mind - **Immediate, not staged.** These calls do not use draft and publish; `configurationVersion` does not apply. - **Reverse with the pair.** Use `resume` to undo `suspend`, not a configuration rollback. - **Handle failures with the standard envelope.** On failure these return the standard [error envelope](https://pan.dev/prisma-browser/guide/errors); branch on `error.code`. ## Related - [Quarantine a lost or compromised device](https://pan.dev/prisma-browser/guide/quarantine-a-device) - Concepts: [Direct actions](https://pan.dev/prisma-browser/guide/direct-actions), [Errors](https://pan.dev/prisma-browser/guide/errors) --- # Change a rule's scope _Source: https://pan.dev/prisma-browser/guide/change-a-rule-scope | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ A rule's `scope` decides who it applies to: users, user groups, device groups, IPs, and locations. Scope changes use the same delta pattern as the rest of the API, so you can add or remove members without re-sending the whole list and without risking a race with a concurrent edit. This works the same way on every policy type. **Use this when:** you need to widen, narrow, or retarget who a rule covers (add a group, exclude a region, pin to specific devices). **Prerequisites:** a Super User service account and the environment variables from [Getting started](https://pan.dev/prisma-browser/guide/getting-started). Have the rule's ID and its policy type (`sign-in`, `security`, `access-and-data`, or `customization`). ```bash export RULE_ID='0RLEXAMPLERULEXXXXXXXXXXXXXX' export TYPE='security' ``` ## Scope segments Each segment supports `isAny` (apply to all) plus add/remove deltas: | Segment | Add / remove | |---|---| | Users | `addUsers` / `removeUsers` | | User groups | `addUserGroups` / `removeUserGroups` | | Device groups | `addDeviceGroups` / `removeDeviceGroups` | | Public / private IPs | `addPublicIps` / `removePublicIps`, `addPrivateIps` / `removePrivateIps` | | Locations | `addLocations` / `removeLocations` | ## Add and remove members in one call PATCH the rule with only the scope deltas. Adds and removes can be combined. ``` PATCH /seb-api/v1/policy/{type}/rules/{id} ``` ```bash curl -sS -X PATCH "$PB_API_BASE/policy/$TYPE/rules/$RULE_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "scope": { "users": { "addUserGroups": ["0UGEXAMPLECONTRACTORSXXXXXX"], "removeUserGroups": ["0UGEXAMPLEINTERNSXXXXXXXXXX"] } } }' ``` Response (`200`): ```json { "id": "0RLEXAMPLERULEXXXXXXXXXXXXXX" } ``` ## Apply to everyone, or narrow back down - To make a segment apply to all, set `isAny: true` for that segment (for example `"users": { "isAny": true }`). - To stop applying to everyone and target specific members, set `isAny: false` and add the members you want. A segment with `isAny: false` must contain at least one member. ## Exclude by device group or location The same pattern narrows a rule. For example, stop a rule from applying in one region: ```bash curl -sS -X PATCH "$PB_API_BASE/policy/$TYPE/rules/$RULE_ID" \ -H "Authorization: Bearer $PB_TOKEN" -H "Content-Type: application/json" \ -d '{ "scope": { "locations": { "removeLocations": ["DE"] } } }' ``` Response (`200`): ```json { "id": "0RLEXAMPLERULEXXXXXXXXXXXXXX" } ``` ## Publish to make it live Scope edits land on the **draft**. Publish to enforce them: ```bash curl -sS -X POST "$PB_API_BASE/configuration-management/draft/publish" \ -H "Authorization: Bearer $PB_TOKEN" -H "Content-Type: application/json" \ -d '{"description": "Retarget rule scope"}' ``` Returns `201` (a new active version is created). With an empty draft it returns `409`: ```json { "message": "No pending changes found in the current draft" } ``` **Note** To roll a rule out to more people over time, see [Roll out a rule gradually](https://pan.dev/prisma-browser/guide/gradual-rollout-with-user-group). To move users in and out via a managed list rather than directly on the rule, see [Add or remove users on a rule](https://pan.dev/prisma-browser/guide/manage-users-on-a-rule). ## Related - [Policy overview](https://pan.dev/prisma-browser/guide/rules), [Delta patch](https://pan.dev/prisma-browser/guide/delta-patch) - [Add or remove users on a rule](https://pan.dev/prisma-browser/guide/manage-users-on-a-rule), [Roll out a rule gradually](https://pan.dev/prisma-browser/guide/gradual-rollout-with-user-group) --- # Manage login and data controls _Source: https://pan.dev/prisma-browser/guide/manage-login-and-data-controls | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ Access and data rules carry two control groups: `loginControls` (how users may authenticate to the target application) and `dataControls` (what they may do with data once inside, such as clipboard, file transfer, watermarking, and printing). This page shows how to set and update them from automation. **Use this when:** you need to enforce or adjust login behavior or data protection on an existing access and data rule. **Prerequisites:** a Super User service account and the environment variables from [Getting started](https://pan.dev/prisma-browser/guide/getting-started). Have an access and data rule's ID. New to this policy type? Read [Access and data rules](https://pan.dev/prisma-browser/guide/access-and-data-rules) first. ```bash export RULE_ID='0RLEXAMPLEACCESSRULEXXXXXXX' ``` **Caution** **Controls are replaced whole, never delta-patched.** In a PATCH, a control you include fully replaces its previous body, omitted controls are preserved, and sending `null` removes a control. There is no per-element delta inside a control. Always send the complete control object you want. ## Login controls `loginControls` is keyed by control type: | Key | Purpose | |---|---| | `formLogin` | Username/password form login (allow, block, or restrict to domains; MFA; account shield) | | `passkeyLogin` | Passkey login (allow/block, optional MFA) | | `idpLogin` | Identity-provider (SSO and SAML) login, across the board or for specific provider domains | | `socialLogin` | Consumer social login (Google, Microsoft, Facebook, GitHub, X, LinkedIn, Apple), with `googleAllowedDomains` to keep corporate Google Workspace domains working | For the settings each control accepts, see the [Create an Access and Data rule](https://pan.dev/prisma-browser/api/create-access-and-data-rule) reference. Restrict form login to corporate domains: ```bash curl -sS -X PATCH "$PB_API_BASE/policy/access-and-data/rules/$RULE_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "loginControls": { "formLogin": { "action": "allowSpecificDomains", "domains": ["@company.com"], "mfaRequired": true } } }' ``` Response (`200`): ```json { "id": "0RLEXAMPLEACCESSRULEXXXXXXX" } ``` ## Data controls `dataControls` is keyed by control type. Common keys include `clipboard`, `fileProtection`, `webpageWatermarking`, `screenshot`, `print`, `genAIPrompt`, `camera`, and `microphone`. (For attaching an Enterprise Data Loss Prevention (DLP) profile, see [Set a DLP profile on a rule](https://pan.dev/prisma-browser/guide/set-a-dlp-profile).) Block clipboard in and out, and enable watermarking: ```bash curl -sS -X PATCH "$PB_API_BASE/policy/access-and-data/rules/$RULE_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "dataControls": { "clipboard": { "dataIn": { "active": true, "action": "block" }, "dataOut": { "active": true, "action": "block" } }, "webpageWatermarking": { "action": "enable", "settings": { "opacityPercentage": 25, "densityLevel": "standard" } } } }' ``` Response (`200`): ```json { "id": "0RLEXAMPLEACCESSRULEXXXXXXX" } ``` ## Remove a control Send the control key with `null`: ```bash curl -sS -X PATCH "$PB_API_BASE/policy/access-and-data/rules/$RULE_ID" \ -H "Authorization: Bearer $PB_TOKEN" -H "Content-Type: application/json" \ -d '{ "dataControls": { "webpageWatermarking": null } }' ``` Response (`200`): ```json { "id": "0RLEXAMPLEACCESSRULEXXXXXXX" } ``` ## Publish Control edits land on the **draft**. Publish to enforce them: ```bash curl -sS -X POST "$PB_API_BASE/configuration-management/draft/publish" \ -H "Authorization: Bearer $PB_TOKEN" -H "Content-Type: application/json" \ -d '{"description": "Tighten login and data controls"}' ``` Returns `201` when a new active version is created, or `409` with `{"message": "No pending changes found in the current draft"}` if the draft is empty. ## Related - [Access and data rules](https://pan.dev/prisma-browser/guide/access-and-data-rules), [Set a DLP profile on a rule](https://pan.dev/prisma-browser/guide/set-a-dlp-profile) - [Delta patch](https://pan.dev/prisma-browser/guide/delta-patch), [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish) --- # Quarantine a lost or compromised device _Source: https://pan.dev/prisma-browser/guide/quarantine-a-device | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ When a device is lost, stolen, or compromised, you can act on it immediately with device actions. Like user actions, these apply to the live device at once: no draft, no publish. See [Direct actions](https://pan.dev/prisma-browser/guide/direct-actions) for the contrast with policy. **Use this when:** a device must be suspended, archived, or removed immediately (lost laptop, compromised endpoint, decommissioning). **Prerequisites:** a Super User service account and the environment variables from [Getting started](https://pan.dev/prisma-browser/guide/getting-started). ## The device actions All are `POST` calls that identify the target device(s) by ID, take effect immediately, and return `200`. | Goal | Endpoint | Effect | |---|---|---| | Suspend access now | `POST /seb-api/v1/devices/suspend` | Suspends the device. | | Restore access | `POST /seb-api/v1/devices/resume` | Resumes a suspended device. | | Archive a device | `POST /seb-api/v1/devices/archive` | Moves the device to archived state. | | Restore an archived device | `POST /seb-api/v1/devices/restore` | Restores an archived device. | | Remove permanently | `POST /seb-api/v1/devices/delete` | Permanently deletes the device. | | Invalidate sessions | `POST /seb-api/v1/devices/force-reauth` | Forces re-authentication on the device. | ## Suspend a device immediately ```bash curl -sS -X POST "$PB_API_BASE/devices/suspend" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "deviceIds": ["0DEEXAMPLELOSTLAPTOPXXXXXXX"] }' ``` Response (`200`): ```json { "suspendedDeviceIds": ["0DEEXAMPLELOSTLAPTOPXXXXXXX"], "message": "1 devices suspended successfully" } ``` ## Decommission a device To retire a device permanently, resume it first if it is suspended, then delete it: ```bash curl -sS -X POST "$PB_API_BASE/devices/resume" \ -H "Authorization: Bearer $PB_TOKEN" -H "Content-Type: application/json" \ -d '{ "deviceIds": ["0DEEXAMPLELOSTLAPTOPXXXXXXX"] }' curl -sS -X POST "$PB_API_BASE/devices/delete" \ -H "Authorization: Bearer $PB_TOKEN" -H "Content-Type: application/json" \ -d '{ "deviceIds": ["0DEEXAMPLELOSTLAPTOPXXXXXXX"] }' ``` Each action returns `200` with a summary message, for example: ```json { "message": "1 devices deleted successfully" } ``` **Caution** **A suspended device must be resumed before it can be deleted, and delete is permanent.** ## Related - [Suspend a compromised user](https://pan.dev/prisma-browser/guide/suspend-a-compromised-user) - Concepts: [Direct actions](https://pan.dev/prisma-browser/guide/direct-actions), [Errors](https://pan.dev/prisma-browser/guide/errors) --- # Configure cloud storage and rotate credentials with no downtime _Source: https://pan.dev/prisma-browser/guide/rotate-cloud-storage-credentials | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ A **cloud storage provider** integration connects the Prisma Browser to Microsoft OneDrive or Google Drive so policy can route data there (for example, file uploads governed by an access and data rule). Provider credentials (a client secret or a service account key) expire and must be rotated on a schedule. This walkthrough configures a provider and then rotates its credentials without an outage. **Use this when:** you manage cloud storage integrations as code, or you need to rotate an Azure client secret / Google service account key before it expires without breaking the integration. **Prerequisites:** a Super User service account and the environment variables from [Getting started](https://pan.dev/prisma-browser/guide/getting-started). For Microsoft: an Azure AD app (tenant ID, client ID, client secret). For Google: a Workspace admin email for domain-wide delegation and a base64-encoded service account JSON key. **Caution** **Credentials are validated live before they are saved.** Both create and replace validate against the provider before persisting. If validation fails the call returns `422` and nothing changes, so a bad rotation can never take down a working integration. ## 1. Configure a provider `POST /integrations/cloud-storage`. `displayName` must be unique (1 to 30 characters), `type` is `microsoft` or `google`, and you send the credential block that matches the type. ```bash # Microsoft OneDrive curl -sS -X POST "$PB_API_BASE/integrations/cloud-storage" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "displayName": "OneDrive - Corp", "type": "microsoft", "microsoft": { "tenantId": "00000000-0000-0000-0000-000000000000", "clientId": "11111111-1111-1111-1111-111111111111", "clientSecret": "EXAMPLE~oldClientSecretValue" } }' ``` Response (`201`): ```json { "id": "0CSEXAMPLEPROVIDERXXXXXXXXX" } ``` For Google, send a `google` block instead: `{ "emailAddress": "admin@example.com", "serviceAccountKey": "" }`. **Caution** **Credential fields are write-only.** Reads return the provider with its live `status` (`connected` / `disconnected`) and non-secret properties, never the secret or key you sent. ## 2. Find the provider and check its status List all providers (each includes live connectivity status) to get the `id` you rotate against. ```bash curl -sS "$PB_API_BASE/integrations/cloud-storage" \ -H "Authorization: Bearer $PB_TOKEN" ``` ### Response (shape) ```json { "providers": [ { "id": "0CSEXAMPLEPROVIDERXXXXXXXXX", "displayName": "OneDrive - Corp", "type": "microsoft", "enabled": true, "status": "connected" } ] } ``` ## 3. Rotate credentials with no downtime `PUT /integrations/cloud-storage/{providerId}` replaces the credentials. The provider type cannot change, and every credential field is required on each call. Because the new credentials are validated live before they are persisted, the swap is atomic: it only takes effect if the new secret actually works. The no-downtime sequence: 1. Create the **new** secret/key at the provider (Azure or Google) while the old one is still valid. 2. `PUT` the new credentials. On success (`200`), the integration now uses them; on `422`, the old credentials remain in force. 3. Confirm `status` is `connected`. 4. Only then delete the **old** secret/key at the provider. ```bash export PROVIDER_ID='0CSEXAMPLEPROVIDERXXXXXXXXX' ``` ```bash curl -sS -X PUT "$PB_API_BASE/integrations/cloud-storage/$PROVIDER_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "microsoft": { "tenantId": "00000000-0000-0000-0000-000000000000", "clientId": "11111111-1111-1111-1111-111111111111", "clientSecret": "EXAMPLE~newRotatedSecretValue" } }' ``` Response (`200`): ```json { "id": "0CSEXAMPLEPROVIDERXXXXXXXXX" } ``` **Caution** **Do not revoke the old secret until the rotation succeeds.** If the `PUT` returns `422` or the provider comes back `disconnected`, the old credentials are still the ones in use. Fix the new secret and retry; revoking the old one first is what causes an outage. ## Tips and gotchas - **`type` is immutable.** Sending `type` on a `PUT` is rejected (`400`). To change provider type, delete and recreate. - **Delete is blocked while in use.** `DELETE /integrations/cloud-storage/{providerId}` returns `409` with a `ruleIds` list if any policy rule references the provider. Detach it from those rules first. - **Schedule ahead of expiry.** Drive rotation from the secret's expiry date, not a fixed calendar, so a new secret is always in place before the old one lapses. ## Related - Use cases: [Manage login and data controls](https://pan.dev/prisma-browser/guide/manage-login-and-data-controls), [Set a DLP profile on a rule](https://pan.dev/prisma-browser/guide/set-a-dlp-profile) --- # Reorder rules and sections _Source: https://pan.dev/prisma-browser/guide/reorder-rules-and-sections | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ Rules are evaluated top to bottom, so order is policy. Each policy type exposes a `positions` sub-resource for changing the order of its rules and sections. You can resubmit the full order (`PUT`) or apply a few targeted moves (`PATCH`). This page shows both. **Use this when:** you need to raise a rule's priority, move a rule into a different section, or reorder sections. **Prerequisites:** a Super User service account and the environment variables from [Getting started](https://pan.dev/prisma-browser/guide/getting-started). Pick your policy type (`sign-in`, `security`, `access-and-data`, or `customization`). ```bash export TYPE='security' ``` ## See the current order Read the policy; it returns rules and sections interleaved in evaluation order. ```bash curl -sS "$PB_API_BASE/policy/$TYPE" -H "Authorization: Bearer $PB_TOKEN" ``` Response (`200`). Rules and sections are interleaved in evaluation order: ```json { "pageInfo": { "hasNextPage": false, "cursor": "", "totalCount": 3 }, "data": [ { "type": "Section", "id": "0SREXAMPLEHIGHRISKXXXXXXXXXX", "position": 1, "name": "High risk" }, { "type": "Rule", "id": "0RLEXAMPLEBLOCKDEVTOOLSXXXXX", "position": 2, "name": "Block dev tools", "mode": "active", "evaluationOrder": 1 }, { "type": "Rule", "id": "0RLEXAMPLEALLOWBASELINEXXXXX", "position": 3, "name": "Allow baseline", "mode": "active", "evaluationOrder": 2 } // ... more rules and sections ], "metadata": { "configurationVersion": { "id": "0CV01EXAMPLEXXXXXXXXXXXXXXXXX", "status": "draft", "number": 0 } } } ``` ## Option A: Replace the whole order (PUT) Submit the complete, ordered list of every rule and section. Index 0 is evaluated first. All existing rules and sections must be present, and a section's rules must be contiguous. ``` PUT /seb-api/v1/policy/{type}/positions ``` Each entry is either a section or a rule. A rule names the section it belongs to via `sectionId` (omit or `null` for a standalone rule). ```bash curl -sS -X PUT "$PB_API_BASE/policy/$TYPE/positions" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "positions": [ { "type": "Section", "id": "0SREXAMPLEHIGHRISKXXXXXXXXXX" }, { "type": "Rule", "id": "0RLEXAMPLEBLOCKDEVTOOLSXXXXX", "sectionId": "0SREXAMPLEHIGHRISKXXXXXXXXXX" }, { "type": "Rule", "id": "0RLEXAMPLEALLOWBASELINEXXXXX" } ] }' ``` Response (`200`): ```json { "message": "Positions updated successfully", "itemsUpdated": 3 } ``` **Caution** **PUT is all-or-nothing.** You must include every rule and section. If you only know the few items you want to move, use `PATCH` instead so you don't have to reconstruct the entire list. ## Option B: Apply targeted moves (PATCH) Submit an ordered list of moves. Each move repositions one rule or section into a container (`target.sectionId`) using a position keyword (`top`, `bottom`, `before`, `after`) plus an anchor when using `before`/`after`. Moving a section carries its child rules with it. Moves apply atomically: if one fails, nothing changes and the failing move's index is returned. ``` PATCH /seb-api/v1/policy/{type}/positions ``` ```bash curl -sS -X PATCH "$PB_API_BASE/policy/$TYPE/positions" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "moves": [ { "subject": { "type": "Rule", "id": "0RLEXAMPLEBLOCKDEVTOOLSXXXXX" }, "target": { "position": "top", "sectionId": "0SREXAMPLEHIGHRISKXXXXXXXXXX" } }, { "subject": { "type": "Rule", "id": "0RLEXAMPLENEWRULEXXXXXXXXXXX" }, "target": { "position": "after", "anchor": { "type": "Rule", "id": "0RLEXAMPLEBLOCKDEVTOOLSXXXXX" }, "sectionId": "0SREXAMPLEHIGHRISKXXXXXXXXXX" } } ] }' ``` Response (`200`): ```json { "message": "Positions updated successfully", "itemsUpdated": 2 } ``` ## Rules to remember - `top` / `bottom` place the subject first/last in the container and take no anchor. - `before` / `after` require an `anchor` sibling in the same container. - A rule's container is a section (`sectionId`) or the top level (`sectionId: null`). - Baseline (default) rules are pinned at the bottom and cannot be moved or used as anchors. ## Publish Order changes land on the **draft**. Publish to enforce the new evaluation order: ```bash curl -sS -X POST "$PB_API_BASE/configuration-management/draft/publish" \ -H "Authorization: Bearer $PB_TOKEN" -H "Content-Type: application/json" \ -d '{"description": "Reorder security rules"}' ``` Returns `201` (a new active version is created). With an empty draft it returns `409`: ```json { "message": "No pending changes found in the current draft" } ``` ## Related - [Policy overview](https://pan.dev/prisma-browser/guide/rules), [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish) --- # Set a DLP profile on a rule _Source: https://pan.dev/prisma-browser/guide/set-a-dlp-profile | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ An access and data rule can enforce an Enterprise DLP (data loss prevention) data profile through `dataControls.dlpProfileId`. The profile itself is defined in Enterprise DLP, a separate service, so this is a two-part task: look up the profile ID in the DLP API, then attach it to your rule. **Use this when:** you want a rule in the Prisma Browser to apply an existing Enterprise DLP data profile (for example, to detect and block sensitive content on upload or in the clipboard). **Prerequisites:** a Super User service account and the environment variables from [Getting started](https://pan.dev/prisma-browser/guide/getting-started). Access to Enterprise DLP for the same tenant. ## 1. Find available DLP profile IDs DLP data profiles are managed by Enterprise DLP, on a different host (`https://api.dlp.paloaltonetworks.com`) and documented at [pan.dev/dlp/api/](https://pan.dev/dlp/api/). List the profiles to discover valid IDs; the profile ID is `content[].id`. ``` GET https://api.dlp.paloaltonetworks.com/v2/api/data-profiles ``` ```bash curl -sS "https://api.dlp.paloaltonetworks.com/v2/api/data-profiles?page=0&size=50" \ -H "Authorization: Bearer $PB_TOKEN" ``` ### Response (shape) ```json { "content": [ { "id": "11995044", "name": "PII - strict" }, { "id": "11995051", "name": "PCI - block upload" } ] } ``` Fetch one profile by ID with `GET /v2/api/data-profiles/{resourceId}`. **Note** **The DLP API is a separate service.** It uses a different host and its own access. The Prisma Browser does not list DLP profiles; it only references them by ID. The token must be authorized for both services on the same tenant. ## 2. Attach the profile to your rule Set `dataControls.dlpProfileId` on the access and data rule. Use the `id` value from the DLP API (the same string shown in the Enterprise DLP UI). ```bash export RULE_ID='0RLEXAMPLEACCESSRULEXXXXXXX' ``` ```bash curl -sS -X PATCH "$PB_API_BASE/policy/access-and-data/rules/$RULE_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "dataControls": { "dlpProfileId": "11995044", "fileProtection": { "fileUpload": { "action": "block" } } } }' ``` Response (`200`): ```json { "id": "0RLEXAMPLEACCESSRULEXXXXXXX" } ``` **Caution** **A DLP profile cannot stand alone.** `dlpProfileId` must accompany at least one inline data control (or a control set). Sending `dlpProfileId` with no controls is rejected. The example pairs it with `fileProtection` so the profile actually drives enforcement. **Caution** **The ID is validated.** It must reference a data profile that already exists in Enterprise DLP for the same tenant, or the rule write returns `400`. The Policy API resolves it to an internal reference at write time; you never supply or see that internal ID. ## 3. Confirm and publish Reads return the profile as an expanded object rather than the flat ID: ```json "dataControls": { "dlpProfile": { "id": "11995044", "name": "PII - strict" } } ``` Publish to enforce it: ```bash curl -sS -X POST "$PB_API_BASE/configuration-management/draft/publish" \ -H "Authorization: Bearer $PB_TOKEN" -H "Content-Type: application/json" \ -d '{"description": "Attach DLP profile to finance rule"}' ``` Returns `201` when a new active version is created. ## Tips and gotchas - **Profile-control compatibility is validated.** Some controls only work with certain profile types; an incompatible pairing is rejected with `400`. To save anyway (the incompatible control becomes a runtime no-op), set `dataControls.bypassDlpProfileValidation: true`. Use this deliberately. - **Same tenant, both services.** The DLP profile and the Browser rule must belong to the same tenant. ## Related - [Manage login and data controls](https://pan.dev/prisma-browser/guide/manage-login-and-data-controls), [Access and data rules](https://pan.dev/prisma-browser/guide/access-and-data-rules) - Reference: [Enterprise DLP API](https://pan.dev/dlp/api/) --- # Bulk-sync a rule's applications _Source: https://pan.dev/prisma-browser/guide/bulk-update-rule-applications-with-delta | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ A common automation job: keep the list of SaaS applications governed by an access-and-data rule in sync with an external source (a CMDB, a risk feed, a spreadsheet). This walkthrough does it with **delta patch**, so you never have to read-modify-write the whole list and never clobber concurrent edits. **Prerequisites:** a Super User service account and the environment variables from [Getting started](https://pan.dev/prisma-browser/guide/getting-started). Read [Delta patch](https://pan.dev/prisma-browser/guide/delta-patch) and [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish) first. The scenario: a rule currently scopes a set of SaaS applications. Your source of truth says some applications should be **added** and others **removed**. You will compute that delta and apply it in a single PATCH. --- ## 1. Identify the rule and the desired applications ```bash export RULE_ID='0RLEXAMPLERULEXXXXXXXXXXXXXX' # an access-and-data rule ``` You have a desired set of application IDs. You do not need to know the rule's current list: delta patch only needs what to add and what to remove. --- ## 2. (Optional) read the current list to compute a delta If your source gives you a full desired set rather than an explicit add/remove list, read the rule once, diff in your code, then send the delta. The current SaaS application IDs live at `applications.saas.specific.applicationIds`. ### Python ```python import os, requests base = os.environ["PB_API_BASE"] headers = {"Authorization": f"Bearer {os.environ['PB_TOKEN']}"} rule_id = os.environ["RULE_ID"] # Desired end state from your source of truth: desired = {"0AP01AAA...", "0AP01BBB...", "0AP01DDD..."} rule = requests.get(f"{base}/policy/access-and-data/rules/{rule_id}", headers=headers, timeout=30).json() saas = rule.get("applications", {}).get("saas", {}).get("specific", {}) current = set(saas.get("applicationIds", [])) to_add = sorted(desired - current) to_remove = sorted(current - desired) print("add:", to_add, "remove:", to_remove) ``` **Note** Even though you read the current list here, you still send a **delta**, not a full replacement. That way, any application a concurrent editor added that is not part of your desired-set logic is left untouched if you choose to scope your diff narrowly. If your source truly owns the entire list, a full replacement (`applicationIds`) is also valid; see [Delta patch: when to use which](https://pan.dev/prisma-browser/guide/delta-patch#when-to-use-which). --- ## 3. Apply the delta (PATCH the draft) Send only the additions and removals. Include `accessMode: "specific"` so the scope is in specific-applications mode. ```bash curl -sS -X PATCH "$PB_API_BASE/policy/access-and-data/rules/$RULE_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "applications": { "saas": { "accessMode": "specific", "specific": { "addApplicationIds": ["0AP01DDDXXXXXXXXXXXXXXXXXXXXX"], "removeApplicationIds": ["0AP01CCCXXXXXXXXXXXXXXXXXXXXX"] } } } }' ``` A `200` means the draft was updated: ```json { "id": "0RLEXAMPLERULEXXXXXXXXXXXXXX" } ``` --- ## 4. Verify and publish Read the rule back from the draft to confirm the new list, then publish. ```bash # verify (draft) curl -sS "$PB_API_BASE/policy/access-and-data/rules/$RULE_ID" \ -H "Authorization: Bearer $PB_TOKEN" # publish curl -sS -X POST "$PB_API_BASE/configuration-management/draft/publish" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{"description": "Sync access-and-data rule apps from source of truth"}' ``` The `GET` returns the full rule (confirm the application list), and the publish returns `201` when a new active version is created. --- ## Why delta wins here - **No lost updates.** If a colleague added an application between your read and write, your delta does not erase it. - **Smaller payloads.** You send a handful of IDs, not the entire (possibly hundreds-long) list. - **Retry-safe.** Re-running the same delta is a no-op: adding an existing application or removing an absent one does not error. So if your job fails after the PATCH but before publish, run it again. --- ## Beyond applications The same add/remove pattern works for the rule's other lists. Combine them in one PATCH: ```json { "applications": { "saas": { "accessMode": "specific", "specific": { "addApplicationIds": ["0AP01DDD..."], "addUrls": ["https://newtool.example.com"], "removeWebClassifications": ["SocialNetworking"] } } } } ``` See [Delta patch](https://pan.dev/prisma-browser/guide/delta-patch#where-delta-applies) for the full list of delta-capable fields. --- # Manage allowed browser extensions _Source: https://pan.dev/prisma-browser/guide/manage-allowed-extensions | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ Maintain the list of browser extensions a [security rule](https://pan.dev/prisma-browser/guide/rules) allows, adding or removing extension IDs from automation. Extensions are configured inside the rule's `allowedOrBlockedExtensions` control, so the workflow is different from list fields elsewhere in the API: there is no per-element delta, you read the control, change the array, and write it back whole. **Use this when:** - You curate a list of approved Chrome extensions and want a script to keep it current. **Prerequisites:** a Super User service account and the environment variables from [Getting started](https://pan.dev/prisma-browser/guide/getting-started). Read [Policy overview](https://pan.dev/prisma-browser/guide/rules) first. ```bash export RULE_ID='0RLEXAMPLEEXTENSIONSXXXXXXXX' # a security rule ``` **Caution** **Pair an allow list with a block-all baseline.** An allow list only matters if everything else is blocked. Make sure a baseline rule (or any lower-priority rule that matches the same users) sets the extensions policy to block all, and let this higher-priority rule carry the allow list. Without a block-all baseline, extensions not on your list are still allowed by default. **Caution** **Gotcha: controls have no delta.** Unlike a rule's scope or an access-and-data application list, controls are replaced **whole**. There is no `addExtension` / `removeExtension`. To change the list you must GET the rule, edit the `extensions` array yourself, and PATCH the entire control back. Read-modify-write is required here, so guard against concurrent editors (read immediately before you write). --- ## 1. Read the current control ```bash curl -sS "$PB_API_BASE/policy/security/rules/$RULE_ID" -H "Authorization: Bearer $PB_TOKEN" ``` The relevant slice of the response: ```json { "controls": { "allowedOrBlockedExtensions": { "mode": "allowByList", "extensions": [ { "id": "aapbdbdomjkkjkaonfhkkikfgjllcleb" } ] } } } ``` | Field | Notes | |---|---| | `mode` | `allowAll`, `blockAll`, `allowByList`, or `blockByListOrRisk`. Use `allowByList` to allow only the listed extensions. | | `extensions` | Array of `{ id }`. Each `id` is a 32-character Chrome extension ID (`a`-`p` only). Up to 1000. | --- ## 2. Compute the new array In your code, append the extension IDs to add and drop the ones to remove. The result is the complete new list. --- ## 3. PATCH the whole control Send the full control body with the updated `extensions` array. Include `mode` so the control stays in allow-by-list mode: ```bash curl -sS -X PATCH "$PB_API_BASE/policy/security/rules/$RULE_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "controls": { "allowedOrBlockedExtensions": { "mode": "allowByList", "extensions": [ { "id": "aapbdbdomjkkjkaonfhkkikfgjllcleb" }, { "id": "bfnaelmomeimhlpmgjnjophhpkkoljpa" } ] } } }' ``` Response (`200`): ```json { "id": "0RLEXAMPLEEXTENSIONSXXXXXXXX" } ``` **Note** Other controls on the rule are untouched. In a PATCH, controls you do not mention are preserved; only `allowedOrBlockedExtensions` is replaced because that is the only key you sent. --- ## Verify and publish ```bash curl -sS "$PB_API_BASE/policy/security/rules/$RULE_ID" -H "Authorization: Bearer $PB_TOKEN" curl -sS -X POST "$PB_API_BASE/configuration-management/draft/publish" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{"description": "Update allowed extensions"}' ``` The `GET` returns the full rule (confirm the `extensions` array), and the publish returns `201` when a new active version is created. --- ## Full script (Python) ```python import os, requests base = os.environ["PB_API_BASE"] headers = {"Authorization": f"Bearer {os.environ['PB_TOKEN']}"} rule_id = os.environ["RULE_ID"] def sync_extensions(to_add, to_remove): # 1. Read immediately before writing (controls have no delta) rule = requests.get(f"{base}/policy/security/rules/{rule_id}", headers=headers, timeout=30).json() control = rule.get("controls", {}).get("allowedOrBlockedExtensions", {"mode": "allowByList", "extensions": []}) # 2. Compute the new array current = {e["id"] for e in control.get("extensions", [])} current |= set(to_add) current -= set(to_remove) # 3. PATCH the whole control back body = {"controls": {"allowedOrBlockedExtensions": { "mode": control.get("mode", "allowByList"), "extensions": [{"id": i} for i in sorted(current)], }}} resp = requests.patch(f"{base}/policy/security/rules/{rule_id}", headers=headers, json=body, timeout=30) resp.raise_for_status() return resp.json()["id"] sync_extensions( to_add=["bfnaelmomeimhlpmgjnjophhpkkoljpa"], to_remove=[], ) # Publish (full, or partial-publish is not yet available for policy objects) requests.post(f"{base}/configuration-management/draft/publish", headers=headers, json={"description": "Update allowed extensions"}, timeout=30) ``` **Note** [partial publish](https://pan.dev/prisma-browser/guide/draft-and-publish#partial-publish) does not yet support policy objects, so publish the whole draft (or stage extension changes in their own publish cycle). --- ## Related - Policy: [Policy overview](https://pan.dev/prisma-browser/guide/rules), [Security rules](https://pan.dev/prisma-browser/guide/security-rules) - Concepts: [Delta patch](https://pan.dev/prisma-browser/guide/delta-patch) (why controls differ from list fields) --- # Add or remove users on a rule _Source: https://pan.dev/prisma-browser/guide/manage-users-on-a-rule | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ Add or remove the people a rule applies to. You can put users (or user groups) directly on the rule's scope, or manage a [local user group](https://pan.dev/prisma-browser/guide/user-groups) and reference it from the rule. Same outcome, different operational trade-offs. **Use this when:** - You want to change who a rule covers from automation. - You are deciding whether to scope a rule by individual users or by a managed group. **Prerequisites:** a Super User service account and the environment variables from [Getting started](https://pan.dev/prisma-browser/guide/getting-started). Read [Policy overview](https://pan.dev/prisma-browser/guide/rules), [User groups](https://pan.dev/prisma-browser/guide/user-groups), and [Delta patch](https://pan.dev/prisma-browser/guide/delta-patch) first. ```bash export RULE_ID='0RLEXAMPLERULEXXXXXXXXXXXXXX' ``` --- ## Approaches | Approach | How it works | Best when | Tradeoffs | |---|---|---|---| | **A. Directly on the rule** | Add/remove users and user groups in the rule's `scope` via delta patch | A small, rule-specific audience | The rule object changes on every membership edit; not reusable | | **B. Via a local user group** | Manage one [local user group](https://pan.dev/prisma-browser/guide/user-groups); reference it from the rule once | The audience changes often, or several rules share it | One extra object; you publish the group, not the rule | **Recommendation:** use **B** when the audience churns or is shared (it isolates membership changes from the rule and pairs with [partial publish](https://pan.dev/prisma-browser/guide/draft-and-publish#partial-publish)). Use **A** for a small, stable, rule-specific set. --- ## Approach A: directly on the rule Add and remove users and groups in the rule scope. Deltas leave everyone else in scope untouched: ```bash curl -sS -X PATCH "$PB_API_BASE/policy/security/rules/$RULE_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "scope": { "users": { "addUsers": ["0UR01CAROLXXXXXXXXXXXXXXXXXXX"], "removeUsers": ["0UR01BOBXXXXXXXXXXXXXXXXXXXXX"], "addUserGroups": ["0UG01ENGXXXXXXXXXXXXXXXXXXXXX"] } } }' ``` Response (`200`): ```json { "id": "0RLEXAMPLERULEXXXXXXXXXXXXXX" } ``` **Note** To scope a rule to everyone, send `"users": { "isAny": true }`. To go back to a specific list, send the `add…` fields with `isAny` omitted or false. --- ## Approach B: via a local user group ### 1. Create (or reuse) a local user group ```bash curl -sS -X POST "$PB_API_BASE/user-groups" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "DevTools block - scope", "userIds": ["0UR01ALICEXXXXXXXXXXXXXXXXXXX"] }' ``` ```json { "id": "0UG01SCOPEXXXXXXXXXXXXXXXXXXX" } ``` ```bash export UG_ID='0UG01SCOPEXXXXXXXXXXXXXXXXXXX' ``` ### 2. Reference the group from the rule (once) ```bash curl -sS -X PATCH "$PB_API_BASE/policy/security/rules/$RULE_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "scope": { "users": { "addUserGroups": ["'"$UG_ID"'"] } } }' ``` Response (`200`): ```json { "id": "0RLEXAMPLERULEXXXXXXXXXXXXXX" } ``` ### 3. From now on, edit the group, not the rule ```bash curl -sS -X PUT "$PB_API_BASE/user-groups/$UG_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "users": [ { "userId": "0UR01DANXXXXXXXXXXXXXXXXXXXXX", "action": "add" } ] }' ``` Response (`200`): ```json { "id": "0UG01SCOPEXXXXXXXXXXXXXXXXXXX", "userGroupId": "0UG01SCOPEXXXXXXXXXXXXXXXXXXX" } ``` The rule stays put; only the group changes. Publish the group (with [partial publish](https://pan.dev/prisma-browser/guide/draft-and-publish#partial-publish), publish only `0UG...`). **Note** This is the foundation of a [gradual rollout](https://pan.dev/prisma-browser/guide/gradual-rollout-with-user-group): keep adding members to the group over time and the rule's reach expands with each publish. --- ## Verify and publish ```bash curl -sS "$PB_API_BASE/policy/security/rules/$RULE_ID" -H "Authorization: Bearer $PB_TOKEN" curl -sS -X POST "$PB_API_BASE/configuration-management/draft/publish" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{"description": "Update rule audience"}' ``` The `GET` returns the full rule so you can confirm the scope: ```json { "id": "0RLEXAMPLERULEXXXXXXXXXXXXXX", "name": "Block developer tools", "scope": { "users": { "isAny": false, "userGroups": [ { "id": "0UG01SCOPEXXXXXXXXXXXXXXXXXXX", "name": "DevTools block - scope" } ] } // ... other scope segments } // ... controls, metadata } ``` The publish returns `201` when a new active version is created. --- ## Full script (Python, approach B) ```python import os, requests base = os.environ["PB_API_BASE"] headers = {"Authorization": f"Bearer {os.environ['PB_TOKEN']}"} rule_id = os.environ["RULE_ID"] # 1. Create a local user group ug_id = requests.post(f"{base}/user-groups", headers=headers, json={"name": "DevTools block - scope", "userIds": ["0UR01ALICEXXXXXXXXXXXXXXXXXXX"]}, timeout=30).json()["id"] # 2. Reference it from the rule (once) requests.patch(f"{base}/policy/security/rules/{rule_id}", headers=headers, json={"scope": {"users": {"addUserGroups": [ug_id]}}}, timeout=30).raise_for_status() # 3. Ongoing: change membership on the group def set_members(add=(), remove=()): users = [{"userId": u, "action": "add"} for u in add] + \ [{"userId": u, "action": "remove"} for u in remove] if users: requests.put(f"{base}/user-groups/{ug_id}", headers=headers, json={"users": users}, timeout=30).raise_for_status() set_members(add=["0UR01DANXXXXXXXXXXXXXXXXXXXXX"]) # 4. Publish only the group requests.post(f"{base}/configuration-management/draft/partial-publish", headers=headers, json={"entityIds": [ug_id], "description": "Expand rule audience"}, timeout=30) ``` --- ## Related - Building blocks: [Rules](https://pan.dev/prisma-browser/guide/rules), [User groups](https://pan.dev/prisma-browser/guide/user-groups) - Concepts: [Delta patch](https://pan.dev/prisma-browser/guide/delta-patch), [Partial publish](https://pan.dev/prisma-browser/guide/draft-and-publish#partial-publish) - Related use cases: [Roll out a rule gradually](https://pan.dev/prisma-browser/guide/gradual-rollout-with-user-group), [Change a rule's scope](https://pan.dev/prisma-browser/guide/change-a-rule-scope) --- # Handle user access requests _Source: https://pan.dev/prisma-browser/guide/handle-user-access-requests | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ When a rule prompts for **admin approval**, the user submits a request instead of being allowed or blocked outright. This walkthrough automates the administrator side: poll for pending requests, approve or decline them with a bypass timeframe, and revoke an approval if circumstances change. See [User requests](https://pan.dev/prisma-browser/guide/user-requests) for how the prompt is configured. **Use this when:** you want a helpdesk, SOAR, or chat-ops workflow to triage access requests instead of having an administrator watch Strata Cloud Manager. **Prerequisites:** a Super User service account and the environment variables from [Getting started](https://pan.dev/prisma-browser/guide/getting-started). At least one rule with a prompt set to `adminApproval`. **Note** **These are direct operations.** Approving, declining, and revoking take effect immediately. There is no draft or publish step. ## 1. List pending requests Filter by `request.status=Pending` to get the queue. The list is cursor-paginated. ```bash curl -sS "$PB_API_BASE/user-requests?request.status=Pending&sort=request.created_at&order=asc&limit=50" \ -H "Authorization: Bearer $PB_TOKEN" ``` ### Response (shape) ```json { "pageInfo": { "nextCursor": "eyJvIjoxMH0" }, "data": [ { "id": "0URLEXAMPLEREQUESTXXXXXXXXX", "userId": "0UREXAMPLEUSERXXXXXXXXXXXXX", "type": "WebAccess", "status": "Pending", "url": "https://files.example.com/share", "reason": "Need to download the vendor SOW", "ruleId": "0RLEXAMPLEACCESSRULEXXXXXXX", "createdAt": "2026-06-29T08:14:00Z" } ] } ``` You can narrow the queue further with `request.type` (`WebAccess` or `AppLogin`), `request.user_id`, `request.rule_id`, or `request.url`. ## 2. Approve or decline a request `POST /user-requests/{id}/action` with `action` set to `approve` or `decline`. On approval, set `adminBypassTimeframe` to control how long the approval holds before the user is challenged again. Add an `adminComment` for the audit trail. ```bash export REQUEST_ID='0URLEXAMPLEREQUESTXXXXXXXXX' ``` ```bash # Approve for 24 hours curl -sS -X POST "$PB_API_BASE/user-requests/$REQUEST_ID/action" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "action": "approve", "adminBypassTimeframe": "24h", "adminComment": "Approved: vendor SOW download" }' # Or decline curl -sS -X POST "$PB_API_BASE/user-requests/$REQUEST_ID/action" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "action": "decline", "adminComment": "Use the approved vendor portal instead" }' ``` Response (`200`) returns the updated request: ```json { "id": "0URLEXAMPLEREQUESTXXXXXXXXX", "status": "Approved" } ``` Allowed `adminBypassTimeframe` values: `Once`, `10m`, `1h`, `4h`, `9h`, `12h`, `24h`, `3d`, `7d`, `14d`, `30d`, `60d`, `90d`. Use `Once` for a single passage and a longer window for repeat access. ## 3. Revoke an approval If an approval should no longer stand (for example, the user left the project), revoke it. This withdraws an already-approved request and moves it to `Revoked`. ```bash curl -sS -X POST "$PB_API_BASE/user-requests/$REQUEST_ID/revoke" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "revokerComment": "Access no longer required" }' ``` Response (`200`) returns the updated request: ```json { "id": "0URLEXAMPLEREQUESTXXXXXXXXX", "status": "Revoked" } ``` ## Tips and gotchas - **Approve idempotency.** Acting on a request that is not `Pending` is rejected; check `status` before acting if you run the poller frequently. - **Bypass timeframe is per approval.** It is independent of the rule prompt's `durationMinutes`; the value you send on approval wins for that request. - **Audit fields are populated for you.** `respondedBy`, `responseTime`, `revokedBy`, and `revokedAt` are set from the calling identity and timestamps, so use a dedicated service account if you want a clean audit trail. ## Related - Concepts: [User requests](https://pan.dev/prisma-browser/guide/user-requests), [Pagination](https://pan.dev/prisma-browser/guide/pagination) --- # Require an IdP authentication factor _Source: https://pan.dev/prisma-browser/guide/require-idp-authentication-factor | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ A security rule can require an **authentication factor** to unlock the Prisma Browser or to perform step-up multi-factor authentication (MFA). The `identityProvider` factor authenticates the user against an identity provider (IdP) defined by a Cloud Identity Engine (CIE) authentication profile. The profile itself is managed in CIE, a separate service, so this is a two-part task: look up the profile ID in the CIE API, then reference it from `controls.authenticationFactor` on a security rule. **Use this when:** you want browser unlock or step-up MFA to run against a specific IdP configuration rather than the tenant's globally configured profile. **Prerequisites:** a Super User service account and the environment variables from [Getting started](https://pan.dev/prisma-browser/guide/getting-started). Access to Cloud Identity Engine for the same tenant, with at least one authentication profile configured. ## 1. Find available CIE authentication profiles Authentication profiles are managed by CIE and are not listed on pan.dev. Discover the valid profiles for the tenant through the CIE API proxy, which is served from the SASE host root (not under `/seb-api/v1`). The `profile_id` is the value you attach to the rule. ``` GET /cie/cas/v1/auth-profiles ``` ```bash curl -sS "${PB_API_BASE%/seb-api/v1}/cie/cas/v1/auth-profiles" \ -H "Authorization: Bearer $PB_TOKEN" ``` ### Response (shape) ```json [ { "cas_tenant_id": "43743185388544200", "display_name": "Example Tenant - CIE", "profiles": [ { "profile_id": "d7c3f6a2-9b41-4e58-a0c2-1f2e3d4c5b6a", "display_name": "corp-idp-profile", "mode": "single", "mfa_enforceable": true, "auth_types": ["saml"] } ] } ] ``` For the full per-profile IdP configuration, use `GET /cie/cas/v1/detailed-auth-profiles`. Capture the `profile_id` you want: ```bash export AUTH_PROFILE_ID='d7c3f6a2-9b41-4e58-a0c2-1f2e3d4c5b6a' ``` **Note** **CIE is a separate service.** The Prisma Browser does not manage authentication profiles; it only references them by ID. The token must be authorized for both services on the same tenant. To require MFA at the factor, choose a profile whose `mfa_enforceable` is `true`. ## 2. Attach the factor to a security rule The factor lives on a security rule at `controls.authenticationFactor`. Select the factor with the `method` discriminator (`identityProvider`, `passkey`, or `pinCode`). For an IdP factor, set `identityProvider.profileSource` to `custom` and pass the CIE `profile_id` as `authProfileId`. ``` POST /seb-api/v1/policy/security/rules ``` ```bash curl -sS -X POST "$PB_API_BASE/policy/security/rules" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Require IdP authentication factor", "mode": "active", "scope": { "users": { "isAny": true } }, "controls": { "authenticationFactor": { "method": "identityProvider", "identityProvider": { "profileSource": "custom", "authProfileId": "'"$AUTH_PROFILE_ID"'", "incognito": true, "forceReauthentication": true } } } }' ``` Response (`201`): ```json { "id": "0RLEXAMPLESECURITYRULEXXXXX" } ``` `incognito` performs the IdP authentication in an isolated browser session, and `forceReauthentication` reauthenticates against the IdP rather than reusing an existing session; both default to `true`. ## 3. Publish The rule is created on the draft. Publish to make it live (see [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish)). ```bash curl -sS -X POST "$PB_API_BASE/configuration-management/draft/publish" \ -H "Authorization: Bearer $PB_TOKEN" -H "Content-Type: application/json" \ -d '{"description": "Require IdP authentication factor"}' ``` Returns `201` when a new active version is created. ## Tips and gotchas - **Use the tenant default instead of a specific profile.** Set `profileSource` to `useConfiguredAuthProfile` (the default) and omit `authProfileId` to use the tenant's globally configured authentication profile. - **The profile ID is validated against CIE.** `authProfileId` must reference a profile that exists in CIE for the same tenant. - **Other factor methods.** Use `method: "passkey"` (internal or external authenticators) or `method: "pinCode"` (PIN length and lockout policy) when browser unlock should not depend on an IdP. ## Related - Policy: [Security rules](https://pan.dev/prisma-browser/guide/security-rules), [Policy overview](https://pan.dev/prisma-browser/guide/rules) - Concepts: [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish), [Authentication](https://pan.dev/prisma-browser/guide/authentication) --- # Roll out a rule gradually _Source: https://pan.dev/prisma-browser/guide/gradual-rollout-with-user-group | Generated: 2026-09-18 16:27 UTC_ _Guide page: the controls, fields and enum values named here are examples, not a complete list. The OpenAPI specification at https://pan.dev/spec/prisma-browser-management.yaml is the authoritative catalog._ Roll a rule out to more and more people over time without ever editing the rule again. Attach a [local user group](https://pan.dev/prisma-browser/guide/user-groups) to the rule once, then expand the group in batches. Each publish widens the rule's reach. When the rollout is complete, the group covers your entire intended scope. **Use this when:** - You want to ship a new control to a pilot first, then 10%, then everyone. - You want the rule to stay fixed while the audience grows on a schedule. **Prerequisites:** a Super User service account and the environment variables from [Getting started](https://pan.dev/prisma-browser/guide/getting-started). Read [User groups](https://pan.dev/prisma-browser/guide/user-groups) and [Partial publish](https://pan.dev/prisma-browser/guide/draft-and-publish#partial-publish) first. ```mermaid flowchart LR g["Create rollout group (pilot)"] --> link["Attach group to rule (once)"] link --> b1["Add batch + publish group"] b1 --> b2["Add batch + publish group"] b2 --> done["Group = full scope"] ``` --- ## 1. Create the rollout group with the first batch Start with the pilot users: ```bash curl -sS -X POST "$PB_API_BASE/user-groups" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Rollout - DevTools block", "userIds": ["0UR01PILOT1XXXXXXXXXXXXXXXXXX", "0UR01PILOT2XXXXXXXXXXXXXXXXXX"] }' ``` ```json { "id": "0UG01ROLLOUTXXXXXXXXXXXXXXXXX" } ``` ```bash export UG_ID='0UG01ROLLOUTXXXXXXXXXXXXXXXXX' export RULE_ID='0RLEXAMPLERULEXXXXXXXXXXXXXX' ``` --- ## 2. Attach the group to the rule (once) ```bash curl -sS -X PATCH "$PB_API_BASE/policy/security/rules/$RULE_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "scope": { "users": { "addUserGroups": ["'"$UG_ID"'"] } } }' ``` Response (`200`): ```json { "id": "0RLEXAMPLERULEXXXXXXXXXXXXXX" } ``` Publish once so the rule is live but scoped only to the pilot: ```bash curl -sS -X POST "$PB_API_BASE/configuration-management/draft/publish" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{"description": "DevTools block: pilot"}' ``` Returns `201` (a new active version is created). **Note** The rule is now fixed. Every later step touches only the group, never the rule. --- ## 3. Expand in batches Each rollout wave: add the next batch of users to the group, then publish only the group with [partial publish](https://pan.dev/prisma-browser/guide/draft-and-publish#partial-publish) so the wave does not carry unrelated draft edits. ```bash # Add the next wave curl -sS -X PUT "$PB_API_BASE/user-groups/$UG_ID" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "users": [ { "userId": "0UR01WAVE2AXXXXXXXXXXXXXXXXXX", "action": "add" }, { "userId": "0UR01WAVE2BXXXXXXXXXXXXXXXXXX", "action": "add" } ] }' # Publish only the group curl -sS -X POST "$PB_API_BASE/configuration-management/draft/partial-publish" \ -H "Authorization: Bearer $PB_TOKEN" \ -H "Content-Type: application/json" \ -d '{"entityIds": ["'"$UG_ID"'"], "description": "DevTools block: wave 2"}' ``` The `PUT` returns the group ID, and the partial-publish publish returns `201`: ```json { "id": "0UG01ROLLOUTXXXXXXXXXXXXXXXXX", "userGroupId": "0UG01ROLLOUTXXXXXXXXXXXXXXXXX" } ``` Repeat on your cadence (hourly, daily) until the group holds everyone in scope. --- ## 4. Converge on one group At the end of the rollout the single group represents your full intended scope. Keep it as the rule's permanent scope; no migration needed. If you piloted with users directly and a group separately, remove the stragglers and let the group be the sole source of truth. **Note** If you ever need to pause or roll back a wave, remove that batch from the group and publish the group again. The rule itself never has to change. --- ## Full script (Python) ```python import os, time, requests base = os.environ["PB_API_BASE"] headers = {"Authorization": f"Bearer {os.environ['PB_TOKEN']}"} rule_id = os.environ["RULE_ID"] waves = [ ["0UR01PILOT1XXXXXXXXXXXXXXXXXX", "0UR01PILOT2XXXXXXXXXXXXXXXXXX"], ["0UR01WAVE2AXXXXXXXXXXXXXXXXXX", "0UR01WAVE2BXXXXXXXXXXXXXXXXXX"], ["0UR01WAVE3AXXXXXXXXXXXXXXXXXX"], ] # 1. Create the group with the first wave ug_id = requests.post(f"{base}/user-groups", headers=headers, json={"name": "Rollout - DevTools block", "userIds": waves[0]}, timeout=30).json()["id"] # 2. Attach to the rule once, publish the pilot requests.patch(f"{base}/policy/security/rules/{rule_id}", headers=headers, json={"scope": {"users": {"addUserGroups": [ug_id]}}}, timeout=30).raise_for_status() requests.post(f"{base}/configuration-management/draft/publish", headers=headers, json={"description": "DevTools block: pilot"}, timeout=30) # 3. Expand wave by wave, publishing only the group for i, wave in enumerate(waves[1:], start=2): requests.put(f"{base}/user-groups/{ug_id}", headers=headers, json={"users": [{"userId": u, "action": "add"} for u in wave]}, timeout=30).raise_for_status() requests.post(f"{base}/configuration-management/draft/partial-publish", headers=headers, json={"entityIds": [ug_id], "description": f"DevTools block: wave {i}"}, timeout=30) time.sleep(0) # replace with your cadence (e.g. one wave per day) ``` --- ## Related - Building blocks: [User groups](https://pan.dev/prisma-browser/guide/user-groups), [Rules](https://pan.dev/prisma-browser/guide/rules) - Concepts: [Partial publish](https://pan.dev/prisma-browser/guide/draft-and-publish#partial-publish), [Draft and publish](https://pan.dev/prisma-browser/guide/draft-and-publish) - Related use cases: [Add or remove users on a rule](https://pan.dev/prisma-browser/guide/manage-users-on-a-rule), [Change a rule's scope](https://pan.dev/prisma-browser/guide/change-a-rule-scope)