> ## 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.

# Folder modes in integrations


All Cloudinary product environments are set up to work with either **dynamic folders** or **fixed folders**. The majority of supported Cloudinary features are identical for both modes, but each of these modes also have a variety of different behaviors, supported methods and parameters, response keys, webhook notifications, upload preset settings and more. Therefore, you must make sure all code in your integration will work as expected for both modes:

* In **fixed folder** mode, the folder and asset name are a direct reflection of the asset's **public_id** full path, and thus control the delivery URL path and file name. In fixed folder mode, moving the asset to another folder or changing the folder or asset 'name' shown in the Media Library interface, also modifies the asset's URL, and if not done carefully, risks breaking production content.
* In **dynamic folder** mode, moving assets between **asset folders** and renaming those folders, or even moving entire folders and the assets currently "in" them to a new path, DO NOT affect the asset's **public ID** value or delivery URL path. Dynamic folder mode also introduces the concept of a **display name**, which, like asset folders, can be freely modified without affecting the **public ID** and delivery URL. Dynamic folders provide Cloudinary users the flexibility to organize and manage media assets with high performance and without risk of breaking production content. 

This page describes the ways that the [dynamic folders](folder_modes) mode impacts your Cloudinary integration. It assumes you're familiar with both the Cloudinary Console and API capabilities that were available prior to this new option.

## Checking the folder mode (Config method)

