Box Developer Documentation

Join BoxWorks 2024 to discover what's possible with content and AI!

Register now!

Update metadata on an item

Update metadata on an item

Updating the metadata applied to a file or folder can be done by using the item's id, the template's templateKey and scope, and a set of JSON operations to manipulate the data on the template instance.

Update metadata on a file

To update the metadata to a file, call the PUT /files/:file_id/metadata/:scope/:templateKey API endpoint with the file's file_id, the template's scope and templateKey, set of JSON operations to manipulate the data on the template instance.

cURL
curl -i -X PUT "https://api.box.com/2.0/files/12345/metadata/enterprise_27335/blueprintTemplate" \
     -H "authorization: Bearer <ACCESS_TOKEN>" \
     -H "content-type: application/json-patch+json" \
     -d '[
        {
          "op": "test",
          "path": "/competitiveDocument",
          "value": "no"
        },
        {
          "op": "remove",
          "path": "/competitiveDocument"
        },
        {
          "op": "test",
          "path": "/status",
          "value": "active"
        },
        {
          "op": "replace",
          "path": "/status",
          "value": "inactive"
        },
        {
          "op": "test",
          "path": "/author",
          "value": "Jones"
        },
        {
          "op": "copy",
          "from": "/author",
          "path": "/editor"
        },
        {
          "op": "test",
          "path": "/currentState",
          "value": "proposal"
        },
        {
          "op": "move",
          "from": "/currentState",
          "path": "/previousState"
        },
        {
          "op": "add",
          "path": "/currentState",
          "value": "reviewed"
        }
      ]'
TypeScript Gen
await client.fileMetadata.updateFileMetadataById(
  file.id,
  'global' as UpdateFileMetadataByIdScope,
  'properties',
  [
    {
      op: 'replace' as UpdateFileMetadataByIdRequestBodyOpField,
      path: '/abc',
      value: newValue,
    } satisfies UpdateFileMetadataByIdRequestBody,
  ],
);
Python Gen
client.file_metadata.update_file_metadata_by_id(
    file.id,
    UpdateFileMetadataByIdScope.GLOBAL.value,
    "properties",
    [
        UpdateFileMetadataByIdRequestBody(
            op=UpdateFileMetadataByIdRequestBodyOpField.REPLACE.value,
            path="/abc",
            value=new_value,
        )
    ],
)
.NET Gen
await client.FileMetadata.UpdateFileMetadataByIdAsync(fileId: file.Id, scope: UpdateFileMetadataByIdScope.Global, templateKey: "properties", requestBody: Array.AsReadOnly(new [] {new UpdateFileMetadataByIdRequestBody() { Op = UpdateFileMetadataByIdRequestBodyOpField.Replace, Path = "/abc", Value = newValue }}));
Swift Gen (Beta)
try await client.fileMetadata.updateFileMetadataById(fileId: file.id, scope: UpdateFileMetadataByIdScope.global, templateKey: "properties", requestBody: [UpdateFileMetadataByIdRequestBody(op: UpdateFileMetadataByIdRequestBodyOpField.replace, path: "/abc", value: newValue)])
Java
BoxFile file = new BoxFile(api, "id");
file.updateMetadata(new Metadata("templateScope", "templateKey").add("/foo", "bar"));
Python
file_obj = client.file(file_id='11111')
file_metadata = file_obj.metadata(scope='enterprise', template='myMetadata')

updates = file_metadata.start_update()
updates.add('/foo', 'bar')
updates.update('/baz', 'murp', old_value='quux')  # Ensure the old value was "quux" before updating to "murp"

