---
title: "Remove role assignments"
method: DELETE
path: "/role-assignments"
tags: ["role-assignments"]
---

# Remove role assignments

`DELETE /role-assignments`

## Request

- Base URL: `https://api.opal.dev/v1`
- URL: `https://api.opal.dev/v1/role-assignments`
- Auth: HTTP bearer

Revoke one or more role assignments. Each entry identifies a specific
stored grant by the 5-tuple `(entity_id, entity_type, principal_id,
principal_type, access_level_remote_id)` — see the field description
on `access_level_remote_id` for when it must be supplied and when it
must be omitted. The call is idempotent: an entry that does not match
any stored grant is silently skipped and absent from `entries` in the
response. Caller must have `EDIT_ASSIGNMENTS` on each entity.

## Request body

- RemoveRoleAssignmentsRequest: Request body for `DELETE /role-assignments`.
  - `role_assignments` RemoveRoleAssignmentInput[], required
    - `entity_id` string, uuid, required: The ID of the entity (resource or group) to revoke access from.
    - `entity_type` 'GROUP' | 'RESOURCE' | 'USER', required: The type of an entity.
    - `principal_id` string, uuid, required: The ID of the principal (user, group, or resource) losing access.
    - `principal_type` 'GROUP' | 'RESOURCE' | 'USER', required: The type of an entity.
    - `access_level_remote_id` string, nullable: Remote ID of the specific access-level grant to revoke. The lookup is exact-match on this value against stored grants. Required when the entity has access levels defined (the server returns 422 if omitted or sent as `null`/empty for such an entity). Must be omitted or `null` for entities with no access-level catalog (group memberships and resources that have no defined access levels); supplying a value in that case also returns 422. These rules mirror those of `POST /v1/role-assignments`. A specified value that does not match any stored grant for this `(entity_id, principal_id)` pair is a successful idempotent no-op: HTTP 200 is returned and the entry is absent from `entries`.

## Response `200`

The role assignments were revoked (or did not exist; see idempotency note).

- RemoveRoleAssignmentsResponse: Response body for `DELETE /role-assignments`.
  - `task_ids` string[], required: Opaque identifiers for the push tasks enqueued to propagate the revocation to the end system. Encoded identically to the `PushTaskId` scalar in the GraphQL API (base64 of the underlying protobuf). One ID per deferred revocation (today, resource/user grants are always deferred); empty when every revocation completed inline (resource/group, group/user, group/group).
  - `entries` RemoveRoleAssignmentsEntry[], required: One entry per role assignment that was removed inline. Shorter than the request when some assignments did not exist (idempotent), and empty when every revocation in the batch was deferred to an async propagation task — in that case `task_ids` carries one ID per deferred revocation and `entries` is empty. Resource/user grants are currently always deferred; the other `(entity, principal)` combinations complete inline and populate `entries`.
    - `role_assignment` RoleAssignment, required: A role assignment representing access granted to a principal for an entity.
      - `id` string, uuid, required: Unique identifier for the role assignment.
      - `entity_id` string, uuid, required: The ID of the entity (resource or group) being accessed.
      - `entity_type` 'GROUP' | 'RESOURCE' | 'USER', required: The type of an entity.
      - `principal_id` string, uuid, required: The ID of the principal being granted access.
      - `principal_type` 'GROUP' | 'RESOURCE' | 'USER', required: The type of an entity.
      - `access_level` ResourceAccessLevel: # Access Level Object ### Description The `AccessLevel` object is used to represent the level of access that a principal has. The "default" access level is a `AccessLevel` object whose fields are all empty strings. ### Usage Example View the `AccessLevel` of a resource/user or resource/group pair to see the level of access granted to the resource.
        - `access_level_name` string, required: The human-readable name of the access level.
        - `access_level_remote_id` string, required: The machine-readable identifier of the access level.
      - `expiration` string, date-time, nullable: When this access expires. Null when the grant is indefinite.
      - `granted_at` string, date-time, nullable: When this access was first granted.

## Other responses

- `400`: The request body was malformed or failed schema validation.
- `403`: Caller lacks `EDIT_ASSIGNMENTS` on one or more entities.
- `422`: Business validation rejected the request.

## Changes

- **2026-10-28** `c54b1c07fcdb`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/opal/apis/opal-api/changes/role-assignments/delete.md)

---

[API](https://skmtc.dev/opal/apis/opal-api.md) - [All operations](https://skmtc.dev/opal/apis/opal-api/llms.txt) - [OpenAPI document](https://skmtc.dev/opal/apis/opal-api/revisions/e7877ea96eab?raw)
