> ## Documentation Index
> Fetch the complete documentation index at: https://cloudinary.com/documentation/llms.txt
> Use this file to discover all available pages before exploring further.

# Manage roles

This guide describes the Roles and Permissions system. For details on all roles available in the legacy system, see [Role-based permissions](user_provisioning#role_based_permissions).

> **INFO**:
>
> :title=Which permissions system do you have?
> Use the rollout schedule to find out:

> * **Enterprise accounts**: Broad Enterprise migration hasn't started yet. If your team hasn't already been moved with Cloudinary's help, you're still on the legacy system.

> * **Existing free and paid accounts**: Migration starts May 12, 2026.

> * **New free accounts (created since February 2026)**: You may already have the new system.
> You can confirm which permissions system you have. Open **Console Settings** and look for **Role Management**. If it's listed, your account is on Roles and Permissions. If it isn't listed, you're still on the legacy permissions model. 
> ![Global role management](https://cloudinary-res.cloudinary.com/image/upload/f_auto/q_auto/bo_1px_solid_grey/roles_interface.png "thumb: w_800,dpr_2, width:800, with_code:false, with_url:false, popup:true")
> The Roles and Permissions system provides more granular, flexible access control than the legacy system. 

> * For a quick comparison, see [Roles and Permissions vs. legacy](dam_admin_users_groups#roles_and_permissions_vs_legacy). 

> * If your account is being migrated, see [Migrating to Roles and Permissions](permissions_migration) to understand what changes.

## Overview

[permission-ref]: permissions_system_roles_policies#system_policies_list

Cloudinary roles define what principals (users, groups, and API keys) can access and do across your account. Managing roles via the Permissions API allows you to create, update, and delete custom roles programmatically, so you can scale permissions consistently without assigning individual policies one by one.
> **NOTE**: :title=For Free plan customers:

The Permissions API isn't available on the Free plan. You can [manage roles and permissions](dam_admin_permissions) via the [Console](https://console.cloudinary.com/app/settings/role-management) only and assign folder roles to API keys and other principals programmatically via the [Admin API](permissions_assign_roles_api#assign_folder_roles_via_the_admin_api).

On this page, you'll learn how to:

* [Retrieve roles and system policies](#retrieve_roles_and_system_policies)
* [Manage custom roles](#manage_custom_roles)
* [Plan roles effectively, with key considerations in mind](#considerations_for_planning_roles_effectively)

## Base URL and authentication

All Permissions API endpoints on this page use the following base path:

```
https://api.cloudinary.com/v2/accounts/ACCOUNT_ID/permissions
```

Replace **ACCOUNT_ID** with your actual account ID. 

Find your account ID and credentials in the [Account Management Keys](https://console.cloudinary.com/app/settings/account-api-keys) page of the Console Settings.
## Retrieve roles and system policies

Use these endpoints to review available roles and system policies in your account.

### Role details

Use these endpoints to list and inspect both system and custom roles, and to review which system policies each role includes. This is useful when planning which roles you want to assign and figuring out whether you need to create additional custom roles. 

{table:class=no-borders overview}  Endpoint | Use Case |
|----------|----------|
| `GET /roles` | List all roles. You can filter by `management_type=system` or `management_type=custom`.|
| `GET /roles/{role_id}` | View the policies included in a specific role |

*  `GET /roles` **sample response**: Retrieve a list of roles. You'll need the role's `id` to inspect details or assign the role to a principal.

    ```json
    {
      "id": "cld::role::prodenv::master_admin",
      "name": "Master Admin",
      "description": "Fully manage all product environments, including settings, all features across products, and dashboards and reports.",
      "management_type": "system",
      "permission_type": "global",
      "scope_type": "prodenv",
      "created_at": "1719475216",
      "updated_at": "1719475216"
    },
    {
      "id": "upload_manager_12334565",
      "name": "Upload manager",
      "description": "Responsible for managing upload settings and uploading new assets.",
      "management_type": "custom",
      "permission_type": "global",
      "scope_type": "prodenv",
      "created_at": "1719475216",
      "updated_at": "1719475216"
    }
    ```

**Field definitions:**

{table:class=med-1stcol} Field                  | Description                                                                                   |
|------------------------|-----------------------------------------------------------------------------------------------|
| `id`                   | The role's unique identifier. Use this when assigning the role programmatically. System roles use the `cld::role::` prefix. |
| `name`                 | Display name for the role, as shown in the Console.                                           |
| `description`          | Summary of the permission the role grants.                                                  |
| `management_type`      | Predefined roles are `"system"` and roles that you create are `"custom"`.     |
| `permission_type`      | Indicates the level at which the role applies, either `"global"` or `"content"`.                             |
| `scope_type`           | Specifies the scope of the role. Global roles can be `"account"` or `"prodenv"` (product environment). Content roles are always `"prodenv"`. |
| `created_at`, `updated_at` | Timestamps indicating when the role was created and last updated (Unix epoch format).        |

* `GET /role` **sample response**: Retrieves all policies included in a specific role, providing a more detailed view of what that role can do.

    ```json
    {
      "policies": [
        {
          "id": "cld::policy::global::upload_presets::manage",
          "name": "Manage upload settings",
          "description": "View, create, modify, or delete upload settings, such as upload presets, upload mappings, and upload defaults.",
          "scope_type": "prodenv",
          "permission_type": "global",
          "policy_statement": "permit(principal, action, resource is Cloudinary::UploadPreset);\npermit(principal, action, resource is Cloudinary::UploadMapping);\npermit(principal, action == Cloudinary::Action::\"update_settings\", resource is Cloudinary::ProductEnvironment);\npermit(principal, action, resource == Cloudinary::Feature::\"cld::global::upload_settings::manage\");\npermit(principal, action, resource == Cloudinary::Feature::\"cld::global::upload_settings::access\");\n",
          "created_at": "1719475215",
          "updated_at": "1719475215"
        },
        {
          "id": "cld::policy::content::folder::view_download",
          "name": "View all assets",
          "description": "View all assets in the folder and its nested subfolders.",
          "scope_type": "prodenv",
          "permission_type": "content",
          "policy_statement": "permit(principal, action == Cloudinary::Action::\"read\", resource is Cloudinary::Folder) when { resource.ancestor_ids.contains(\"<folder_id>\") };\npermit(principal, action == Cloudinary::Action::\"read\", resource is Cloudinary::Asset) when { resource.ancestor_ids.contains(\"<folder_id>\") };\n",
          "policy_parameters": [
            "folder_id"
          ],
          "created_at": "1719475216",
          "updated_at": "1719475216"
        }
      ],
      "id": "upload_manager_12334565",
      "name": "Upload manager",
      "description": "Responsible for managing upload settings and uploading new assets.",
      "management_type": "custom",
      "permission_type": "global",
      "scope_type": "prodenv",
      "created_at": "1719475216",
      "updated_at": "1719475216"
    }
    ```

For a list of all system roles, see [System roles list](permissions_system_roles_policies#system_roles_list).

### System policy details

You can retrieve a comprehensive list of system policies using `GET /policies/system`.

The policy object returned by `GET /policies/system` follows the same structure as the policies listed in the policies array returned by `GET /roles/{role_id}`. This shared structure makes it easier to reference system policies when creating or reviewing custom roles.

**Each policy includes the following fields:**

{table:class=med-1stcol} Field                  | Description                                                                                         |
|------------------------|-----------------------------------------------------------------------------------------------------|
| `id`                   | The unique `system_policy_id` used when referencing this policy in roles.                           |
| `name`                 | The display name of the policy, shown in the Console.                                               |
| `description`          | A summary of what the policy allows, including relevant UI and API capabilities.                    |
| `scope_type`           | Defines the scope at which the policy applies. Typically `"prodenv"` for product environments.      |
| `permission_type`      | Indicates whether the policy applies globally or in a more granular context (`"global"` or `"content"`).     |
| `policy_statement`     | The underlying Cedar policy expression that defines the permission logic. For more information, see [Understanding the policy_statement](permissions_manage_roles_api#understanding_the_policy_statement). |
| `policy_parameters`    | Relevant only if the `permission_type` is `content`. Specifies whether the policy applies to a `folder_id` or a `collection_id`. | 
| `created_at`, `updated_at` | Unix timestamps indicating when the policy was created and last updated.                   |

For a list of all system policies, see [System policies list](permissions_system_roles_policies#system_policies_list).

### Understanding the policy_statement

Each system policy includes a `policy_statement` field, which defines the actual access rule in Cedar, an expressive, logic-based language designed for fine-grained authorization. This statement determines what action the policy allows, and on which resources, and under what conditions.

#### Structure and purpose

The `policy_statement` defines the core logic of a policy. It contains one or more `permit` rules, each made up of the following components:

* **principal**: The actor (such as a user, group, or API key) that receives the permission.
  
* **action**: The operation the policy allows (e.g., `read`, `update_settings`, `delete`).

* **resource**: The object the action targets, typically a content instance or feature (e.g., `Cloudinary::Asset`, `Cloudinary::Folder`, `Cloudinary::Feature::"cld::global::ml::access"`).

* **when clause** (relevant for folder and collection roles only): A condition that determines when the permission applies (e.g., the asset exists within a certain folder).

You define both the principal and the folder or collection ID (for the `when` clauses) when you assign the role. The assignment implicitly specifies the principal, and you pass the folder or collection through the `policy_parameters` field.

#### Example

Here's a `policy_statement` from a policy that allows viewing assets within a specific folder and its subfolders:

```
permit(principal, action == Cloudinary::Action::"read", resource is Cloudinary::Folder)
  when { resource.ancestor_ids.contains("<folder_id>") };

permit(principal, action == Cloudinary::Action::"read", resource is Cloudinary::Asset)
  when { resource.ancestor_ids.contains("<folder_id>") };
```

* **This statement means:** The principal can read folders or assets, but only if they exist within the specified folder (`<folder_id>`).

* **Where it appears:**
  * When calling `GET /policies/system`, each returned system policy includes a `policy_statement`.
  * When calling `GET /roles/{role_id}`, each policy listed under the role also includes its `policy_statement`.

This consistency allows you to clearly see and reason about the exact behavior of each policy, whether you're applying it directly or as part of a role.

## Manage custom roles

Use these endpoints to manage custom roles for your account or product environments.

### Create a custom role

Use [POST /roles/custom](permissions_api#tag/roles/POST/v2/accounts/{account_id}/permissions/roles) to define a role and include the system policies it should grant.

You must specify:

* `permission_type`: `global` or `content`
* `scope_type`: `account` or `prodenv`
* One or more `system_policy_ids`
* (Optional) `id`, `name`, and `description`

Global roles apply to all content or account-level features. Content roles apply to specific folders or collections and require `policy_parameters` when assigning.

#### Example 1: Create a global role to manage uploads

```json
{
  "id": "upload_manager_12334565",
  "permission_type": "global",
  "scope_type": "prodenv",
  "name": "Upload manager",
  "description": "Responsible for uploading new assets. Can manage upload presets.",
  "system_policy_ids": [
    "cld::global::upload_presets::manage", 
    "cld::global::assets_and_folders::create"
  ]
}
```

#### Example 2: Create a content role for folder access

```json
{
  "id": "marketing_folder_editor",
  "permission_type": "content",
  "scope_type": "prodenv",
  "name": "Marketing Folder Editor",
  "description": "Can view and update assets in marketing folders.",
  "system_policy_ids": [
    "cld::content::folder::view_assets_subfolders",
    "cld::content::folder::update::assets"
  ]
}
```

#### Example 3: Create an account-level role for admin tasks

```json
{
  "id": "account_admin_settings",
  "permission_type": "global",
  "scope_type": "account",
  "name": "Account Settings Manager",
  "description": "Manage users and account security settings.",
  "system_policy_ids": [
    "cld::global::users_and_groups::manage",
    "cld::global::account_security::manage"
  ]
}
```

> **NOTE**: If `scope_type` is prodenv, you must specify the product environments when assigning the role. You can assign to "all" environments or a specific list of product environment IDs.

### Update or delete a custom role

* Update a role: [PUT /roles/custom/{role_key}](permissions_api#tag/roles/PUT/v2/accounts/{account_id}/permissions/roles/{role_id})
  You can update the role’s name, description, or system policies. 

* Delete a role: [DELETE /roles/custom/{role_key}](permissions_api#tag/roles/DELETE/v2/accounts/{account_id}/permissions/roles/{role_id})
  You can only delete roles that currently assigned.

## Considerations for planning roles effectively

### Assignment considerations

You can assign roles to groups, users, product environment API keys, and account management keys.

All role types can be assigned to any of these principals. However, some assignments may have no practical effect, depending on scope or usage context:

* **Scope matters**: Assigning an account-level role to a product environment API key has no effect.For example, granting a product environment API key permission to provision users via the Provisioning API won’t work. Those permissions are only relevant at the account level.

* **UI-based permissions**: Roles that grant access to UI areas, such as viewing dashboards or reports, don’t apply to API keys, since only users (not API keys) can interact with the Console. **Exception:** If you’re using an API key to authenticate an integration that embeds the Media Library Widget, you must assign a role that grants access to the Media Library. For more information, see [Integrations](#integrations).

See the full list of [system permission policies](permissions_system_roles_policies#system_policies_list) for details on which permissions are available by scope and applicable to each entity type.

### Integrations

When using Cloudinary [integrations](integrations), you authenticate them with product environment [API keys](https://console.cloudinary.com/app/settings/api-keys). These API keys must receive the necessary permissions explicitly, via roles or custom policies.

**When setting roles and permissions for API keys used to access integrations:**

* Avoid giving broad roles like Master Admin to an integration’s API key. It opens more access than what the integration likely needs.

* Instead, understand what the integration needs to do. Then assign an appropriate role.

* For integrations that use the Media Library Widget, the API key needs specific permissions to access content. Consider one of the following options:
  * **Assign global roles**
      * **Use system roles**: Use a role like **Media Library User** (`cld::role::prodenv::ml_user`) or **Media Library Admin** (`cld::role::prodenv::ml_admin`), if it matches the required access level.
      * **Use custom roles**: Assign a custom global role that includes the **Access the Media Library** permission (`cld::policy::global::ml::access`) as well as *global* folder permissions (e.g., view, upload, delete).
  * **Assign content roles**
      * Assign system or custom *folder* or *collection* roles for targeted access to specific instances.
      > **NOTE**:
>
> When assigning content roles, you must also assign a global role that grants the **Access the Media Library** permission (`cld::policy::global::ml::access`).

Assigning the right scope and level of access helps integrations function properly without compromising security or best practices.

### Actions that require multiple permissions

Some tasks require multiple permissions to enable. If a user doesn't have all the permissions listed for that action, they won't be able to perform it. Make sure the roles you create contain all the listed permissions to perform these actions:

{table:class=no-borders overview} Action | Required System Policies |
|--------|--------------------------|
| **Use Moderation tab to moderate assets** | `cld::policy::global::moderation_queue::access` (Access the Moderation page)  `cld::policy::global::assets::moderate` (Moderate all assets) **OR**  `cld::policy::content::folder::moderate` (Moderate assets)  *For UI access:* `cld::policy::global::ml::access` (Access to the Media Library)`cld::policy::global::assets_and_folders::view`(View all folders and assets) **OR** `cld::policy::content::folder::view` (View assets) |
| **Add assets to (non-dynamic) collections** |  `cld::policy::global::collections:update` (Manage all (non-dynamic) collections) **OR** `cld::policy::content::collection::add_assets` (Add assets) `cld::policy::global::assets_and_folders::view`(View all folders and assets) **OR** `cld::policy::content::folder::view` (View assets) *For UI access (collections are only available in the UI):* `cld::policy::global::ml::access` (Access to the Media Library) `cld::policy::global::collection::view` (View all (non-dynamic) collections)  **OR** `cld::policy::content::collection::view` (View collection) |
| **Remove assets from (non-dynamic) collections** | `cld::policy::global::collections::manage` (Manage all (non-dynamic) collections) **OR** `cld::policy::content::collection::remove_assets` (Remove assets)`cld::policy::global::assets_and_folders::view`(View all folders and assets) **OR** `cld::policy::content::folder::view` (View assets) *For UI access (collections are only available in the UI):* `cld::policy::global::ml::access` (Access to the Media Library) `cld::policy::global::collection::view` (View all (non-dynamic) collections)  **OR** `cld::policy::content::collection::view` (View collection) |
| **Relate one asset to another** | `cld::policy::global::asset_relation::create` (Relate assets) *For UI access :* `cld::policy::global::ml::access` (Access to the Media Library) `cld::policy::global::assets_and_folders::view` (View all folders and assets) **OR** `cld::policy::content::folder::view` (View assets) |
| **Move assets between folders** | `cld::policy::content::folder::move_assets` (Move assets out of the source folder)   `cld::policy::content::folder::add_assets` (Move assets into the destination folder)  *For UI access:* `cld::policy::global::ml::access` (Access to the Media Library) `cld::policy::content::folder::view` (View all folders and assets) **OR** `cld::policy::global::assets_and_folders::view` (View assets) |
| **Start creative approval proofs** | `cld::policy::global::creative_approval_proofs::create` (Start creative approval proofs)   *For UI access (proofs are only available in the UI):* `cld::policy::global::ml::access` (Access to the Media Library)`cld::policy::content::folder::view` (View all folders and assets) **OR** `cld::policy::global::assets_and_folders::view` (View assets)  |
| **Manage public links for assets and collections** | `cld::policy::global::public_links::manage` (Manage  public links)  *For UI access:* `cld::policy::global::ml::access` (Access to the Media Library) `cld::policy::content::folder::view` (View all folders and assets) **OR** `cld::policy::global::assets_and_folders::view` (View assets) **OR** `cld::policy::content::collection::view` (View collection) |
| **Move folders** | `cld::policy::content::folder::move` (Move folder)  `cld::policy::content::folder::move_assets` *Required in fixed-folder mode*  *For UI access:* `cld::policy::global::ml::access` (Access to the Media Library)`cld::policy::global::assets_and_folders::view`(View all folders and assets) **OR** `cld::policy::content::folder::view` (View assets) |

> **See also**:
>
> * [Role-based permissions](permissions_overview): An overview of Cloudinary's role-based permissions solution

> * [Role management in the Console](permissions_manage_roles_ui): UI-based role management

> * [Assign roles](permissions_assign_roles_api): How to assign roles via API

> * [System role and policy reference](permissions_system_roles_policies): A list of all system roles and system permission polices provided by Cloudinary

> * [Permissions API reference](permissions_api): Full list of endpoints and schemas

> * [Define custom policies](permissions_custom_policies): Create and apply policies outside of roles