updated_metadata = file_metadata.update(updates)
print('Updated metadata on file!')
print(f'foo is now {updated_metadata["foo"]} and baz is now {updated_metadata["baz"]}')
.NET
var updates = new List<BoxMetadataUpdate>()
{
    new BoxMetadataUpdate()
    {
        Op = MetadataUpdateOp.test,
        Path = "/competitiveDocument",
        Value = "no"
    },
    new BoxMetadataUpdate()
    {
        Op = MetadataUpdateOp.remove,
        Path = "/competitiveDocument"
    },
    new BoxMetadataUpdate()
    {
        Op = MetadataUpdateOp.test,
        Path = "/status",
        Value = "active"
    },
    new BoxMetadataUpdate()
    {
        Op = MetadataUpdateOp.replace,
        Path = "/competitiveDocument",
        Value = "inactive"
    },
    new BoxMetadataUpdate()
    {
        Op = MetadataUpdateOp.test,
        Path = "/author",
        Value = "Jones"
    },
    new BoxMetadataUpdate()
    {
        Op = MetadataUpdateOp.copy,
        From="/author",
        Path = "/editor"
    },
    new BoxMetadataUpdate()
    {
        Op = MetadataUpdateOp.test,
        Path = "/currentState",
        Value = "proposal"
    },
    new BoxMetadataUpdate()
    {
        Op = MetadataUpdateOp.move,
        From = "/currentState",
        Path = "/previousState"
    },
    new BoxMetadataUpdate()
    {
        Op = MetadataUpdateOp.add,
        Path = "/currentState",
        Value = "reviewed"
    }
};
Dictionary<string, object> updatedMetadata = await client.MetadataManager
    .UpdateFileMetadataAsync("11111", updates, "enterprise", "marketingCollateral");
Node
var updates = [
	{ op: 'test', path: '/competitiveDocument', value: 'no' },
	{ op: 'remove', path: '/competitiveDocument' },
	{ op: 'test', path: '/status', value: 'active' },
	{ op: 'replace', path: '/status', value: 'inactive' },
	{ op: 'test', path: '/author', value: 'Jones' },
	{ op: 'copy', from: '/author', path: '/editor' },
	{ op: 'test', path: '/currentState', value: 'proposal' },
	{ op: 'move', from: '/currentState', path: '/previousState' },
	{ op: 'add', path: '/currentState', value: 'reviewed' }
];
client.files.updateMetadata('11111', client.metadata.scopes.ENTERPRISE, "marketingCollateral", updates)
	.then(metadata => {
		/* metadata -> {
			audience: 'internal',
			documentType: 'Q1 plans',
			status: 'inactive',
			author: 'Jones',
			'$type': 'marketingCollateral-d086c908-2498-4d3e-8a1f-01e82bfc2abe',
			'$parent': 'file_11111',
			'$id': '2094c584-68e1-475c-a581-534a4609594e',
			'$version': 1,
			'$typeVersion': 0,
			editor: 'Jones',
			previousState: 'proposal',
			currentState: 'reviewed',
			'$template': 'marketingCollateral',
			'$scope': 'enterprise_12345' }
		*/
	});
iOS
client.metadata.update(
    forFileWithId: "11111",
    scope: "enterprise",
    templateKey: "personnelRecord",
    operations: [
        .test(path: "/department", value: "Sales"),
        .replace(path: "/department", value: "Marketing")
    ]
) { (result: Result<MetadataObject, BoxSDKError>) in
    guard case let .success(metadata) = result {
        print("Error updating metadata")
        return
    }

    print("Employee department updated to \(metadata.keys["department"])")
}

The authenticated user needs to have write access on the file to be able to write changes to the metadata on a file.

Update metadata on a folder

To update the metadata to a folder, call the PUT /folders/:folder_id/metadata/:scope/:templateKey API endpoint with the folder's folder_id, the template's scope and templateKey, set of JSON operations to manipulate the data on the template instance.