If you need to run different code depending on whether the product environment is dynamic or fixed folder mode, you can use the [config](admin_api#config) method of the Admin API to return the current settings, including information on the `folder_mode` (dynamic or fixed).

It's recommended to run this method immediately after a product environment connects to your integration, and then use the returned value as the basis for all other code that can be impacted by the folder mode.
For example, to return the product environment configuration including settings info:

```multi
|python
cloudinary.api.config(settings=True)

|cli
cld admin config settings=true

|curl
curl https://<API_KEY>:<API_SECRET>@api.cloudinary.com/v1_1/<CLOUD_NAME>/config?settings=true
```

The response contains an object with detailed product environment information.

```json
{ 
  "cloud_name": "demo",
  "created_at": "2023-05-08T08:20:11Z",
  "settings": {
    "folder_mode": "dynamic"
  },
}
```

## Important considerations

If you are integrating with Cloudinary and your integration has any code that relates to public ID path or folder structure in any way, then there are a variety of considerations you should be aware of, and you must ensure that your integration will fully support both fixed folder **AND** dynamic folder product environments.

Take note of the following important considerations when integrating with **dynamic and fixed folder** product environments.

### Uploading assets

Dynamic folder mode introduces the concept of `display_name` and `asset_folder` that can be freely modified without affecting the `public_id` and delivery URL.

When uploading files, the following parameters can also be defined:

* `public_id_prefix` to prepend a string to the `public_id` with a forward slash. This prefix can be useful to provide context and improve the SEO of an asset's filename in the delivery URL, but the value does not impact the folder location where the asset is placed. This value is NOT returned in the response as it's just an optional technique for building the public ID value from two separate parts: the returned public ID in the response represents the full path.
* `use_filename_as_display_name` to automatically assign the filename of the uploaded asset as the asset's display name.  If desired, this option can be used in addition to `use_filename`, which controls the last segment of the public ID.`

#### The 'folder' parameter

The `folder` parameter of the [Upload](image_upload_api_reference#upload) method should not be used in dynamic folder mode. It is officially supported for backward compatibility and is the equivalent of setting both `asset_folder` and `public_id_prefix` to the same value (if those values are not explicitly passed). 

However, keep in mind that many dynamic mode customers may have their assets organized in a deep folder structure, while their public IDs may have no slashes in them (this is the default behavior for all dynamic mode accounts) or their public ID paths may intentionally use public ID paths intended for SEO purposes that are completely different than their folder structure.  

If you pass the `folder` parameter when uploading assets to a dynamic folder product environment, it would apply a path to the public ID that the customer may not expect. 
  
#### Public IDs with slashes

When passing a `public_id` value that includes slashes (for example, `animals/cats/tiger`), the entire passed value continues to be the `public_id`, but in dynamic folder mode, the slashes do not define the path where the asset is placed. If you don't also pass an `asset_folder` value, the asset will be displayed in the root folder.
    
If you want the asset to be displayed in a folder that matches the `public_id` path, then also include an `asset_folder` parameter in your upload call with the same path. Alternatively, pass the `public_id` value without a path, and instead, specify the path in the `asset_folder` parameter, while also using the `use_asset_folder_as_public_id_prefix:true` option in your upload call or upload preset to ensure the two paths match.  

However, keep in mind that this only synchronizes between the `public_id` path and the **initial** `asset_folder` path. If a user later moves the asset to another asset folder, the public ID path won't change to reflect the asset's new location. This avoids changing the public ID and breaking delivery URLs in production.

#### Default upload behavior

In dynamic folder mode, if the `display_name` is defined neither explicitly nor via the `use_filename_as_display_name` option, the initial display name will be the same as the public ID (or the last segment of the public ID, if the public ID includes slashes (/) ). If an upload (or upload preset) doesn't define an `asset_folder` parameter (or `folder` parameter) the asset will be uploaded to the root folder, even if the public ID includes a path with slashes. 

### Upload presets

In dynamic folder mode, the `use_asset_folder_as_public_id_prefix`  optional parameter is available in the create/update [upload_presets](admin_api#upload_presets) method of the Admin API and determines whether to automatically apply the path specified in the `asset_folder` parameter as a prefix to the `public_id` value. This ensures that the public ID path will always match the initial asset folder and can help to retain the behavior that previously existed in fixed folder mode. 

As with uploading assets, the `asset_folder`, `public_id_prefix`,  `use_filename_as_display_name`, as well as the `use_asset_folder_as_public_id_prefix` parameter can also be set via the [upload_presets](admin_api#upload_presets) method of the Admin API.

### Upload mappings

In dynamic folder mode with [upload mappings](admin_api#upload_mappings), the `folder` parameter continues to represent both the asset folder that will be created at the root level when an asset is [lazily uploaded](migration#lazy_migration_with_auto_upload) upon delivery, and also the first segment of the public ID of each asset that gets uploaded this way.
  
As with any other asset folder, a folder created through this process can be renamed without breaking the public ID of the uploaded asset, but if another asset is lazily uploaded to the same upload mapping folder, the defined upload mapping parent folder will be re-created.

### Updating assets

In dynamic folder mode, the `display_name` and `asset_folder` can also be set for existing assets using the [Explicit](image_upload_api_reference#explicit) method. These parameters are also available in the [Update](admin_api#update_details_of_an_existing_resource) method of the Admin API.

#### Renaming assets

If your integration includes management functionality, keep in mind the difference between a public ID and a display name, especially when updating assets. The [rename](image_upload_api_reference#rename) method changes the public ID and resulting delivery URL, so to only change the display name use the [explicit](image_upload_api_reference#explicit) or [update](admin_api#update_details_of_an_existing_resource) methods.

### Folders methods

The [folders](admin_api#folders) methods of the Admin API reflect the folder mode:

* In fixed folder mode, the folders methods relate to public ID paths. The public ID paths of assets are the same as the folder names or path structure where those assets are stored.
* In dynamic folder mode, the folders methods relate to asset folders and not to public ID paths. The public ID paths of assets are not necessarily the same as the asset folder names or path structure where those assets are located.

Make sure you don't have any code that use these methods in dynamic folder mode and then assume that the folder paths in the response relate to public ID paths.

In dynamic folder mode, you also have access to the following methods:

* [Get resources by asset folder](admin_api#get_resources_by_asset_folder) method that returns all assets located directly in a specified asset folder, regardless of the public ID paths of those assets. 
* [Update folder](admin_api#update_folder) method that updates the full path of an existing asset folder. 

### Asset IDs

In both fixed and dynamic folder modes, you also have access to methods that refer to the asset ID, an immutable identifier, regardless of resource type, type, public ID, display name, or asset folder:

* [Get resources by asset IDs](admin_api#get_resources_by_asset_ids): Returns the assets with the specified asset IDs. 
* [Get a single resource by asset ID](admin_api#get_details_of_a_single_resource_by_asset_id): Returns the asset with the specified asset ID, including details on all derived assets from this asset. 

  
### Webhook notifications

In dynamic folder mode, changes to the display name and asset folder (moving assets) via the Console or API trigger webhook [notifications](notifications#notification_response_examples). New events include:

* [resource_display_name_changed](notifications#change_display_name)
* [move](notifications#move_between_asset_folders)
* [move_asset_folder](notifications#move_an_asset_folder)
* [delete_asset_folder](notifications#delete_an_asset_folder)

The `create_folder`, `create_asset_folder`, `delete`, `rename` and `upload` events are also still relevant for dynamic folder mode, and the notification payloads now additionally include the asset's `display_name` and `asset_folder`.

### API responses

In dynamic folder mode, the API methods now additionally include the asset's `display_name` and `asset_folder` as part of the response.

## Integration scenario check list

The following integration use cases include important information to take into consideration when integrating with both dynamic and fixed folder modes.

### Syncing asset and folder structure with Cloudinary

The biggest change will be if your integration has relied on using the URL path (public ID) as an indicator of folder structure or asset location. This is only true for product environments using fixed folder mode. In dynamic folder mode, users can move assets between asset folders and rename those folders without affecting the asset's public ID value and delivery URL path. 

Dynamic folder mode introduces the concept of `display_name` and `asset_folder` that can be freely modified without affecting the `public_id` and delivery URL. 

Use the [folders](admin_api#folders) method to get lists of existing folders instead of relying on URL paths and then use the [Get resources by asset folder](admin_api#get_resources_by_asset_folder) method to retrieve the list of assets in each folder.

If you use webhooks to mirror the Cloudinary Media Library folder structure and assets, the [notifications](notifications) triggered for various operations and their responses are slightly different in dynamic and fixed folder modes.

### Searching for assets

Search expressions that include the `folder` field are only useful for searching within fixed folder mode product environments. In dynamic folder mode, the search expressions should use the `asset_folder` field.  You can also search for assets by their `asset_id`.

### Media Library Widget

If your integration includes the [Media Library Widget](media_library_widget), take note of the changes to the [JSON response](media_library_widget#sample_json_response_for_a_transformed_image), especially if you are syncing using [webhook notifications](notifications).

### Set asset folder based on public ID 

If your integration sets the folder based on the `public_id` parameter, where the portion of the public ID that sets the folder is the prefix and is separated by a slash, you can use the following script together with the [eval](upload_parameters#eval_modify_upload_options_before_upload) upload parameter to set the `asset_folder` and `display_name` in dynamic folder mode:

```javascript
if (resource_info.public_id) {
    const publicIdParts = resource_info.public_id.split('/');
    if (publicIdParts.length === 1) {
        upload_options['asset_folder'] = '';
        upload_options['display_name'] = resource_info.public_id;
    } else {
        const suffix = publicIdParts.pop();
        upload_options['asset_folder'] = publicIdParts.join('/');
        upload_options['display_name'] = suffix;
    }
}
```

``` multi
|ruby 
Cloudinary::Uploader.upload("user_photo.jpg", 
  eval: "if (resource_info.public_id) {const publicIdParts = resource_info.public_id.split('/');if (publicIdParts.length === 1) {upload_options['asset_folder'] = '';upload_options['display_name'] = resource_info.public_id;} else {const suffix = publicIdParts.pop();upload_options['asset_folder'] = publicIdParts.join('/');upload_options['display_name'] = suffix;}}")

|php_2
$cloudinary->uploadApi()->upload("user_photo.jpg",[ 
    "eval" => "if (resource_info.public_id) {const publicIdParts = resource_info.public_id.split('/');if (publicIdParts.length === 1) {upload_options['asset_folder'] = '';upload_options['display_name'] = resource_info.public_id;} else {const suffix = publicIdParts.pop();upload_options['asset_folder'] = publicIdParts.join('/');upload_options['display_name'] = suffix;}}" ]);

|nodejs
cloudinary.v2.uploader
.upload("user_photo.jpg",
  { eval: "if (resource_info.public_id) {const publicIdParts = resource_info.public_id.split('/');if (publicIdParts.length === 1) {upload_options['asset_folder'] = '';upload_options['display_name'] = resource_info.public_id;} else {const suffix = publicIdParts.pop();upload_options['asset_folder'] = publicIdParts.join('/');upload_options['display_name'] = suffix;}}" })
.then(result=>console.log(result)); 

|python
cloudinary.uploader.upload("user_photo.jpg",
  eval = "if (resource_info.public_id) {const publicIdParts = resource_info.public_id.split('/');if (publicIdParts.length === 1) {upload_options['asset_folder'] = '';upload_options['display_name'] = resource_info.public_id;} else {const suffix = publicIdParts.pop();upload_options['asset_folder'] = publicIdParts.join('/');upload_options['display_name'] = suffix;}}")

|java
cloudinary.uploader().upload("user_photo.jpg", 
  Cloudinary.asMap(
    "eval", "if (resource_info.public_id) {const publicIdParts = resource_info.public_id.split('/');if (publicIdParts.length === 1) {upload_options['asset_folder'] = '';upload_options['display_name'] = resource_info.public_id;} else {const suffix = publicIdParts.pop();upload_options['asset_folder'] = publicIdParts.join('/');upload_options['display_name'] = suffix;}}"));

|csharp
var uploadParams = new ImageUploadParams(){
  File = new FileDescription(@"user_photo.jpg"),
  Eval = "if (resource_info.public_id) {const publicIdParts = resource_info.public_id.split('/');if (publicIdParts.length === 1) {upload_options['asset_folder'] = '';upload_options['display_name'] = resource_info.public_id;} else {const suffix = publicIdParts.pop();upload_options['asset_folder'] = publicIdParts.join('/');upload_options['display_name'] = suffix;}}"};
var uploadResult = cloudinary.Upload(uploadParams); 

|go
resp, err := cld.Upload.Upload(ctx, "user_photo.jpg", uploader.UploadParams{
	Eval:            "if (resource_info.public_id) {const publicIdParts = resource_info.public_id.split('/');if (publicIdParts.length === 1) {upload_options['asset_folder'] = '';upload_options['display_name'] = resource_info.public_id;} else {const suffix = publicIdParts.pop();upload_options['asset_folder'] = publicIdParts.join('/');upload_options['display_name'] = suffix;}}"})


|android
MediaManager.get().upload("user_photo.jpg")
  .option("eval", "if (resource_info.public_id) {const publicIdParts = resource_info.public_id.split('/');if (publicIdParts.length === 1) {upload_options['asset_folder'] = '';upload_options['display_name'] = resource_info.public_id;} else {const suffix = publicIdParts.pop();upload_options['asset_folder'] = publicIdParts.join('/');upload_options['display_name'] = suffix;}}").dispatch();

|swift
let params = CLDUploadRequestParams()
  .setEval("if (resource_info.public_id) {const publicIdParts = resource_info.public_id.split('/');if (publicIdParts.length === 1) {upload_options['asset_folder'] = '';upload_options['display_name'] = resource_info.public_id;} else {const suffix = publicIdParts.pop();upload_options['asset_folder'] = publicIdParts.join('/');upload_options['display_name'] = suffix;}}")
var mySig = MyFunction(params)  // your own function that returns a signature generated on your backend
params.setSignature(CLDSignature(signature: mySig.signature, timestamp: mySig.timestamp))
let request = cloudinary.createUploader().signedUpload(
  url: "user_photo.jpg", params: params) 

|cli
cld uploader upload user_photo.jpg eval 'if (resource_info.public_id) {const publicIdParts = resource_info.public_id.split('/');if (publicIdParts.length === 1) {upload_options['asset_folder'] = '';upload_options['display_name'] = resource_info.public_id;} else {const suffix = publicIdParts.pop();upload_options['asset_folder'] = publicIdParts.join('/');upload_options['display_name'] = suffix;}}'

|curl
curl https://api.cloudinary.com/v1_1/demo/image/upload -X POST --data 'file=user_photo.jpg&eval=\"if (resource_info.public_id) {const publicIdParts = resource_info.public_id.split(\'/\');if (publicIdParts.length === 1) {upload_options[\'asset_folder\'] = \'\';upload_options[\'display_name\'] = resource_info.public_id;} else {const suffix = publicIdParts.pop();upload_options[\'asset_folder\'] = publicIdParts.join(\'/\');upload_options[\'display_name\'] = suffix;}}"&timestamp=173719931&api_key=436464676&signature=a788d68f86a6f868af'
```
