Publish a single object (partial publish)
Partial publish is a preview feature. Send x-prisma-browser-preview: partial-publish on every call; without it the request returns 400. See Partial publish draft in the API reference.
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 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. Read Partial publish and Draft and publish first.
Non-policy objects only. Partial publish supports user groups, application groups, applications, device groups, and application tags. Naming a rule or section returns 501 PARTIAL_PUBLISH_POLICY_UNSUPPORTED; for policy changes, use a normal full publish.
The two-call pattern
1. Edit the object
For example, add a user to a group:
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):
{ "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
curl -sS -X POST "$PB_API_BASE/configuration-management/draft/partial-publish" \
-H "Authorization: Bearer $PB_TOKEN" \
-H "x-prisma-browser-preview: partial-publish" \
-H "Content-Type: application/json" \
-d '{ "entityIds": ["'"$UG_ID"'"], "description": "ServiceNow INC0042311: contractor offboarding" }'
Response (201):
{
"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.
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.
curl -sS -X POST "$PB_API_BASE/configuration-management/draft/partial-publish" \
-H "Authorization: Bearer $PB_TOKEN" \
-H "x-prisma-browser-preview: partial-publish" \
-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:
curl -sS "$PB_API_BASE/configuration-management/draft/pending-changes" \
-H "Authorization: Bearer $PB_TOKEN"
Response (200):
{
"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.
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)
import os, requests
base = os.environ["PB_API_BASE"]
headers = {"Authorization": f"Bearer {os.environ['PB_TOKEN']}"}
publish_headers = {**headers, "x-prisma-browser-preview": "partial-publish"}
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=publish_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.errors[] identifies the offending items: field names the request field, index its position, value the offending ID, and error (when present) the reason:
{
"error": {
"code": "NOT_FOUND",
"message": "No pending change found for one or more of the specified entities.",
"timestamp": "2026-08-06T09:15:00Z",
"details": {
"errors": [
{ "index": 0, "field": "entityIds", "error": "no pending change in the current draft", "value": "0UG01TEAMXXXXXXXXXXXXXXXXXXXX" }
]
}
}
}
| HTTP | code | Condition | What to do |
|---|---|---|---|
| 400 | VALIDATION_ERROR | An ID is malformed or duplicated, the list is empty or exceeds 1000 items, the body carries only description, or the x-prisma-browser-preview header is missing | 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.errors[] 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.errors[].ids lists the blockers | Include the blocking IDs and retry, or escalate. Reports only the first conflict found |
| 409 | PUBLISH_IN_PROGRESS | Another publish or a draft reset holds the draft lock. Nothing was published | Wait, then retry |
| 429 | (gateway) | The tenant's request rate limit was exceeded | Retry after the current window; see Errors |
| 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. details.errors[] lists every offender | Publish policy changes with a full publish |
| 501 | PARTIAL_PUBLISH_TYPE_UNSUPPORTED | You named an entity that is not supported in draft mode. details.errors[] lists every offender | Use a full publish |
Two of these deserve explicit handling:
404after an apparently successful write. APUTthat 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.409from 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, Draft and publish
- Building blocks: User groups, Application groups
- Related use cases: Gradual rollout with a user group, Manage an allow / block list