cURL
curl -i -X PUT "https://api.box.com/2.0/folders/4353455/metadata/enterprise_27335/blueprintTemplate" \
     -H "authorization: Bearer <ACCESS_TOKEN>" \
     -H "content-type: application/json-patch+json" \
     -d '[
        {
          "op": "test",
          "path": "/competitiveDocument",
          "value": "no"
        },
        {
          "op": "remove",
          "path": "/competitiveDocument"
        },
        {
          "op": "test",
          "path": "/status",
          "value": "active"
        },
        {
          "op": "replace",
          "path": "/status",
          "value": "inactive"
        },
        {
          "op": "test",
          "path": "/author",
          "value": "Jones"
        },
        {
          "op": "copy",
          "from": "/author",
          "path": "/editor"
        },
        {
          "op": "test",
          "path": "/currentState",
          "value": "proposal"
        },
        {
          "op": "move",
          "from": "/currentState",
          "path": "/previousState"
        },
        {
          "op": "add",
          "path": "/currentState",
          "value": "reviewed"
        }
      ]'
TypeScript Gen
await client.folderMetadata.updateFolderMetadataById(
  folder.id,
  'global' as UpdateFolderMetadataByIdScope,
  'properties',
  [
    {
      op: 'replace' as UpdateFolderMetadataByIdRequestBodyOpField,
      path: '/abc',
      value: newValue,
    } satisfies UpdateFolderMetadataByIdRequestBody,
  ],
);
Python Gen
client.folder_metadata.update_folder_metadata_by_id(
    folder.id,
    UpdateFolderMetadataByIdScope.GLOBAL.value,
    "properties",
    [
        UpdateFolderMetadataByIdRequestBody(
            op=UpdateFolderMetadataByIdRequestBodyOpField.REPLACE.value,
            path="/abc",
            value=new_value,
        )
    ],
)
.NET Gen
await client.FolderMetadata.UpdateFolderMetadataByIdAsync(folderId: folder.Id, scope: UpdateFolderMetadataByIdScope.Global, templateKey: "properties", requestBody: Array.AsReadOnly(new [] {new UpdateFolderMetadataByIdRequestBody() { Op = UpdateFolderMetadataByIdRequestBodyOpField.Replace, Path = "/abc", Value = newValue }}));
Swift Gen (Beta)
try await client.folderMetadata.updateFolderMetadataById(folderId: folder.id, scope: UpdateFolderMetadataByIdScope.global, templateKey: "properties", requestBody: [UpdateFolderMetadataByIdRequestBody(op: UpdateFolderMetadataByIdRequestBodyOpField.replace, path: "/abc", value: newValue)])
Java
BoxFolder folder = new BoxFolder(api, "id");
folder.updateMetadata(new Metadata().add("/foo", "bar"));
Python
folder = client.folder(folder_id='22222')
folder_metadata = folder.metadata(scope='enterprise', template='myMetadata')

updates = folder_metadata.start_update()
updates.add('/foo', 'bar')
updates.update('/baz', 'murp', old_value='quux')  # Ensure the old value was "quux" before updating to "murp"

updated_metadata = folder_metadata.update(updates)
print('Updated metadata on folder!')
print(f'foo is now {updated_metadata["foo"]} and baz is now {updated_metadata["baz"]}')
.NET
var updates = new List<BoxMetadataUpdate>()
{
    new BoxMetadataUpdate()
    {
        Op = MetadataUpdateOp.test,
        Path = "/competitiveDocument",
        Value = "no"
    },
    new BoxMetadataUpdate()
    {
        Op = MetadataUpdateOp.remove,
        Path = "/competitiveDocument"
    },
    new BoxMetadataUpdate()
    {
        Op = MetadataUpdateOp.test,
        Path = "/status",
        Value = "active"
    },
    new BoxMetadataUpdate()
    {
        Op = MetadataUpdateOp.replace,
        Path = "/competitiveDocument",
        Value = "inactive"
    },
    new BoxMetadataUpdate()
    {
        Op = MetadataUpdateOp.test,
        Path = "/author",
        Value = "Jones"
    },
    new BoxMetadataUpdate()
    {
        Op = MetadataUpdateOp.copy,
        From="/author",
        Path = "/editor"
    },
    new BoxMetadataUpdate()
    {
        Op = MetadataUpdateOp.test,
        Path = "/currentState",
        Value = "proposal"
    },
    new BoxMetadataUpdate()
    {
        Op = MetadataUpdateOp.move,
        From = "/currentState",
        Path = "/previousState"
    },
    new BoxMetadataUpdate()
    {
        Op = MetadataUpdateOp.add,
        Path = "/currentState",
        Value = "reviewed"
    }
};
Dictionary<string, object> updatedMetadata = await client.MetadataManager
    .UpdateFolderMetadataAsync("11111", updates, "enterprise", "marketingCollateral");
Node
var updates = [
	{ op: 'test', path: '/competitiveDocument', value: 'no' },
	{ op: 'remove', path: '/competitiveDocument' },
	{ op: 'test', path: '/status', value: 'active' },
	{ op: 'replace', path: '/status', value: 'inactive' },
	{ op: 'test', path: '/author', value: 'Jones' },
	{ op: 'copy', from: '/author', path: '/editor' },
	{ op: 'test', path: '/currentState', value: 'proposal' },
	{ op: 'move', from: '/currentState', path: '/previousState' },
	{ op: 'add', path: '/currentState', value: 'reviewed' }
];
client.folders.updateMetadata('11111', client.metadata.scopes.ENTERPRISE, "marketingCollateral", updates)
	.then(metadata => {
		/* metadata -> {
			audience: 'internal',
			documentType: 'Q1 plans',
			status: 'inactive',
			author: 'Jones',
			'$type': 'marketingCollateral-d086c908-2498-4d3e-8a1f-01e82bfc2abe',
			'$parent': 'folder_11111',
			'$id': '2094c584-68e1-475c-a581-534a4609594e',
			'$version': 1,
			'$typeVersion': 0,
			editor: 'Jones',
			previousState: 'proposal',
			currentState: 'reviewed',
			'$template': 'marketingCollateral',
			'$scope': 'enterprise_12345' }
		*/
	});
iOS
client.metadata.update(
    forFolderWithId: "22222",
    scope: "enterprise",
    templateKey: "personnelRecord",
    operations: [
        .test(path: "/department", value: "Sales"),
        .replace(path: "/department", value: "Marketing")
    ]
) { (result: Result<MetadataObject, BoxSDKError>) in
    guard case let .success(metadata) = result {
        print("Error updating metadata")
        return
    }

    print("Employee department updated to \(metadata.keys["department"])")
}

The authenticated user needs to have write access on the file to be able to write changes to the metadata on a file.

JSON Operations

Updating an piece of metadata follow the JSON-Patch specification, which is represented as a list of operation objects.

For metadata instances, these operations can be either add, replace, remove , test, move, or copy. Every operation exists out of an op name, the JSON Pointer path that points to the field to changes, and an optional value or from value depending on the operation being made.

[
  { "op": "test", "path": "/competitiveDocument", "value": "no" },
  { "op": "remove", "path": "/competitiveDocument" },
  { "op": "test", "path": "/status", "value": "active" },
  { "op": "replace", "path": "/status", "value": "inactive" },
  { "op": "test", "path": "/author", "value": "Jones" },
  { "op": "copy", "from": "/author", "path": "/editor" },
  { "op": "move", "from": "/currentState", "path": "/previousState" },
  { "op": "add", "path": "/currentState", "value": "reviewed" }
]

When editing metadata, only values that adhere to the metadata template schema will be accepted. The update is applied completely or not at all. If any errors occur during the application of the update operations, the metadata instance is not changed.

The template instance can only be updated if the template has already been assigned to the file or folder.

Add a new value

To add a new value on a template, use the add operation.

[
  {
    "op": "add",
    "path": "/name",
    "value": "Model 3"
  }
]

This will add the name field with a value of Model 3 . Before this operation, the template did not have a value for the name field.

{
  // "name": null, // old value
  "name": "Model 3", // new value
  "category": "SUVs",
  "$type": "productInfo-8120731a-41e4-11ea-b77f-2e728ce88125",
  "$parent": "folder_3456",
  "$id": "22ba8c96-41e6-11ea-b77f-2e728ce88125",
  "$version": 3,
  "$typeVersion": 0,
  "$template": "productInfo",
  "$scope": "enterprise_1234567",
  "$canEdit": true
}

For enum and multiSelect fields this new value needs to be one of the valid options for the field.

Replace a value

To replace a value on a template, use the replace operation.

[
  {
    "op": "replace",
    "path": "/name",
    "value": "Model 4"
  }
]

This will replace the name field value Model 3 with a new value of Model 4.

{
  // "name": "Model 3", # Old value
  "name": "Model 3", // new value
  "category": "SUVs",
  "$type": "productInfo-8120731a-41e4-11ea-b77f-2e728ce88125",
  "$parent": "folder_3456",
  "$id": "22ba8c96-41e6-11ea-b77f-2e728ce88125",
  "$version": 3,
  "$typeVersion": 0,
  "$template": "productInfo",
  "$scope": "enterprise_1234567",
  "$canEdit": true
}

For enum and multiSelect fields this new value needs to be one of the valid options for the field.

Copy a value

To copy a value from one field to another, use the copy operation.

[
  {
    "op": "copy",
    "from": "/name",
    "path": "/displayName"
  }
]

This will add the displayName field with a value that matches the value of the name field. Before this operation, the template did not have a value for the displayName field.

{
  "name": "Model 3",
  "displayName": "Model 3", // new value, copied from the name
  "category": "SUVs",
  "$type": "productInfo-8120731a-41e4-11ea-b77f-2e728ce88125",
  "$parent": "folder_3456",
  "$id": "22ba8c96-41e6-11ea-b77f-2e728ce88125",
  "$version": 3,
  "$typeVersion": 0,
  "$template": "productInfo",
  "$scope": "enterprise_1234567",
  "$canEdit": true
}

For enum and multiSelect fields this new value needs to be one of the valid options for the field.

Move a value

To move a value from one field to another, use the move operation.

[
  {
    "op": "copy",
    "from": "/name",
    "path": "/displayName"
  }
]

This will add the displayName field with a value that matches the value of the name field. Before this operation, the template did not have a value for the displayName field. After this operation, the name field no longer exists.

{
  // "name": "Model 3", // old value, no longer present now
  "displayName": "Model 3", // new value, copied from the name
  "category": "SUVs",
  "$type": "productInfo-8120731a-41e4-11ea-b77f-2e728ce88125",
  "$parent": "folder_3456",
  "$id": "22ba8c96-41e6-11ea-b77f-2e728ce88125",
  "$version": 3,
  "$typeVersion": 0,
  "$template": "productInfo",
  "$scope": "enterprise_1234567",
  "$canEdit": true
}

For enum and multiSelect fields this new value needs to be one of the valid options for the field.

Remove a value

To remove a value from the metadata instance, use the remove operation.

[
  {
    "op": "remove",
    "path": "/name"
  }
]

This will remove the name field completely from the metadata instance.

{
  // "name": "Model 3", // old value, no longer present now
  "category": "SUVs",
  "$type": "productInfo-8120731a-41e4-11ea-b77f-2e728ce88125",
  "$parent": "folder_3456",
  "$id": "22ba8c96-41e6-11ea-b77f-2e728ce88125",
  "$version": 3,
  "$typeVersion": 0,
  "$template": "productInfo",
  "$scope": "enterprise_1234567",
  "$canEdit": true
}

For enum and multiSelect fields this new value needs to be one of the valid options for the field.

Test a value

To test that a field has the value you expect, use the test operation.

[
  {
    "op": "test",
    "path": "/name",
    "value": "Model 4"
  }
]

When a test fails the API will not perform any of the operations and return a 409 Conflict HTTP status with the following error.

{
  "message": "value differs from expectations",
  "code": "failed_json_patch_application",
  "request_id": "bzxgr1gbcq5h67pj"
}

The main purpose of this operation is to validate that the values on the metadata instance are as expected before any operations are performed. The Box API either performs all changes or none, and therefore a failing test is very useful to ensure all values are expected before any transformation is applied.