# Developer Guides This section contains 469 documentation items covering guide, quick-start. Categories: Guides , Box AI Studio , API Calls , Applications , Box Archive , Authentication , Authorization , Box AI , Box Relay , Box MCP Server , Box Sign , CLI , Collaborations , Comments , Collections , Box Doc Gen , Downloads , Events , Embed Box , Files , File Requests , Folders , Start Here , Integration Mappings , Legal Holds , Metadata , Mobile , Representations , Retention Policies , Search , Security , Shared Links , Box Skills , SSO & App users , Tasks , Tooling , Trash , Uploads , Users , Web Links , Webhooks ## Untitled *Type: guide | Category: Guides * Guides # Guides **Reference:** https://developer.box.com/guides/ --- ## Untitled *Type: guide | Category: Box AI Studio * Get started with AI studio Box AI Studio is available only for Enterprise Advanced accounts. To start creating custom AI agents with AI… # Get started with AI studio Box AI Studio is available only for Enterprise Advanced accounts. To start creating custom AI agents with AI studio you need a platform application with enabled Box AI scope and a developer token to authenticate your calls. ## Create a platform application First you need to create a platform application you will use to make calls. To create an application, follow the guide on [creating platform apps](g://applications/app-types/platform-apps). ## Enable Box AI studio To use Box AI studio, make sure it is enabled by a Box admin in the Admin Console. If you are a Box Admin, you will find the necessary information in [Enabling Box AI Studio and Managing Agents](https://support.box.com/hc/en-us/articles/37228079461267-Enabling-Box-AI-Studio-and-Managing-Agents/#h_01JH9HAMP43YYN6VWM51QCK413). To interact with Box AI API, you need the `ai.readwrite` [scope](g://api-calls/permissions-and-errors/scopes) added for your application. Before you add the scope, make sure that the Box Admin has granted you the access to Box AI API. If you can't see the **Manage AI** option in your app configuration settings, contact your admin. To add a scope: Open your application in Developer Console. Go to **Configuration** > **Required Access Scopes** > **Content Actions** Select the **Manage AI** scope. Box Platform will automatically include the scope when making the call. If you are added as an collaborator for a given app, but do not have Box AI API access, you will see the **Manage AI** scope checked and grayed out. This means the app owner has the AI scope enabled but you cannot change this setting. Submit your app for [authorization or enablement](g://authorization). If you want to enable Box AI API for an existing application, you must [re-authorize](g://authorization/custom-app-approval#re-authorization-on-changes) it. ## Generate a developer token You need a developer token to authenticate your app when sending requests. To generate a token: 1. Go to **Developer Console** > **My Platform Apps**. 2. Click the **Options menu** button (…) on the right. 3. Select **Generate Developer Token**. The token will be automatically generated and saved to clipboard. You can also open your app, go to **Configuration** > **Developer Token** and generate the token. A developer token is only valid for one hour. For additional details, see [developer token](g://authentication/tokens/developer-tokens). After you generate the token, you can use it in cURL or other clients, such as [Postman](g://tooling/postman), to make calls. **Reference:** https://developer.box.com/guides/ai-studio/getting-started-ai-studio/ --- ## Untitled *Type: guide | Category: Box AI Studio * Box AI Studio Box AI Studio is available only for Enterprise Advanced accounts. Box AI Studio allows you to build and manage custom AI… # Box AI Studio Box AI Studio is available only for Enterprise Advanced accounts. [Box AI Studio](https://support.box.com/hc/en-us/articles/37228079461267-Enabling-Box-AI-Studio-and-Managing-Agents) allows you to build and manage custom AI agents to best suit your business needs. For example, you can create an AI agent that acts as a compliance consultant, answering questions regarding customer documentation with the FedRAMP Moderate compliance in mind. ## Box AI Studio capabilities Currently, you can configure Box AI agent to answer user questions or generate text you can use in your documents. ## Supported languages Box AI studio works in a number of languages including English, Japanese, French, Spanish, and many more. However, the underlying models are primarily trained on English language documents. This means that prompts in other languages may return answers of lower quality than in English. Tests have shown satisfactory results for summarizing, checking grammar and spelling, and answering questions, but bear in mind that the results may be different than in English. Switch the language to Japanese to get better results for this language. **Reference:** https://developer.box.com/guides/ai-studio/ --- ## Untitled *Type: guide | Category: API Calls * Allow domain access To use the Box APIs it is important that your application and users have access to the following domains, where needed… # Allow domain access To use the Box APIs it is important that your application and users have access to the following domains, where needed. ## File preview To enable file preview, your application might need to load javascript file from the Box content delivery network (CDN). This file is loaded from the following domains. - `api.box.com` - `boxcdn.net` - `boxcloud.com` - `dl2.boxcloud.com` to `dl20.boxcloud.com` ## File downloads The following API domains are used to download files via the Box API. - `api.box.com` to initially request a file to download - `dl.boxcloud.com` to actually download files for authenticated users - `public.boxcloud.com` to actually download files for unauthenticated users Ensuring access to these domains is only a first step to downloading a file. To download a file the users need to have proper access permissions and need to be fully authenticated where needed. ## File uploads The following API domains are used to upload files via the Box API. - `api.box.com` to start a file upload - `upload.app.box.com` and `upload.box.com` to upload the file to Box **Reference:** https://developer.box.com/guides/api-calls/allowing-domain-access/ --- ## Untitled *Type: guide | Category: API Calls * API versioning strategy Box provides versioning capabilities for selected API endpoints. The version control system guarantees seamless… # API versioning strategy Box provides versioning capabilities for selected API endpoints. The version control system guarantees seamless functioning of existing endpoint versions, even if Box introduces new ones. API versioning empowers Box to continually enhance its platform, while also offering third-party developers a reliable avenue for feature updates and deprecations. To stay informed about the forthcoming API modifications, monitor the [Changelog](page://changelog) and maintain a current email address in the Developer Console's App Info section. In 2024, Box introduced year-based API versioning. All endpoints available at the end of 2024 were assigned the version `2024.0`. **No action is required for API users to continue using Box APIs.** To make version-aware API calls, include the `box-version` header with the value `2024.0` in your requests. ## How Box API versioning works Box API supports versioning in `header`. To determine which version to use, look at the API reference and included sample requests. ### Versioning in header Box API processes the `box-version` header which should contain a valid version name. For example, when a client wants to get a list of all sign requests using version `2025.0`, the request should look like this: ``` curl --location 'https://api.box.com/2.0/sign_requests' \ --header 'box-version: 2025.0' \ --header 'Authorization: Bearer … ``` If the provided version is correct and supported by the endpoint, a response is sent to the client. If the endpoint is available in multiple versions, the response will include the `box-version` header, which indicates the version used to handle the request. Endpoints introduced after 2024 may return a `400` error code if the version is incorrect. More information about versioning errors can be found [here](g://api-calls/permissions-and-errors/versioning-errors). If your request doesn't include a version, the API defaults to the initial Box API version - `2024.0` - the version of endpoints available before year-based versioning was introduced. However, relying on this behavior is not recommended when adopting deprecated changes. To ensure consistency, always specify the API version, with each request. By making your application version-aware, you anchor it to a specific set of features, ensuring consistent behavior throughout the supported timeframe. ## Release schedule and naming convention Box can introduce a new breaking change to certain endpoints **once per year**, which results in a new API version. Introducing a new version of the Sign Request endpoint means that **all paths and HTTP methods** of an endpoint will support it. For example, if Sign Request endpoints receive a new version it will apply to all endpoints listed in the table: | Method | Request URL | Description | | --- | --- | --- | | GET | https://api.box.com/2.0/sign_requests/:id | Retrieves specific sign request details. | | GET | https://api.box.com/2.0/sign_requests/ | Retrieves all sign requests. | | POST | https://api.box.com/2.0/sign_requests/ | Creates new sign requests. | | POST | https://api.box.com/2.0/sign_requests/:id/resend | Sends a specific sign request again. | | POST | https://api.box.com/2.0/sign_requests/:id/cancel | Cancels a specific sign request. | ### Naming convention New API versions are labeled according to the calendar year of their release. **Example**: If a new version of the Sign Requests endpoint is released in 2025, it will be named `2025.0`. Box can issue a new breaking change to API endpoints **once** per year, reserving the right to release an additional breaking change to address security or privacy concerns. In such cases, the new version will be incremented by one in the suffix. **Example**: If security issues need addressing in the previously released version `2025.0` of Sign Requests, the new version will be labeled `2025.1`. Each stable version is supported for a minimum of 12 months. This means that when a new version is released, the previous version becomes deprecated and will be available for use, but no new features will be added. It also means, that a new version cannot be released sooner than every 12 months. We strongly recommend updating your apps to make requests to the latest stable API version. However, if your app uses a stable version that is no longer supported, then you will get a response with an HTTP error code `400 - Bad Request`. For details, see [Versioning Errors](g://api-calls/permissions-and-errors/versioning-errors). ### Endpoint versioning indication To keep you informed about the current API state, and improve the readability of the versioned API reference, the affected endpoints are marked with a pill based on the `x-stability-level` tag or `deprecated` attribute. | Schema element | Pill name | Description | | --- | --- | --- | | x-stability-level: beta | Beta | Endpoints marked with beta, are offered subject to Box’s Main Beta Agreement, meaning the available capabilities may change at any time. When the beta endpoint becomes stable, the beta indication is removed. | | x-stability-level: stable or no x-stability-level tag | Latest version | Latest version marks the most recent stable API version of an endpoint. | | deprecated: true | Deprecated | An endpoint is deprecated, which means it is still available for use, but no new features are added. Such an endpoint is annotated with the deprecated attribute set to true. | ## Versioning errors When using versioned API actions such as calling an incorrect API version in header or a deprecated version can lead to errors. For details on possible errors, see [versioning errors](g://api-calls/permissions-and-errors/versioning-errors). ## How Box SDK versioning works The versioning strategy applies only to [next generation generated SDKs](page://sdks-and-tools/#next-generation-sdks). Box SDKs support the **All Versions In** SDK approach. This means that every release of SDK provides access to all endpoints in any version which is currently live. All generated SDKs use manager's approach - they group all endpoints with the same domain in one manager. For example `FolderManager` contains methods to: `create_folder`, `get_folder_by_id`, `update_folder_by_id`, `delete_folder_by_id`, `get_folder_items` and `copy_folder`. This division is done based on the value of `x-box-tag` field, which is assigned to each method in Public API Service specification. It mostly corresponds to the root of the endpoint URL, but not necessarily. For example: `FolderManager` contains methods with `https://api.box.com/2.0/folders` root URL, but the same base URL is also used in some methods of `SharedLinkFoldersManager`. References to all managers are stored under one Box Client object. See an example of the endpoint's lifecycle: Initial state (only one version is available). ``` class FilesManager { async updateFileById( fileId: string, requestBody: UpdateFileByIdRequestBody, queryParams: UpdateFileByIdQueryParams, headers: UpdateFileByIdHeaders ): Promise < FileFull > {} } ``` A new `v2025_0` version of the endpoint is introduced (previous version is deprecated). The SDK introduces a new method for each new version of an endpoint. These methods are stored in the same manager as the old ones, but their names and corresponding classes are suffixed with the version number. The old method is deprecated with a notice indicating the minimal maintenance date – this will be the date when the endpoint will be considered for end-of-life status. ``` class FilesManager { /** * @deprecated This endpoint will be EOL'ed after 05-2026. */ async updateFileById( fileId: string, requestBody: UpdateFileByIdRequestBody, queryParams: UpdateFileByIdQueryParams, headers: UpdateFileByIdHeaders ): Promise<FileFull> {} async updateFileById_2025_0( fileId: string, requestBody: UpdateFileByIdRequestBody_2025_0, queryParams: UpdateFileByIdQueryParams_2025_0, headers: UpdateFileByIdHeaders_2025_0 ): Promise<FileFull_2025_0> {} } ``` The API endpoint is marked as End-of-Life (EOL) The SDK releases a breaking change release with removed end-of-life (EOL) endpoints. Ideally, we should group the end-of-life dates for all endpoints into one date per quarter to avoid releasing numerous new major versions of SDKs. ``` class FilesManager { async updateFileById_2025_0( fileId: string, requestBody: UpdateFileByIdRequestBody_2025_0, queryParams: UpdateFileByIdQueryParams_2025_0, headers: UpdateFileByIdHeaders_2025_0 ): Promise < FileFull_2025_0 > {} } ``` ## Breaking vs non-breaking changes Breaking changes in the Box API occur within versioned releases, typically accompanied by a new major API version. Minor adjustments, which do not disrupt existing functionality, can be integrated into an existing API version. The following table lists both breaking and non-breaking changes. | API Change | Breaking change | | --- | --- | | New endpoints | No | | New read-only or optional fields in request | No | | New required fields in request | Yes | | New string constant in request | Yes | | Deprecation | No | | Retired / End-of-Life endpoints | Yes | | Rename/reshape of a field, data type, or string constant | Yes | | More restrictive change to field validations | Yes | | Less restrictive change to field validations | No | | Changing HTTP status code returned by an operation | Yes | | Removing a declared property | Yes | | Removing or renaming APIs or API parameters | Yes | | Adding a required request header | Yes | | Adding more error codes | No | | Removing or modifying error codes | Yes | | Adding a member to an enumeration | Yes | The [oasdiff](https://github.com/Tufin/oasdiff/blob/main/BREAKING-CHANGES-EXAMPLES.md) tool allows detecting most of the possible breaking changes. ## AI agent configuration versioning [AI agent](g://box-ai/ai-agents) versioning gives the developers more control over model version management and ensures consistent responses. For details, see [AI agent configuration versioning guide](g://box-ai/ai-agents/ai-agent-versioning). ## Support policy and deprecation information When new versions of the Box APIs and Box SDKs are released, earlier versions will be retired. Box marks a version as `deprecated` at least 24 months before retiring it. In other words, a deprecated version cannot become end-of-life sooner than after 24 months. Similarly, for individual APIs that are generally available (GA), Box declares an API as `deprecated` at least 24 months in advance of removing it from the GA version. When we increment the major version of the API (for example, from `2025.0` to `2026.0`), we're announcing that the current version (in this example, `2025.0`) is immediately deprecated and we'll no longer support it 24 months after the announcement. We might make exceptions to this policy for service security or health reliability issues. When an API is marked as deprecated, we strongly recommend that you migrate to the latest version as soon as possible. In some cases, we'll announce that new applications will have to start using the new APIs a short time after the original APIs are deprecated. When customer calls deprecated API endpoint, the response will contain a header: ``` Deprecation: date="Fri, 11 Nov 2026 23:59:59 GMT" Box-API-Deprecated-Reason: https://developer.box.com/reference/deprecated ``` The date tells clients when this version was marked as deprecated. ## Versioning considerations When building your request, consider the following: - Endpoints in version `2024.0` can be called without specifying the version in the `box-version` header. If no version is specified and the `2024.0` version of the called endpoint does not exist, the response will return an HTTP error code `400 - Bad Request`. - If the `box-version` version header is specified but the requested version does not exist, the response will return an HTTP error code `400 - Bad Request`. For details, see [versioning errors](g://api-calls/permissions-and-errors/versioning-errors). When Box deprecates a resource or a property of a resource in the API, the change is communicated in one or more of the following ways: Calls that include the deprecated behavior return the response header `Box-API-Deprecated-Reason` and a link to get more information: ``` box-version: 2025.0 Deprecation: version="version", date="date" Box-API-Deprecated-Reason: https://developer.box.com/reference/deprecated ``` A deprecation announcement is posted in the developer changelog. The API reference is updated to identify the affected resource and any action you need to take. Affected endpoints are marked with **deprecated** pill. If there is an imminent backwards-incompatible change that affects your app, then the contact email for your app might be contacted about the deprecation. ## Additional resources - [API reference](page://reference) **Reference:** https://developer.box.com/guides/api-calls/api-versioning-strategy/ --- ## Untitled *Type: guide | Category: API Calls * Ensure consistency with headers Some Box APIs support headers used to ensure consistency between your application and Box. etag, if-match… # Ensure consistency with headers Some Box APIs support headers used to ensure consistency between your application and Box. ## etag, if-match, and if-none-match Many of the file system items (files or folders) that can be requested via the API return an `etag` value for the item. For example, a file resource returns an `etag` in the JSON response. ``` curl https://api.box.com/2.0/files/12345 \ -H "authorization: Bearer ACCESS_TOKEN" ``` ``` { "id": 12345, "etag": 1, "type": "file", "sequence_id": 3, "name": "Contract.pdf", ... } ``` This `etag` can be used as the value of a `if-match` or `if-none-match` header to either ensure a resource hasn't changed since the `etag` value was received, or to prevent unnecessary downloads for items that haven't changed. For example, to fetch the same file only if it has changed, pass in the `etag` value in a `if-none-match` header. ``` curl https://api.box.com/2.0/files/12345 \ -H "authorization: Bearer ACCESS_TOKEN" \ -H "if-none-match: 1" ``` This API call would result in an empty response if the file had not changed. ## Ensure consistent changes The `if-match` header allows your application to ensure that no changes are made to items when another application or a user has made changes to the item since your application last inspected it. This helps ensure that changes aren't lost when two applications or users are changing items at the same time. The following endpoints support this header. | if-match capable endpoints | | | --- | --- | | POST /files/:id/content | Upload a new file version | | PUT /files/:id | Update a file's information | | DELETE /files/:id | Delete a file | | PUT /folders/:id | Update a folder's information | | DELETE /folders/:id | Delete a folder | | PUT /web_links/:id | Update a web link's information | | DELETE /web_links/:id | Delete a web link | The response of these APIs calls depends on the existence of the item, and whether the `etag` value matches the most recent version. | Item found? | Etag match? | HTTP status | | --- | --- | --- | | Yes | Yes | 200 | | Yes | No | 412 | | No | Yes | 412 | | No | No | 404 | # Moving items The `if-match` header can not be used to prevent moving of files, folders, or web links. Instead, Box will always ensure that the latest item is moved to the new location. ## Prevent unnecessary request downloads The `if-none-match` header allows your application to ensure that no information is downloaded for items that have not changed since your application last inspected it. This helps ensure no unnecessary information is downloaded, speeding up your application and saving on bandwidth. | if-none-match capable endpoints | | | --- | --- | | GET /files/:id | Get a file's information | | GET /folders/:id | Get a folder's information | | GET /web_links/:id | Get a web link's information | | GET /shared_items | Get a shared item's information | The response of these APIs calls depends on the existence of the item, and whether the `etag` value matches the most recent version. | Item found? | Etag match? | HTTP Status | | --- | --- | --- | | Yes | Yes | 304 | | Yes | No | 200 | | No | Yes | 404 | | No | No | 404 | **Reference:** https://developer.box.com/guides/api-calls/ensure-consistency/ --- ## Untitled *Type: guide | Category: API Calls * API Calls The Box API is a restful API that follow common HTTP standards where possible. The following guides take a look at some of the… # API Calls The Box API is a restful API that follow common HTTP standards where possible. The following guides take a look at some of the useful features and common mistakes that a developer can encounter when working with these APIs. ## API calls insights Admins and co-admins can access the Platform Insights dashboard that provides information on the total number of API calls per application. See [Platform Insights](https://support.box.com/hc/en-us/articles/20738406915219-Platform-Insights) and [applications](g://applications) for details. **Reference:** https://developer.box.com/guides/api-calls/ --- ## Untitled *Type: guide | Category: API Calls * Language codes The Box API uses a modified version of the ISO 639-1 Language Code to specify a user's language. The following is a list of… # Language codes The Box API uses a modified version of the **ISO 639-1 Language Code** to specify a user's language. The following is a list of language codes used when [creating](e://post_users#param-language) or [updating](e://put_users_id#param-language). | Language | Code | | --- | --- | | Bengali | bn | | Danish | da | | German | de | | English (US) | en | | English (UK) | gb | | English (Canada) | e2 | | English (Australia) | e3 | | Spanish (Latin America) | s2 | | Spanish | es | | Finnish | fi | | French | fr | | French (Canada) | f2 | | Hindi | hi | | Italian | it | | Japanese | ja | | Korean | ko | | Norwegian (Bokmal) | nb | | Dutch | nl | | Polish | pl | | Portuguese | pt | | Russian | ru | | Swedish | sv | | Turkish | tr | | Chinese (Simplified) | zh | | Chinese (Traditional) | zt | **Reference:** https://developer.box.com/guides/api-calls/language-codes/ --- ## Untitled *Type: guide | Category: API Calls * Request extra fields The number of fields returned for a resource depends on the API endpoint used to request the resource. Use the fields… # Request extra fields The number of fields returned for a resource depends on the API endpoint used to request the resource. ## Use the fields query parameter To request a specific field for a resource that is not returned by default in the standard response, append the `fields` query parameter to your request. The value of this parameter is a comma separated list of field names. ``` curl https://api.box.com/2.0/files/12345?fields=is_package,lock \ -H "authorization: Bearer ACCESS_TOKEN" ``` ``` { "etag": "1", "id": "12345", "is_package": false, "lock": null, "type": "file" } ``` It is important to note that when a specific field is requested no other fields are returned except for those requested and the **base** set of fields. For a file, this base set is comprised of the `etag`, `id`, and `type` values. ## Resource variants The following resource variants are available in the Box API. ### Standard The default set of fields returned in an API response. The standard variant is returned when requesting a resource through the main APIs available for that resource. For example, when requesting the [`GET /files/:id`](endpoint://get_files_id) endpoint the API will return the standard variation of a file. ``` curl https://api.box.com/2.0/files/12345 \ -H "authorization: Bearer ACCESS_TOKEN" ``` ``` { "content_created_at": "2019-06-20T06:04:41-07:00", "content_modified_at": "2019-06-20T06:04:41-07:00", "created_at": "2019-06-20T07:28:42-07:00", "created_by": { "id": "191919191", "login": "joe@example.com", "name": "Joe Box", "type": "user" }, "description": "", "etag": "1", "file_version": { "id": "56663434454334", "sha1": "585afa5209bbd586c79499b7336601341ad06cce", "type": "file_version" }, "id": "12345", ... "size": 65000647, "trashed_at": null, "type": "file" } ``` ### Mini Where a resource is returned as a nested part of another response it is often reduced in size, only returning some of the more essential fields. This variant is commonly known as the mini resource variant. For example, when requesting the [`GET /folders/:id/items`](endpoint://get_folders_id_items) endpoint the API will return a mini variation of files and folders nested within the `item_collection`. ``` curl https://api.box.com/2.0/files/12345 \ -H "authorization: Bearer ACCESS_TOKEN" ``` ``` { "id": "0", "type": "folder", "item_collection": { "entries": [ { "etag": "1", "file_version": { "id": "56663434454334", "sha1": "585afa5209bbd586c79499b7336601341ad06cce", "type": "file_version" }, "id": "12345", "name": "Video.mp4", "sequence_id": "1", "sha1": "585afa5209bbd586c79499b7336601341ad06cce", "type": "file" } ... ] ... } ... } ``` To request more information for a nested resource we recommend calling the API for that resource to request it by ID, and optionally pass along the `field` query parameter. For example, to get the owner of a file returned when listing the items in a folder, request that file by ID with the query parameter `field=owned_by`. ### Full The total set of fields that can be returned in an API response. The full variant is returned when requesting a resource through the main APIs available for that resource and by appending the `fields` query parameter. For example, when requesting the [`GET /files/:id`](endpoint://get_files_id) endpoint with the `fields=is_package,lock` parameter the API will return the fields specified plus the basic fields for the file. ``` curl https://api.box.com/2.0/files/12345?fields=is_package,lock \ -H "authorization: Bearer ACCESS_TOKEN" ``` ``` { "etag": "1", "id": "12345", "is_package": false, "lock": null, "type": "file" } ``` **Reference:** https://developer.box.com/guides/api-calls/request-extra-fields/ --- ## Untitled *Type: guide | Category: API Calls * Sorting responses Where an API returns a collection of items it often supports sorting of API responses. Use the sort and direction query… # Sorting responses Where an API returns a collection of items it often supports sorting of API responses. Use the `sort` and `direction` query parameters to sort the collection either in ascending or descending order. ``` curl https://api.box.com/2.0/folders/0/items?sort=size&direction=DESC \ -H "authorization: Bearer ACCESS_TOKEN" ``` Not all API endpoints that return collections have support for sorting. Especially endpoints that use marker-based pagination often lack support for sorting the results. ## Sorting criteria The field to sort on is defined by the `sort` query parameter. Check the API endpoint's documentation for the possible options for this value. In some APIs the `sort` field is the second criteria by which the items are sorted. For example for the [`GET /folders/:id/items`](endpoint://get_folders_id_items) endpoint the results are always sorted by their type first before any other criteria. ## Sorting direction The sorting direction supports two values, either `ASC` for ascending order, or `DESC` for the reverse. **Reference:** https://developer.box.com/guides/api-calls/sorting/ --- ## Untitled *Type: guide | Category: API Calls * Suppress notifications For some API calls, you can block email and webhook notifications by including a box-notifications: off header with… # Suppress notifications For some API calls, you can block email and webhook notifications by including a `box-notifications: off` header with the API call. ``` curl -X POST https://api.box.com/2.0/folders \ -H "box-notifications: off" \ -H "authorization: Bearer ACCESS_TOKEN" \ -d '{ "name": "New Folder", "parent": { "id": "0" } }' ``` As an example, this can be used for a virus-scanning tool to download copies every user's files in an enterprise without every collaborator on the file receiving an email informing them of the download. All actions will still appear in users updates feed and the audit-logs. # Scope requirement Notification suppression is available for approved applications only. Contact support to request the required scopes to be enabled for your application. The following settings need to be configured for your application for this feature to properly work. - **Can suppress email notifications from API calls** - available on request via support - **Manage Enterprise Properties** - available via the developer console - Co-admin permissions of **Edit settings for your company**. Some notifications can not be suppressed, most notable the creation is users, comments, collaborations, task assignments, and when changing a user's login. **Reference:** https://developer.box.com/guides/api-calls/suppress-notifications/ --- ## Untitled *Type: guide | Category: API Calls * Status codes The following rules can be applied to interpret the HTTP status codes received when using the Box API. HTTP Status 200-299 Box… # Status codes The following rules can be applied to interpret the HTTP status codes received when using the Box API. | HTTP Status | | | --- | --- | | 200-299 | Box received, understood, and accepted the API request. The request has either completed or is in the process of being completed. | | 300-399 | Box received, understood, and accepted the API request, yet the client must take further action in order to complete the request. Often this includes redirect to other URLs. | | 400-499 | An client error occurred when handling the request, often because the client either did not provide the right parameters, did not have access to the resources, or tried to perform an action that is otherwise not possible. | | 500-599 | Box received and accepted the request, but an error occurred within Box while handling it. These errors signify a problem with Box, not a problem with the client's request | **Reference:** https://developer.box.com/guides/api-calls/status-codes/ --- ## Untitled *Type: guide | Category: API Calls * Types and formats The following sections explain some basic concepts about the types and formats that can be encountered within the Box APIs… # Types and formats The following sections explain some basic concepts about the types and formats that can be encountered within the Box APIs. ## Requests The Box APIs use JSON in the requests bodies. There are a few notable exceptions to this rule: - The [`POST /oauth2/token`](endpoint://post-oauth2-token) is used to request access tokens and as per the OAuth 2.0 specification it accepts the body to be sent with a content type of `application/x-www-form-urlencoded`. - Most of the APIs that are used to upload binary data, like the [`POST /files/content`](endpoint://post-files-content) endpoint, expect data to be sent as form data with a content type of `multipart/form-data`. Although not required, we highly recommend passing a header with each API request to define the content type of the data sent, for example `content-type: application/json`. ### Headers As per the HTTP specification, all request header names in the Box API are case-insensitive and can be provided in lowercase, uppercase, or any mixed case form. In other words, the content type header can be set as `CONTENT-TYPE: application/json`, `content-type: application/json`, `content-type: application/json` or even the slightly absurd `cOnTeNt-TyPe: application/json`. Header values **are** mostly case sensitive unless stated otherwise. ### GZip compression By default data sent from Box is not compressed. To improve bandwidth and response times it's possible to compress the API responses by including a `Accept-Encoding: gzip, deflate` request header. ### Date and times The Box APIs support [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) timestamps. The preferred way to format a date in a request is to convert the time to UTC, for example `2013-04-17T09:12:36-00:00`. In those cases where timestamps are rounded to a given day, the time component can be omitted. In this case, `2013-04-17T13:35:01+00:00` would become `2013-04-17`. In those cases where timestamps support millisecond precision the expected request format should be as followed `2013-04-17T09:12:36.123-00:00`. The timezone can differ between different files and folders because an enterprise's timezone can change over time. A common example is daylight saving time. Items created during standard time would have a different timezone than items created during daylight saving time. For this reason it's important to use a `RFC3339`-compliant date-time parser to handle dates returned by the API. Timestamps are restricted to dates after the start of the Unix epoch, `00:00:00 UTC` on January 1, 1970. ## Responses The Box APIs generally returns JSON in the response body. There are a few notable exceptions to this rule as well. - APIs that delete items return an empty body with a `204 No Content` HTTP status code. - APIs used to request binary data either return a `200 OK` status code with the binary data attached, or a `202 Accepted`, or `302 Found` status code with no body and a `location` header pointing to the actual binary file. The `content-type` response header can be used to understand the type of content returned in the API. Additionally, every API endpoint has it's response type documented in our API reference documentation. ### Headers As per the HTTP specification, all response header names in the Box API are case-insensitive and could change over time. This means that the API might return responses with a content type header of `CONTENT-TYPE: application/json`, `content-type: application/json` or `content-type: application/json`. Ideally your application should convert header names to a standard case upon request and then use that standardized set of headers to look up values of the headers. Header values **are** always case sensitive unless stated otherwise. ### Resources Most standard API responses where only one resource is returned follow the following format. ``` { "id": "12345", "type": "folder", ... } ``` Every one of these resources will always return an ID and the type of the resource. ### Collections Where an API response returns multiple items a collection is returned. Although the exact format of these collections can change from endpoint to endpoint they generally are formatted as follows. ``` { "total_count": 5000, "limit": 1000, "offset": 2000, "order": [ { "by": "type", "direction": "ASC" } ], "entries": [ { "id": 12345, "etag": 1, "type": "file", "sequence_id": 3, "name": "Contract.pdf" } ] } ``` | Field | Always present? | | | --- | --- | --- | | entries | Yes | A list of entries in the collection | | total_count | No | The total numbers in the collection that can be requested. This can be larger than this page of results | | limit | No | For endpoints that support offset-based pagination, this specifies the limit to the number of results returned | | offset | No | For endpoints that support offset-based pagination, this specifies the offset of results returned | | order | No | For endpoints that support sorting, this specifies the order the results are returned in | | next_marker | No | For endpoints that support marker-based pagination, this specifies the marker for the next page that can be returned | | prev_marker | No | For endpoints that support marker-based pagination, this specifies the marker for the previous page that can be returned | ### Request IDs When your API call returns in an error, our API will return an error object with a `request_id` field. ``` { "type": "error", "status": 400, "code": "item_name_invalid", "help_url": "https://developer.box.com/guides/api-calls/permissions-and-errors/common-errors/", "message": "Method Not Allowed", "request_id": "abcdef123456" } ``` When reaching out to support about specific error, please provide the full API response including the `request_id` to help our support team to quickly find your request. Most API calls also return a `box-request-id` response header. The value of this header should not be confused with the `request_id` value in the body of an error response. ### Large numbers In some cases the API can return extremely large numbers for a field. For example, a folder's size might have grown to many terabytes of data and as a result the `size` field of the folder might have grown to a very large number. In these cases these numbers are returned in [IEEE754](https://en.wikipedia.org/wiki/IEEE_754) format for example `1.2318237429383e+31`. **Reference:** https://developer.box.com/guides/api-calls/types-and-formats/ --- ## Untitled *Type: guide | Category: Applications * Applications Box Developer Console allows you to create applications you can then use to integrate with Box. My Platform Apps view displays… # Applications Box Developer Console allows you to create applications you can then use to integrate with Box. **My Platform Apps** view displays a list of already created applications and gives you quick access to their configuration details. This way, you don't need to open the app each time you want to generate a Developer Token, copy the Client ID, or generate a report. ## Features **My Platform Apps** page allows you to: - Search through the list of already created apps. - Filter the apps by **Enablement Status** and **Authentication Type**. - Create [a new app](g://applications/app-types/select). - Copy the app's [Client ID](g://authentication/client-credentials). - Rename the app and access its details with one click. - Check application [enablement](g://authorization/custom-app-approval#user-authentication-apps) and [authorization](g://authorization) status. Apps published to Integrations display status from Integrations. The **Options menu** available for every entry allows you to: - Access the configuration details of your application. - Generate a [Developer Token](g://authentication/tokens/developer-tokens). - Add collaborators to your application. - Run the [Platform App Diagnostics Report](g://api-calls/permissions-and-errors/app-diagnostics-report). ## Platform App Insights Admins and co-admins can access the Platform Insights dashboard that provides a comprehensive view of the organization’s platform usage. This includes app-related data, such as: - The total number of API calls per application. - A list of top applications within the enterprise. - A list of pending application approvals. - A list of applications awaiting enablement. See [Platform Insights](https://support.box.com/hc/en-us/articles20738406915219-Platform-Insights) for details. You need the following permissions to access and view Platform Insights: - View settings and apps for your company - Edit settings and apps for your company - Run new reports and access existing reports **Reference:** https://developer.box.com/guides/applications/ --- ## Untitled *Type: guide | Category: Box Archive * Add content to Box Archive To add content to an archive, you need to create it first. If you have not done this yet, use the Create Archive… # Add content to Box Archive To add content to an archive, you need to create it first. If you have not done this yet, use the [Create Archive](https://developer.box.com/reference/v2025.0/post-archives/) endpoint. ## Add file or folder to archive Use the [`PUT /files/:id`](e://put-files-id) API endpoint to add a file to an archive, or the [`PUT /folders/:id`](e://put-folders-id) endpoint to add a folder. The `id` parameter is the ID of the file/folder you want to add to the archive. To specify the destination, use the `parent.id` [parameter](https://developer.box.com/reference/put-folders-id/#param-parent-id) in the request body. This can be either an ID of an archive or the ID of a folder that is inside an archive. **Reference:** https://developer.box.com/guides/archives/add-content/ --- ## Untitled *Type: guide | Category: Box Archive * Box Archive Box Archive is available only for Enterprise Advanced accounts. Box Archive allows you to create and manage archives. An archive… # Box Archive Box Archive is available only for Enterprise Advanced accounts. Box Archive allows you to create and manage archives. An archive is a folder dedicated to storing content that is redundant, outdated, or trivial. Content in an archive is owned by the enterprise, and it is not accessible to previous owner and collaborators. ## Archives are folders Archives are a special type of folders. Box Archive APIs allow you to create, list and delete archives. However, there are other APIs that also work with archives or with content within archives. For a full list of supported APIs, see the [Supported APIs](g://archives/supported-apis) guide. ## Required scopes Before using any of the Box Archive APIs, make sure you can access [Box Archive in Admin Console](https://support.box.com/hc/en-us/p/Product_Page_2023?section-id=40168863437843). Your Box Platform app must have the `GCM` and `Read and write all files and folders` [scopes](https://developer.box.com/guides/api-calls/permissions-and-errors/scopes/) enabled. If you plan to only view archives and not modify them, use the `Read all files and folders` instead of the `Read and write all files and folders` scope. The `GCM` scope is not available in the Developer Console and needs to be enabled by contacting customer support. **Reference:** https://developer.box.com/guides/archives/ --- ## Untitled *Type: guide | Category: Box Archive * Restore content from Box Archive This guide describes how to restore content that you archived by mistake. Restore file or folder from… # Restore content from Box Archive This guide describes how to restore content that you archived by mistake. ## Restore file or folder from archive Use the [`PUT /files/:id`](e://put-files-id) API endpoint to restore a file from an archive, or the [`PUT /folders/:id`](e://put-folders-id) endpoint to restore a folder from an archive. The `id` parameter is the ID of the file/folder you want to restore from an archive. To specify the destination, use the `parent.id` [parameter](https://developer.box.com/reference/put-folders-id/#param-parent-id) in the request body. This is the ID of a folder (can be owned by any user) where you want to restore the file/folder. To restore a file/folder to user's root folder, use `0` as the `parent.id` value. Additionally, pass the ID of the user in the `parent.user_id` [parameter](https://developer.box.com/reference/put-folders-id/#param-parent-user_id) in the request body. **Reference:** https://developer.box.com/guides/archives/restore-content/ --- ## Untitled *Type: guide | Category: Box Archive * Supported APIs for Box Archive The basic Box Archive APIs allow you to create, list and delete archives, but you can use other APIs to… # Supported APIs for Box Archive The basic Box Archive APIs allow you to create, list and delete archives, but you can use other APIs to interact with an archive or its content. See the table below for the full list of those APIs. Those APIs require the [`GCM` scope](https://developer.box.com/guides/api-calls/permissions-and-errors/scopes/#global-content-manager-gcm) to be enabled in your application. This scope is not available in the Developer Console and needs to be enabled by contacting customer support. Additionally, some of the below APIs need to be enabled by contacting customer support to properly work with Box Archive. When contacting customer support, specify the user ID you plan to use those APIs with. | API Endpoint | Description | | --- | --- | | POST /archives | Create an archive. | | GET /archives | List all archives. | | DELETE /archives/:id | Delete an archive. | | PUT /files/:id | Add a file to an archive, restore a file from an archive, or move file within/between archives. Other updates to the file are not allowed. Requires contacting customer support to enable. | | PUT /folders/:id | Add a folder to an archive, restore a folder from an archive, or move folder within/between archives. Other updates to the folder are not allowed. Requires contacting customer support to enable. | | POST /files/content | Upload a file to an archive or to folder within archive. Requires contacting customer support to enable. | | GET /files/:id/content | Download a file from an archive or from folder within archive. | | POST /zip_downloads | Download a zip file of an archive or of a folder within archive. | | POST /folders | Create a folder within an archive. Requires contacting customer support to enable. | | GET /files/:id | Get details of a file within an archive. | | GET /folders/:id | Get details of archive or a folder within an archive. | | GET /folders/:id/items | List items within an archive or a folder within an archive. | | POST /files/:id/copy | Copy a file within an archive or to another archive. | | POST /folders/:id/copy | Copy a folder within an archive or to another archive. | | POST /files/:id/metadata/:scope/:template_key | Create metadata instance on a file within an archive. | | GET /files/:id/metadata/:scope/:template_key | View metadata instance on a file within an archive. | | GET /files/:id/metadata | List all metadata instances on a file within an archive. | | PUT /files/:id/metadata/:scope/:template_key | Update metadata instance on a file within an archive. | | DELETE /files/:id/metadata/:scope/:template_key | Delete metadata instance on a file within an archive. | | POST securityClassification | Create a classification label on a file within an archive. | | GET securityClassification | View a classification label on a file within an archive. | | PUT securityClassification | Update a classification label on a file within an archive. | | DELETE securityClassification | Delete a classification label on a file within an archive. | **Reference:** https://developer.box.com/guides/archives/supported-apis/ --- ## Untitled *Type: guide | Category: Authentication * Best Practices Client secret security Your client secret is confidential and needs to be protected. Because this is how we securely identify… # Best Practices ## Client secret security Your client secret is confidential and needs to be protected. Because this is how we securely identify an application's identity when obtaining an Access Token, you do not want to freely distribute a client secret. This includes via email, public forums and code repositories, distributed native applications, or client-side code. ## Cache tokens Because fetching new tokens is expensive, we recommend using a token cache to prevent unnecessary requests. After retrieving a token, store it in an in-memory cache, like Memcached, or a built-in ASP.NET cache service. By default, Access Tokens are valid for 60 minutes, but we recommend setting the expiration time to around 50 minutes to allow for a buffer. When you need a token, first check the cache for a valid token. If the token expired, get a new one and store it in the cache for 50 minutes. ``` def self.user_client(user_id) access_token=Rails.cache.fetch("box_tokens/user/#{user_id}", :expires_in => 50.minutes) do puts "getting new user token" response= Boxr::get_user_token(user_id, private_key: PRIVATE_KEY, private_key_password: ENV['JWT_PRIVATE_KEY_PASSWORD']) response.access_token end Boxr::Client.new(access_token) end ``` Official Box SDKs use token caching. ## Expired tokens Expired tokens return a 401: Unauthorized error. This error should be handled to refresh the token. ## Downscope tokens It is important to follow the principle of least privilege when thinking about Access Tokens. This can be accomplished through [downscoping](g://authentication/tokens/downscope), where a fully scoped Access Token is exchanged for a more restricted Access Token that can then be deployed to client-side code, mobile environments, or UI tools. ``` //Define resource/scopes that downscoped token has access to String resource = "https://api.box.com/2.0/files/RESOURCE_ID"; List<String> scopes = new ArrayList<String>(); scopes.add("base_preview"); scopes.add("item_download"); //Preform token exchange ScopedToken downscopedToken = client.getLowerScopedToken(scopes,resource); //Downscoped token available in downscopedToken.getAccessToken() ``` ## Revoke tokens Both fully scoped Access Tokens and Downscoped Tokens can be [revoked](g://authentication/tokens/revoke). This allows you to manage the lifespan of a token to reduce exposure when a user logs out , there is suspicious activity, or when you need to push new security enhancements. ## Developer Tokens Developer Tokens should only be used for development or testing purposes and never in production. **Reference:** https://developer.box.com/guides/authentication/best-practices/ --- ## Untitled *Type: guide | Category: Authentication * Authentication Authentication with the Box API uses an Access Token to identify a user. The way in which an Access Token is acquired depends… # Authentication Authentication with the Box API uses an Access Token to identify a user. The way in which an Access Token is acquired depends on the method used to authorize a user. The type of authorization available to an application depends on the use-case as well as the type of application that has been created in the developer console. | Box Application Type | Authentication methods | | --- | --- | | Platform App | OAuth 2.0, JWT, or Client Credentials Grant | | Limited Access App | App token | | Custom Skill | No authentication method selection needed | Learn how to select an authorization type ## Access Tokens for Authentication Every API endpoint requires a valid and active **Access Token** to make API calls. An Access Token is a unique string that identifies an authenticated Box user to the API endpoints. ``` curl https://api.box.com/2.0/users/me \ -H "authorization: Bearer EGmDmRVfhfHsqesn5yVYHAqUkD0dyDfk" ``` Learn more about Access Tokens **Reference:** https://developer.box.com/guides/authentication/ --- ## Untitled *Type: guide | Category: Authentication * Select Auth Method The type of authorization your application can use depends on the type of Box Application that you've configured in the… # Select Auth Method The type of authorization your application can use depends on the type of Box Application that you've configured in the developer console. Learn how to select the application type for your app The following authorization methods are available to each Box application type. | Box Application Type | Supports OAuth 2.0? | JWT? | Client Credentials? | App Token? | | --- | --- | --- | --- | --- | | Platform App | Yes | Yes | Yes | No | | Limited Access App | No | No | No | Yes | | Custom Skill | No | No | No | No | ## Client-side ### OAuth 2.0 OAuth 2.0 requires the application to redirect end-users to their browser to login to Box and authorize the application to take actions on their behalf. # When to use OAuth 2.0? Client-side authentication is the ideal authentication method for apps that: - work with users who have existing Box accounts - use Box for identity management, so users know they are using Box - store data within each user account vs. within an application's Service Account Learn about client-side authentication with OAuth 2.0 ## Server-side ### JWT Server-side authentication using JSON Web Tokens (JWT) does not require end-user interaction and, if granted the proper privileges, can be used to act on behalf of any user in an enterprise. Identity is validated using a JWT assertion and public/private keypair. # When to use JWT? Server-side authentication with JWT is the ideal authentication method for apps that: - work with users without Box accounts - use their own identity system - do not want users to know they are using Box - store data within the application's Service Account and not a user's account Learn about server-side authentication with JWT ### Client Credentials Grant Server-side authentication using Client Credentials Grant does not require end-user interaction and, if granted the proper privileges, can be used to act on behalf of any user in an enterprise. Identity is validated using the application's client ID and client secret. # When to use a Client Credentials Grant? Server-side authentication with Client Credentials Grant is the ideal authentication method for apps that: - work with users without Box accounts - use their own identity management system - do not want users to know they are using Box - store data within the application's Service Account and not a user's account Learn about server-side authentication with Client Credentials Grant ### App Token A server-side App Token is an authentication method where the application only has access to read and write data to its own account. This is mainly used by Box View applications. By using this authentication method there is no need to authorize a user as the application is automatically authenticated as the application's Service Account. # When to use App Tokens? Server-side authentication with App Tokens is the ideal authentication method for apps that: - work in an environment that either has no user model, or has users without Box accounts - use their own identity management system - do not want users to know they are using Box - store data within the application's Service Account and not a user's account Learn about server-side authentication with App Tokens ## Comparison The following is a quick overview of the key difference between client-side and server-side authentication. | | OAuth 2.0 | JWT | Client Credentials | App Tokens | | --- | --- | --- | --- | --- | | Requires user involvement? | Yes | No | No | No | | Requires admin approval? | No | Yes | Yes | Yes | | Can act on behalf of other users? | Yes | Yes | Yes | No | | Do users see Box? | Yes | No | No | No | | Can create App Users? | No | Yes | Yes | No | An Access Token is tied to a specific Box user and the way the token has been obtained determines who that user is. For example, when using client-side authentication the token represents the user who granted access to their account, while while when using server-side authentication the token defaults to the application's Service Account. **Reference:** https://developer.box.com/guides/authentication/select/ --- ## Untitled *Type: guide | Category: Authentication * Box API & SSO Many Box Enterprises use Single Sign On (SSO) to authenticate Managed Users logging in to Box. The way an application built on… # Box API & SSO Many Box Enterprises use **Single Sign On** (SSO) to authenticate [Managed Users](page://platform/user-types/#managed-users) logging in to Box. The way an application built on Box Platform interact with an SSO provider depends on the type of application being built. ## Platform Apps with Client-side Authentication When users authenticate with a [Platform App](g://applications/app-types/platform-apps) configured to use [OAuth 2.0](g://authentication/oauth2) Box will detect if the enterprise is configured to use SSO. If it is, Box will redirect the user to their browser and display the enterprise's configured SSO log-in screen. ### SSO Enabled vs Required Enterprises can configure their SSO in one of two ways: **SSO Required** or **SSO Enabled**. When SSO is enabled but not required, managed users will have the option to either: - log in with a Box username and password - log in with their SSO provider When SSO is enabled and required, Box will force all managed users to log in with their enterprise's configured SSO provider. In this case, any user that tries to log in must be configured on the SSO side, in addition to having a Box account matching the email address passed via SAML. It is not possible to exempt a user from SSO in an enterprise with SSO set to required, even if it is only used for platform use cases. ## Platform Apps with Server-side Authentication For [Platform Apps](g://applications/app-types/platform-apps) that use [JWT](g://authentication/jwt) or [Client Credentials Grant](g:///authentication/client-credentials) and [Limited Access Apps](guide://applications/web-app-integrations) that use [App Token](g://authentication/app-token) authentication, SSO is not used to authenticate with Box. Platform Apps using server-side authentication only use server-to-server API calls to communicate with Box. In this scenario, the way in which an end user is authenticated is determined by the application and not by Box. In other words, end user authentication with the application is determined by the application, while application's authorization to Box is a different matter completely. In these use cases the application authenticates not as a regular Managed User but as a Service Account or App User. These user types do not have access to any Managed User's data by default. For these applications to have access to other Managed User's data they will need explicit [admin approval](g://authorization/custom-app-approval). ## Custom Skills [Custom Skills](g://applications/app-types/custom-skills) are authenticated in a unique way where the application is provided with a unique set of access tokens for every skill event. In this case, the application does not directly interact with the users and therefore SSO is not involved. Even when using Skills, a user uploading a file to a folder that might trigger a Skill event would still need to log in to the web or mobile app. This log in would require them to use SSO where needed. **Reference:** https://developer.box.com/guides/authentication/sso/ --- ## Untitled *Type: guide | Category: Authorization * Platform App Approval Server authentication applications using JWT or Client Credentials Grant must be authorized by a Box Admin before use… # Platform App Approval Server authentication applications using [JWT](g://authentication/jwt) or [Client Credentials Grant](g://authentication/client-credentials) must be authorized by a Box Admin before use. Unpublished applications using [OAuth 2.0](g://authentication/oauth2) authentication may require enablement by a Box Admin if they are [inactive by default](g://security/#enterprise-settings-and-authorization). A Box Admin needs an application's Client ID in order to properly authorize or enable it in the Admin Console. You can use [Integrations](g://applications) view to quickly look up the authorization and enablement status of your application. ## Approval Notifications A semi-automated process to submit an app approval is available in the Developer Console for all platform application types. ### Server authentication apps Navigate to the **Authorization** tab for your application in the [Developer Console](https://app.box.com/developers/console). ### User authentication apps Navigate to the **Enablement** tab for your application in the [Developer Console](https://app.box.com/developers/console). Submitting the application for approval will send an email to your enterprise's Primary Admin to approve the application. When a Box Admin approves or declines your request, you will get an email with the decision. More information on this process is available in our [support article on app authorization](https://support.box.com/hc/en-us/articles/360043697014-Authorizing-Apps-in-the-Box-App-Approval-Process). ## Manual Approval The following steps provide instructions on how to manually approve the application. ### As a developer 1. Navigate to the **Configuration** tab for your application in the [Developer Console](https://app.box.com/developers/console). 2. Scroll down to the OAuth 2.0 Credentials section and copy the **Client ID** value to provide to a Box Admin. Alternatively, hover over the application in the [My Platform Apps](g://applications) view to look up the **ClientID** and then copy it using the `copy` button. # Finding a Box Admin If you don't know your enterprise Admin, go to your Box Account Settings page and scroll to the bottom. If an admin contact is set you should see their contact information under **Admin Contact**. ### As an Admin 1. Navigate to the [Admin Console](https://app.box.com/master/settings/custom) and select the **Integrations** tab (1) from the left navigation panel. 2. Click the **Platform Apps Manager** tab (2) at the top of your screen. 3. For both Server and User Authentication Apps screens, click the **Add Platform App** (3) button in the top right corner to add a new app. 4. Alternatively, you can use the Platform Apps Manager table menu (4) to authorize and enable apps. #### Server Authentication Apps #### User Authentication Apps In the popup that appears, enter the client ID for the application that the developer collected from the **Configuration** tab of the [Developer Console](https://app.box.com/developers/console). ## Re-authorization on changes When the application's scopes or access level change the application needs to be re-authorized. Repeat the process above and request a new Access Token for the new changes to take effect. In the same section where the application was initially authorized, an Admin can re-authorize the application by clicking on the ellipses to the right of the application name to **Reauthorize App**. **Reference:** https://developer.box.com/guides/authorization/custom-app-approval/ --- ## Untitled *Type: guide | Category: Authorization * Common Errors Unable to retrieve service of enterprise app authorization This error indicates that the entered client ID entered is invalid… # Common Errors ## Unable to retrieve service of enterprise app authorization This error indicates that the entered client ID entered is invalid. To resolve this issue, please re-copy the Client ID from the Developer Console and ensure you are not including any trailing characters. ## Something went wrong with adding the app authorization This error indicates you are a Box co-admin granted privileges to view, but not edit enterprise settings. You will need to have your Box Admin grant edit privileges in order to successfully authorize an application. ## Disabled by Administrator This error indicates that not all authorization requirements are satisfied. Please review our [authorization guide](g://authorization) to confirm the necessary steps for a given application. **Reference:** https://developer.box.com/guides/authorization/common-errors/ --- ## Untitled *Type: guide | Category: Authorization * Custom Skill Approval Custom Skills will must be enabled on a folder by the enterprise's Box Admin before use. As a developer As the… # Custom Skill Approval Custom Skills will must be enabled on a folder by the enterprise's Box Admin before use. ## As a developer As the developer, navigate to the application in the [Developer Console](https://app.box.com/developers/console) and copy the application's Client ID to provide to your admin. # Finding a Box Admin If you don't know your enterprise Admin, go to the Box [account settings][settings] page and scroll to the bottom. If an Admin contact is set you should see contact information under "Admin Contact". ## As an Admin To enable a Custom Skill application, navigate to the [Skills section of the Box Admin Console](https://app.box.com/master/skills) and click the "Add Skill" link to add a new skill. Enter the Client ID (API key) for the Custom Skill application. This is the Client ID provided by the developer. Click "Next" and select the folder(s) where the Box Skill application should operate. There are two options here: - **All content in your company** authorizes the Skill at the root folder of every user. This results in every file uploaded to any folder being processed by the Box Skill application. - **Select a list of folders** enables the application to a specific folder or set of folders on which the Skill application operates. Click "Enable Skill" and accept the terms and agreements. The Custom Skill is now enabled. Any new content added to the selected folder(s) will now trigger an event to be sent to the Invocation URL configured in the Box [Developer Console](https://app.box.com/developers/console). # 10 Skill applications per enterprise There is a limit of 10 enabled Skills per enterprise at any given time. Please contact your Box Sales Representative to enable more skills in an enterprise. **Reference:** https://developer.box.com/guides/authorization/custom-skill-approval/ --- ## Untitled *Type: guide | Category: Authorization * Authorization Some applications require explicit Admin authorization before use with an enterprise. The steps an Admin needs to take are… # Authorization Some applications require explicit Admin authorization before use with an enterprise. The steps an Admin needs to take are dependent on the developer-selected authentication method and enabled enterprise settings. ## Authentication methods The following [authentication methods](g://authentication/select) always require explicit Admin authorization: - [Server Authentication (with JWT)](g://authentication/jwt) - [Server Authentication (with Client Credentials Grant)](g://authentication/client-credentials) - [Custom Skill](g://applications/app-types/custom-skills) These authentication methods automatically generate a [Service Account](page://platform/user-types/#service-account). With the right [scopes](g://api-calls/permissions-and-errors/scopes) enabled, a Service Account can perform many Admin actions, thus requiring Admin authorization before use. [OAuth 2.0](g://authentication/oauth2) and [App Token](g://authentication/app-token) apps may also require explicit Admin authorization based on enabled enterprise settings. ## Enterprise settings Subsequent steps are required if any of the following enterprise settings are enabled: - Disable published third party apps by default - Disable unpublished apps by default - Require manual Admin authorization for Limited Access Apps These [settings](https://support.box.com/hc/en-us/articles/360044196653-Managing-custom-apps) can be found by navigating to: **Admin Console** > **Integrations** > **Platform Apps Manager** > **Platform Apps Settings** button. Published Platform Apps are any applications that can be found under Integrations. ## Required actions To see what steps an Admin must complete for a given app, review the following scenarios. **Disable published third party apps by default**: | Authentication Method | Enabled | Disabled | | --- | --- | --- | | OAuth 2.0 | Set to available in individual app controls | Ready for use | | Server Authentication (with JWT) | N/A | N/A | | Server Authentication (client credentials) | N/A | N/A | | App Token Authentication | N/A | N/A | **Disable unpublished apps by default**: | Authentication Method | Enabled | Disabled | | --- | --- | --- | | OAuth 2.0 | Enable in Integrations > Platform Apps Manager > User Authentication Apps > Select Platform App > Use More menu to enable the app. | Ready for use | | Server Authentication (with JWT) | Authorize and enable in Integrations > Platform Apps Manager > Server Authentication Apps Select Platform App > Use More menu to authorize the app. | Authorize in Integrations > Platform Apps Manager > Server Authentication Apps > Select Platform App > Use More menu to authorize the app. | | Server Authentication (client credentials) | Authorize and enable in Integrations > Platform Apps Manager > Server Authentication Apps > Select Platform App > Use More menu to authorize the app. | Authorize in Integrations > Platform Apps Manager > Server Authentication Apps > Select Platform App > Use More menu to enable the app. | | App Token Authentication | Authorize and enable in Integrations > Platform Apps Manager > Server Authentication Apps > Server Authentication Apps > Select Platform App > Use More menu to enable the app. | Ready for use | **Require manual Admin authorization for Limited Access Apps**: | Authentication Method | Enabled | Disabled | | --- | --- | --- | | OAuth 2.0 | N/A | N/A | | Server Authentication (with JWT) | N/A | N/A | | Server Authentication (client credentials) | N/A | N/A | | App Token Authentication | Authorize in Integrations > Platform Apps Manager > Server Authentication Apps Select Platform App > Use More menu to authorize the app. | Automatically authorized and enabled upon creation | **Reference:** https://developer.box.com/guides/authorization/ --- ## Untitled *Type: guide | Category: Authorization * Limited Access App Approval Limited Access Integrations are automatically authorized for use in an enterprise upon creation. However, if the… # Limited Access App Approval Limited Access Integrations are automatically authorized for use in an enterprise upon creation. However, if the enterprise setting to **Require manual Admin authorization for Limited Access Apps** is enabled, an Admin must preform additional steps. ## Approval Notifications A semi-automated process to submit an app approval is available in the Developer Console. Navigate to the **Authorization** tab for your application in the [Developer Console](https://app.box.com/developers/console). Submitting the application for approval will send an email to your enterprise's Primary Admin to approve the application. More information on this process is available in our [support article on app authorization](https://support.box.com/hc/en-us/articles/360043697014-Authorizing-Apps-in-the-Box-App-Approval-Process). ## Manual Approval The following steps provide instructions on how to manually approve the application. ### As a developer 1. Navigate to the **Configuration** tab for your application in the [Developer Console](https://app.box.com/developers/console). 2. Scroll down to the OAuth 2.0 Credentials section and copy the **Client ID** value to provide to a Box Admin. Alternatively, hover over the application in the [My Platform Apps](g://applications) view to look up the **ClientID** and then copy it using the `copy` button. # Finding a Box Admin If you don't know your enterprise Admin, go to your Box Account Settings page and scroll to the bottom. If an admin contact is set you should see their contact information under **Admin Contact**. ### As an Admin As a Box Admin, navigate to the [Admin Console](https://app.box.com/master/settings/custom) and select the **Integrations** > **Platform Apps Manager** > **Add Platform App** In the popup that appears, enter the Client ID for the application that the developer collected from the **Configuration** tab of the Developer Console. ## Re-authorization on changes When the application's scopes or access level change the application needs to be re-authorized. Repeat the process above and request a new Access Token for the new changes to take effect. In the same section where the application was initially authorized, an Admin can re-authorize the application by clicking on the ellipses to the right of the application name to **Reauthorize App**. **Reference:** https://developer.box.com/guides/authorization/limited-access-approval/ --- ## Untitled *Type: guide | Category: Authorization * Platform App Approval Server authentication applications using JWT or Client Credentials Grant must be authorized by a Box Admin before use… # Platform App Approval Server authentication applications using [JWT](g://authentication/jwt) or [Client Credentials Grant](g://authentication/client-credentials) must be authorized by a Box Admin before use. Unpublished applications using [OAuth 2.0](g://authentication/oauth2) authentication may require enablement by a Box Admin if they are [inactive by default](g://security/#enterprise-settings-and-authorization). A Box Admin needs an application's Client ID in order to properly authorize or enable it in the Admin Console. You can use [My Platform Apps](g://applications) view to quickly look up the authorization and enablement status of your application. ## Approval Notifications A semi-automated process to submit an app approval is available in the Developer Console for all platform application types. ### Server authentication apps Navigate to the **Authorization** tab for your application in the [Developer Console](https://app.box.com/developers/console). ### User authentication apps Navigate to the **Enablement** tab for your application in the [Developer Console](https://app.box.com/developers/console). Submitting the application for approval will send an email to your enterprise's Primary Admin to approve the application. When a Box Admin approves or declines your request, you will get an email with the decision. More information on this process is available in our [support article on app authorization](https://support.box.com/hc/en-us/articles/360043697014-Authorizing-Apps-in-the-Box-App-Approval-Process). ## Manual Approval The following steps provide instructions on how to manually approve the application. ### As a developer 1. Navigate to the **Configuration** tab for your application in the [Developer Console](https://app.box.com/developers/console). 2. Scroll down to the OAuth 2.0 Credentials section and copy the **Client ID** value to provide to a Box Admin. Alternatively, hover over the application in the [My Platform Apps](g://applications) view to look up the **ClientID** and then copy it using the `copy` button. # Finding a Box Admin If you don't know your enterprise Admin, go to your Box Account Settings page and scroll to the bottom. If an admin contact is set you should see their contact information under "Admin Contact". ### As an Admin 1. Navigate to the [Admin Console](https://app.box.com/master/settings/custom) and select the **Apps** tab (1) from the left navigation panel. 2. Click the **Platform Apps Manager** tab (2) at the top of your screen. 3. For both Server and User Authentication Apps screens, click the **Add App** (3) button in the top right corner to add a new app. 4. Alternatively, you can use the Platform Apps Manager table menu (4) to authorize and enable apps. #### Server Authentication Apps #### User Authentication Apps In the popup that appears, enter the client ID for the application that the developer collected from the **Configuration** tab of the [Developer Console](https://app.box.com/developers/console). ## Re-authorization on changes When the application's scopes or access level change the application needs to be re-authorized. Repeat the process above and request a new Access Token for the new changes to take effect. In the same section where the application was initially authorized, an Admin can re-authorize the application by clicking on the ellipses to the right of the application name to **Reauthorize App**. **Reference:** https://developer.box.com/guides/authorization/platform-app-approval/ --- ## Untitled *Type: guide | Category: Box AI * Box AI Box AI API allows you to use Box AI functionality in your platform applications. For example, you can implement Box AI question and… # Box AI Box AI API allows you to use Box AI functionality in your platform applications. For example, you can implement Box AI question and answer functionality in your third party application, or generate content right in your product’s content editor. ## Box AI API capabilities Box AI API provides a number of capabilities designed to help you leverage Large Language Models (LLMs) in your application workflows. Currently, you can ask Box AI to answer user questions, summarize the document content, or generate text you can use in your documents. You can also use Box AI to extract metadata from the provided input, such as a schema or metadata template. The [Box AI for UI Elements](g://embed/ui-elements/preview#box-ai-ui-element) functionality allows embedding Box AI in your apps. ### Ask questions to Box AI You can use Box AI API to ask questions about the content, for example, while working on documents you store in Box. Box AI can answer your questions about the content or generate a summary based on the file you supply. Have a look at [Box AI for Documents](https://support.box.com/hc/en-us/articles/22158484213267-Box-AI-for-Documents) to see an example of how users can interact with Box AI while working with their documents. ### Generate text with Box AI You can use Box AI API to generate text from scratch, from existing text within a Box Note, or based on a given document in Preview. For example, you can ask Box AI to create a template or meeting agenda based on an article you are viewing in Preview. Another example is Box Notes that uses Box AI to generate text and refine the already existing note content. For details, see [Box AI for Notes](https://support.box.com/hc/en-us/articles/22198577315347-Box-AI-for-Notes). ### Metadata extraction The [`POST /2.0/ai/extract`](e://post_ai_extract) and [`POST /2.0/ai/extract_structured`](e://post_ai_extract_structured) endpoints allow you to extract data from the provided input and return them in a form of key-value pairs. - Use the `extract_structured` endpoint to extract data according to a pre-defined structure obtained from the metadata template, or a set of fields. - Use the `extract` endpoint to extract data from a file using a prompt that can include a stringified version of formats such as JSON or XML, or even plain text. ### Configuration overrides You can use the `ai_agent` parameter available in the Box AI API requests to override the default agent configuration and introduce your own custom settings. For details, see [AI agent default configuration](g://box-ai/ai-agents/get-agent-default-config). ### Box AI for UI Elements Box AI for UI Elements is available in Content Preview allows asking questions about documents directly within platform applications. Check out how to use the [Box AI for UI Elements](g://embed/ui-elements/preview#box-ai-ui-element) to embed Box AI functionality in your projects. ## Supported languages Box AI works in a number of languages including English, Japanese, French, Spanish, and many more. However, the underlying models are primarily trained on English language documents. This means that prompts in other languages may return answers of lower quality than in English. Tests have shown satisfactory results for summarizing, checking grammar and spelling, and answering questions, but bear in mind that the results may be different than in English. Switch the language to Japanese to get better results for this language. ## Box AI API in User Activity Report (UAR) [User Activity Reports](https://support.box.com/hc/en-us/articles/4415012490387-User-Activity-Report) provide an overview of the actions the users are taking in Box. Box Admins use this report to view the actions taken by their users within a given time period, and this includes interactions with Box AI. The report contains the following action types that Box admins can select to get details for Box AI: - **AI query**: The user queried Box AI and received a response. - **Failed AI query**: The user queried Box AI but did not receive a response. **Reference:** https://developer.box.com/guides/box-ai/ --- ## Untitled *Type: guide | Category: Box Relay * Get Workflows The get workflows endpoint can be used to grab all workflows on a particular folder. It will return all workflows regardless… # Get Workflows The [get workflows](e://get-workflows) endpoint can be used to grab all workflows on a particular folder. It will return all workflows regardless of if there is a flow of type `WORKFLOW_MANUAL_START`. For more information on how to use these endpoints, refer to our [blog](https://medium.com/box-developer-blog/manual-start-workflow-api-box-relay-4f8d0f51b7a4) post. **Reference:** https://developer.box.com/guides/box-relay/get-workflows/ --- ## Untitled *Type: guide | Category: Box Relay * Box Relay Box Relay is a workflow automation application that enables Box power users to automate and accelerate business processes centered… # Box Relay [Box Relay](https://support.box.com/hc/en-us/articles/360044196213-Introducing-Box-Relay) is a workflow automation application that enables Box power users to automate and accelerate business processes centered around content. Currently, there are two API [endpoints](resource://workflow) developers can use with more planned to be released in the future. Both were created to work directly with [manual start flows](https://support.box.com/hc/en-us/articles/360044628853-Creating-and-Running-a-Manual-Start-Workflow). For more information on how to use these endpoints, refer to our [blog](https://medium.com/box-developer-blog/manual-start-workflow-api-box-relay-4f8d0f51b7a4) post. ## Required Scopes The following [scopes](g://api-calls/permissions-and-errors/scopes) must be enabled for an application before use of Box Relay's endpoints. - [Read all files and folders stored in Box](g://api-calls/permissions-and-errors/scopes/#read-all-files-and-folders) - [Write all files and folders stored in Box](g://api-calls/permissions-and-errors/scopes/#read-and-write-all-files-and-folders) - [Manage Box Relay](g://api-calls/permissions-and-errors/scopes/#manage-box-relay) Depending on the selected authentication method and enterprise's settings, your application may require Admin authorization or re-authorization before successful use of any newly selected scopes. ## Rate Limits Please see our [rate limit guide](g://api-calls/permissions-and-errors/rate-limits/#per-api-rate-limits) for more information. ## Testing Due to the feature parity, it may be useful to familiarize yourself with [Box Relay functionality using the Box web app](https://support.box.com/hc/en-us/articles/360044628853-Creating-and-Running-a-Manual-Start-Workflow) before leveraging the API. As with all API endpoints, we recommend testing via [developer sandbox environment](https://support.box.com/hc/en-us/articles/360043697274-Managing-developer-sandboxes-for-Box-admins) to eliminate the risk of impacting production content. **Reference:** https://developer.box.com/guides/box-relay/ --- ## Untitled *Type: guide | Category: Box Relay * Start Workflow The start workflow endpoint can be used to start a flow within a workflow of type WORKFLOW_MANUAL_START. Other flow types… # Start Workflow The [start workflow endpoint](e://post-workflows-id-start) can be used to start a flow within a workflow of type `WORKFLOW_MANUAL_START`. Other flow types cannot be started. If you have the flow set up to accept configurations at runtime, you must send in the optional `outcomes` array object. For more information on how to use this endpoint, refer to our [blog](https://medium.com/box-developer-blog/manual-start-workflow-api-box-relay-4f8d0f51b7a4) post. **Reference:** https://developer.box.com/guides/box-relay/start-workflow/ --- ## Untitled *Type: guide | Category: Box MCP Server * Box MCP Server Model Context Protocol (MCP) is an open protocol that standardizes how applications provide context to LLMs. MCP servers make… # Box MCP Server Model Context Protocol ([MCP](https://modelcontextprotocol.io/introduction)) is an open protocol that standardizes how applications provide context to LLMs. MCP servers make building advanced integrations simpler and less time consuming. Box MCP Server allows third party AI agents from platforms like Copilot Studio, Cursor, Claude for Desktop to access Box content seamlessly. It extends the agent’s capabilities by allowing it to perform actions related to content stored in Box. There are two types of Box MCP Servers: - remote Box MCP Server which you can create or enable in the Box Admin Console. It is hosted directly in Box. - self-hosted Box MCP Server which you can clone and host on your local machine, as this version in an open source Box Developer community project. Check the detailed guides on both types of Box MCP Servers, as the level of the implemented tools differs. Learn how to enable Box MCP Server: Enable the remote Box MCP Server in the Admin Console. It is hosted directly in Box. Beta An open source Box Developer community project. You can clone this Box MCP Sever and host it on your local machine. Open source Watch an interview with Box CTO, Ben Kus, and learn how MCP empowers AI agents to work dynamically across platforms, reducing the development effort. **Reference:** https://developer.box.com/guides/box-mcp/ --- ## Untitled *Type: guide | Category: Box Sign * Cancel Box Sign Request A Box Sign request, that has not yet been signed or declined, can be cancelled using the cancel Box Sign request… # Cancel Box Sign Request A Box Sign request, that has not yet been signed or declined, can be cancelled using the [cancel Box Sign request endpoint](e://post-sign-requests-id-cancel). Any outstanding signers will no longer be able to sign the document. Only the user who created the request, the requester, is able to cancel it. A request cannot be cancelled if it was declined, fully signed, or the document is still converting. **Reference:** https://developer.box.com/guides/box-sign/cancel-sign-request/ --- ## Untitled *Type: guide | Category: Box Sign * Create Box Sign Request At minimum, to create Box Sign request you need the a file you want to be signed, a destination folder for the… # Create Box Sign Request At minimum, to [create Box Sign request](e://post-sign-requests) you need the a file you want to be signed, a destination folder for the signed document/[signing log](https://support.box.com/hc/en-us/articles/4404095202579-Viewing-the-signing-log), and signers. Creation of the CFR Part 11 signature requests with public API is not supported. See [21 CFR Part 11 Compliance Support](https://support.box.com/hc/en-us/articles/24169443030163) for more information. ## Document preparation Preparing a document prior to sending a Box Sign request allows developers to add date, text, checkbox, and/or signature placeholders for signers. This can be done with UI or [tags](https://support.box.com/hc/en-us/articles/4404085855251-Creating-templates-using-tags) directly in the document. If this is not done, signers receive an unprepared document and can place signatures and fields at their own discretion. However, developers can leverage controls in the request that allow them to turn features for the unprepared document on and off. Setting `is_document_preparation_needed` to `true` provides a `prepare_url` in the response. Visiting this link in your browser allows you to complete document preparation and send the request in the UI. To learn more about document tags, please see our [support article](https://support.box.com/hc/en-us/articles/4404085855251-Creating-templates-using-tags). Prefill tags created in a template with the Box web app cannot be accessed from the API. ## Files Each Box Sign request begins with a file that needs to be signed. If the file does not already exist in Box, it must be [uploaded](e://post-files-content), in a separate API call, prior to creating the request. Multiple files can be signed in one request. File ID of the first file in a request is specified in the `source_files` body parameter. The requester must have download privileges to the file in Box. Review our [collaboration levels](https://support.box.com/hc/en-us/articles/360044196413-Understanding-Collaborator-Permission-Levels) to ensure this requirement is met. Supported file types include: - All [documents](g://representations/supported-file-types/#documents) - All [presentations](g://representations/supported-file-types/#presentations) - Images: `png`, `jpg`, `jpeg`, `tiff` only - Text-based files: `.csv`, `.txt` only All file types are converted to `.pdf` for the signature process. This converted document can be found in the `parent_folder` upon successfully sending a request. This means that the final, signed document is a `.pdf`, regardless of the original file type. As each signer completes the request, Box Sign will automatically add a new file version. File size limits are determined by your account type. Please see our [uploads guide](g://uploads/direct) for more information. ## Parent folder The folder ID specified in the `parent_folder` body parameter determines the destination of the final signed document and [signing log](https://support.box.com/hc/en-us/articles/4404095202579-Viewing-the-signing-log). This folder cannot be the All Files or root level, which is represented by folder ID `0`. ## Signers Each signer must be assigned a [role](https://support.box.com/hc/en-us/articles/4404105660947-Roles-for-signers): `signer`, `approver`, or `final copy_reader`. If the requester is not given a role, a signer with the role `final_copy_reader` is automatically created. This means they only receive a copy of the final, signed document and [signing log](https://support.box.com/hc/en-us/articles/4404095202579-Viewing-the-signing-log). Signers do not need to have an existing Box account, nor create one, in order to sign documents. Unlike other API endpoints, signers are invited by email address and not Box `user_id`. If necessary, signers can log in to Box before signing the request. In such case set the parameter `login_required` to `true` for signers. If the signer does not have an existing account, they will have an option to create a free Box account. Box Sign will only attempt to send signing emails to the email addresses provided for signers in the request. For Box users, this does not include email aliases unless specified. Please double check to ensure all provided signer email addresses are valid. ### Inputs The `inputs` parameter represents placeholders that the user can interact with. The `document_tag_id` parameter can be populated with data you want to pass when creating a sign request. ## Templates You can create a sign request using a template. To do so, you must provide the `template_id` parameter. See [this guide](g://box-sign/sign-templates) to learn more about using templates when creating sign requests. ## Redirects The URLs specified in the `redirect_url` and `declined_redirect_url` allow you to redirect signers to a custom landing page after signing or declining the sign request. For example, if you integrate your application with Box Sign, you can redirect signers back to your application or to a custom landing page. You can set redirect URLs globally for all signers as well as for specific signers only. This means that Box Sign will use specific URLs for signers of your choice, and global settings for the rest. If you don't configure any redirect URLs Box Sign will redirect signers to a default page. The default page includes the following note: "Once the document has been completed by all parties, a limited-time link to a finalized copy will be be emailed to you, and, if you have a Box account, a copy will be placed in your account." If you decide to redirect the signers to a different page, this information will not be available to signers. ## Multiple signers and signing order Signing order is determined by ordering the provided `order` numbers from smallest to largest. If two numbers are the same, signers will receive the request at the same time. Initially, only the signer(s) with the lowest assigned `order` number will receive a Box Sign request email. Once they sign, the following user(s) will an email and so on. Box Sign automatically adds a new version of the document to the `parent_folder` as each user signs. If any signer declines, any remaining signers will not receive a Box Sign request email. The overall request is declined. ## Request status - `converting`: The file is converted to a `.pdf` for the signing process once the sign request is sent. - `error_converting`: An issue was encountered while converting the file to a `.pdf`. - `created`: If `document_preparation_is_needed` is set to `true`, but the `prepare_url` has not yet been visited. - `sent`: The request was successfully sent, but no signer has interacted with it. - `error_sending`: An issue was encountered while sending the request. - `viewed`: Once the first, or only, signer clicks on **Review document** in the signing email or visits the signing URL. - `downloaded`: The signing document was downloaded by signer. - `signed`: All signers completed the request. - `signed and downloaded`: The signing document was signed and downloaded by signer. - `declined`: If any signer declines the request. - `cancelled`: If the request is cancelled via UI or API. - `expired`: The date of expiration has passed with outstanding, incomplete signatures. - `finalizing`: If all signers have signed the request, but the final document with signatures and the signing log has not been generated yet. - `error_finalizing`: If the `finalizing` phase did not complete successfully. Encountering an error status requires creating a new sign request to retry. **Reference:** https://developer.box.com/guides/box-sign/create-sign-request/ --- ## Untitled *Type: guide | Category: Box Sign * Embedded Sign client Box Embed allows you to embed Box Sign features into your own website. This way, users don't have to leave the website… # Embedded Sign client [Box Embed](g://embed/box-embed) allows you to embed Box Sign features into your own website. This way, users don't have to leave the website, go to Box Sign to sign the document, and then come back to finish the process. Instead, Box Embed allows them to complete the signing process within the external website. To integrate Box Sign experience within your own website, you need the `iframable_embed_url` parameter that is specifically designed to allow signing documents within the HTML `iframe` tag. A sample `iframable_embed_url` looks as follows: ``` https://app.box.com/embed/sign/document/f14d7098-a331-494b-808b-79bc7f3992a3/f14d7098-a331-494b-808b-79bc7f3992a4 ``` To get the `iframeable_embed_url` pass the [`embed_url_external_user_id`](e://post-sign-requests#param-signers-embed_url_external_user_id) parameter for each signer when calling the [create sign request](e://post-sign-requests) endpoint. The returned response will contain a unique `iframeable_embed_url` for that signer. To embed Sign features and make them available to the users, use the URL within the `iframe` tag: ``` <iframe src="https://app.box.com/embed/sign/document/f14d7098-a331-494b-808b-79bc7f3992a3/f14d7098-a331-494b-808b-79bc7f3992a4" width="{pixels}" height="{pixels}" frameborder="0" allowfullscreen webkitallowfullscreen msallowfullscreen ></iframe> ``` For details on working with Box Embed, see [this guide](g://embed/box-embed#programmatically). Box Embed uses the [Cloud Game](g://embed/box-embed#cloud-game) widget to prevent clickjacking. In this case, when the user wants to sign a document, they will have to interact with the widget and drag a cloud to the correct location before proceeding to document signing. **Reference:** https://developer.box.com/guides/box-sign/embedded-sign-client/ --- ## Untitled *Type: guide | Category: Box Sign * Box Sign Programmatically harness the full functionality of the Box Sign web app experience by leveraging Box Sign’s API endpoints to create… # Box Sign Programmatically harness the full functionality of the Box Sign web app experience by leveraging Box Sign’s API endpoints to create, list, resend, and cancel sign requests. ## Enablement The following account types support requests through the Box Sign API: Business, Business Plus, Enterprise, Enterprise Suites, Enterprise Plus, and Enterprise Advanced. To locate your account type, navigate to **Account Settings** and scroll down to the **Account Details** section of the **Account** tab. For Admin details on restricting access, please see our [support article](https://support.box.com/hc/en-us/articles/4404076971155-Enabling-Box-Sign). ## Required scopes The following [scopes](g://api-calls/permissions-and-errors/scopes) must be enabled for an application before use of Box Sign's endpoints. - [Read all files and folders stored in Box](g://api-calls/permissions-and-errors/scopes/#read-all-files-and-folders) - [Write all files and folders stored in Box](g://api-calls/permissions-and-errors/scopes/#read-and-write-all-files-and-folders) - [Manage signature requests](g://api-calls/permissions-and-errors/scopes/#manage-signature-requests) Depending on the selected authentication method and enterprise's settings, your application may require Admin authorization or re-authorization before successful use of any newly selected scopes. ## Events Please see our [events guide](g://events/event-triggers/sign-events) for more information. ## Webhooks Please see our [webhooks guide](g://webhooks/triggers) for more information. ## Rate Limits Please see our [rate limit guide](g://api-calls/permissions-and-errors/rate-limits/#per-api-rate-limits) for more information. ## Testing Due to the feature parity, it may be useful to familiarize yourself with [Box Sign functionality using the Box web app](https://support.box.com/hc/en-us/articles/4404105810195-Sending-a-document-for-signature) before leveraging the API. As with all API endpoints, we recommend testing via [developer sandbox environment](https://support.box.com/hc/en-us/articles/360043697274-Managing-developer-sandboxes-for-Box-admins) to eliminate the risk of impacting production content. **Reference:** https://developer.box.com/guides/box-sign/ --- ## Untitled *Type: guide | Category: Box Sign * Resend Box Sign Request The resend a Box sign request endpoint can be used to resend request emails to any remaining signers. A Box Sign… # Resend Box Sign Request The [resend a Box sign request endpoint](e://post-sign-requests-id-resend) can be used to resend request emails to any remaining signers. A Box Sign request cannot be resent if the [status](g://box-sign/create-sign-request/#request-status) is: `signed`, `cancelled`, `declined`, `expired`, `error_sending`, or `error_converting`. If a Box Sign request was recently sent, you will need to wait 10 minutes before resending. If you try before this time has passed you will receive a 400 error. Reminder emails can be enabled when creating a Box Sign request to avoid the need to resend the request. **Reference:** https://developer.box.com/guides/box-sign/resend-sign-request/ --- ## Untitled *Type: guide | Category: Box Sign * List Box Sign Requests All The get sign requests endpoint can be used to view a list of all Box Sign requests created by the user associated… # List Box Sign Requests ## All The [get sign requests endpoint](e://get-sign-requests) can be used to view a list of all Box Sign requests created by the user associated with the passed Access Token. ## By ID The [get sign requests by ID endpoint](e://get-sign-requests-id) can be used to view information about a specific Box Sign request. This endpoint requires the sign request's ID, which can be obtained by using the [get all Box Sign requests endpoint](e://get-sign-requests) or in the response when [creating a Box Sign request](e://post-sign-requests). **Reference:** https://developer.box.com/guides/box-sign/list-sign-requests/ --- ## Untitled *Type: guide | Category: Box Sign * Create Sign Request with Sign Template The Sign Request API allows you to use a predefined Box Sign template when creating a sign request… # Create Sign Request with Sign Template The Sign Request API allows you to use a predefined Box Sign template when creating a sign request. The template includes placeholders that are automatically populated with data when creating the request. ## Create Template Start with creating a Box Sign template that includes `text`, `date`, and `signature` fields you will need for you request. See the [template guides](https://support.box.com/hc/en-us/articles/4404094944915-Creating-templates) guide for detailed instructions. ## Get the Template ID To send a sign request, you need to pass the ID of the template you want to use. List the templates to find the `template_id`. The response is similar to the following one (abbreviated for guide purposes). For the full response example, see [Box Sign template API](e://get-sign-templates#response-example). You can also learn more about the specific parameters in the [Create Sign Request](e://post-sign-requests) guide. ``` "entries": [ { "id": "6ae28666-03c4-4ac1-80db-06a90d3b1361", "name": "Contract.pdf", "parent_folder": { "id": "157064745449", "etag": "0", "type": "folder", "sequence_id": "0", "name": "My Sign Requests" }, "source_files": [ { "id": "1216382236853", "etag": "0", "type": "file", "sequence_id": "0", "sha1": "ca9c75cda0d5e3c3c9b0a1e6d42cb5e29a211ab6", "file_version": { "id": "1327286673653", "type": "file_version", "sha1": "ca9c75cda0d5e3c3c9b0a1e6d42cb5e29a211ab6" } } ], "signers": [ { "email": "", "label": "reader", "public_id": "4Z8QZZV4", "role": "final_copy_reader", "is_in_person": false, "order": 1, "inputs": [...] }, { "email": "", "label": "signer1", "public_id": "4Z8QZZV4", "role": "signer", "is_in_person": false, "order": 1, "inputs": [...] }, { "email": "", "label": "signer2", "public_id": "13VK8794", "role": "signer", "is_in_person": false, "order": 1, "inputs": [ { "document_tag_id": "signer2_full_name", "id": "da431975-55c5-4629-86ae-3fb12dda1386", "type": "text", "text_value": null, "is_required": true, "content_type": "full_name", ... }, { "document_tag_id": null, "id": "b5a76a22-8d48-456e-a012-22a12fc91eb7", "type": "signature", ... }, { "document_tag_id": null, "id": "7e0cc4ee-b878-4739-afde-acbf69b117b2", "type": "date", "date_value": null, ... } ], } ] ... } ] ``` ## Create the sign request Follow these steps to [create sign request](e://post-sign-requests) using a template: In the request body, provide the `template_id`: ``` { "template_id": "6ae28666-03c4-4ac1-80db-06a90d3b1361", "parent_folder": { "id": "123456789", "etag": "0", "type": "folder", "sequence_id": "0", "name": "My Sign Requests" }, ... } ``` Add the signer email addresses and roles: ``` { "template_id": "6ae28666-03c4-4ac1-80db-06a90d3b1361", "parent_folder": { "id": "157064745449", "etag": "0", "type": "folder", "sequence_id": "0", "name": "My Sign Requests" }, "signers": [ { "email": "signer1@sample.com", "role": "signer" }, { "email": "signer2@sample.com", "role": "signer" } ] } ``` Add the `prefill_tags` to populate the fields. Make sure the signer order is the same as the one displayed on the template. If the template had `signer1` first and then `signer2`, the `POST` request must reflect the same order to assign the proper signers. ``` { "template_id": "6ae28666-03c4-4ac1-80db-06a90d3b1361", "parent_folder": { "id": "123456789000", "etag": "0", "type": "folder", "sequence_id": "0", "name": "My Sign Requests" }, "signers": [ { "email": "signer1@sample.com", "role": "signer" }, { "email": "signer2@sample.com", "role": "signer" } ], "prefill_tags": [ { "document_tag_id": "signer1_full_name", "text_value": "Aaron Levie" }, { "document_tag_id": "signer2_full_name", "text_value": "Albert Einstein" } ] } ``` Send the `POST` request. The response will be similar to the following: ``` { "is_document_preparation_needed": false, ... "signers": [ { "email": "reader@sample.com", "role": "final_copy_reader", }, { "email": "signer1@sample.com", "role": "signer", }, { "email": "signer2@sample.com", "role": "signer", } ], "id": "d02fefd2-15fa-431f-a127-2b4525616ae6", "prefill_tags": [ { "document_tag_id": "signer1_full_name", "text_value": "Aaron Levie", }, { "document_tag_id": "signer2_full_name", "text_value": "Albert Einstein", } ], "source_files": [], "parent_folder": { "id": "123456789000", "type": "folder", "name": "My Sign Requests" }, "name": "Contract.pdf", "type": "sign-request", "status": "created", "sign_files": { "files": [ { "id": "123456789", "type": "file", "name": "Contract.pdf", } ], "is_ready_for_download": true }, "template_id": "6ae28666-03c4-4ac1-80db-06a90d3b1361" } ``` **Reference:** https://developer.box.com/guides/box-sign/sign-templates/ --- ## Untitled *Type: guide | Category: Box Sign * Suppress default Box Sign notifications Box Sign API allows you to suppress the default Box email notifications sent during the Sign… # Suppress default Box Sign notifications Box Sign API allows you to suppress the default Box email notifications sent during the Sign workflow. ​​This feature facilitates the ownership of Box Sign notifications with the following options: You can use a fully-customized email notification template to send emails from your domain. Apart from emails, you can send push notifications or text messages.​ When you choose to suppress Box email notifications, your organization assumes responsibility for ensuring the delivery to Signers of all notifications at the appropriate time in the signing process and with the appropriate content, in compliance with all applicable laws and regulations, including with respect to obtaining Signer consent to the delivery methods used, if applicable. ## Using Box Sign API to suppress default notifications To suppress Box Sign email notifications, you must set the following parameters: Set the `suppress_notifications` parameter in the [`signers`](e://post-sign-requests/#param-signers) object to `true` to turn the notifications off. Set the [`embed_url_external_user_id`](e://post-sign-requests/#param-signers-embed_url_external_user_id) parameter to specify the user who will not receive notifications. This configuration turns off the automatic Box Sign email notifications for a given user. As a result, you can configure and send your own notifications. ``` curl -i -X POST "https://api.box.com/2.0/sign_requests" \ -H "authorization: Bearer <ACCESS_TOKEN>" \ -d '{ "signers": [ { "role": "signer", "email": "example_email@box.com" "suppress_notifications": true "embed_url_external_user_id": "1234" } ], "source_files": [ { "type": "file", "id": "123456789" } ], "parent_folder": { "type": "folder", "id": "0987654321" } }' ``` ## Signing Log entries When Box Sign default notifications are suppressed, the Signing Log will indicate that the sender has suppressed all Box Sign notifications. The log will also provide information on the system used for purposes of notification delivery and the user ID of the signer on your organization’s system, as provided to Box Sign through your API integration. **Reference:** https://developer.box.com/guides/box-sign/suppress-sign-notifications/ --- ## Untitled *Type: guide | Category: CLI * CLI The Box Command Line Interface (CLI) is a tool for making requests to Box APIs from your terminal window or command prompt. Quick start… # CLI The Box Command Line Interface (CLI) is a tool for making requests to Box APIs from your terminal window or command prompt. ## Quick start [Get started using the Box CLI with OAuth 2.0](g://cli/quick-start). ## CLI setup and use - To use [server authentication](g://authentication/jwt) method, check out [this](g://cli/cli-docs/jwt-cli) guide. - To use [CCG authentication](g://authentication/client-credentials) method, check out [this](https://github.com/box/boxcli/tree/main/docs/configure.md#box-configureenvironmentsadd-path) guide. - To perform bulk actions using CLI commands, check out [this](https://github.com/box/boxcli/blob/main/docs/Bulk%20actions/README.md) guide. ## Sample scripts Using the Box CLI is great by itself, but it becomes even more powerful when combined with a Powershell script. We've created a [library](https://github.com/box/boxcli/tree/main/examples) of script templates for you to get started automating repetitive tasks. - [Explore sample scripts](g://cli/scripts/index) ## Advanced The GitHub page also has some additional guides that might be of interest to an advanced CLI user. - [Setup autocomplete](https://github.com/box/boxcli/blob/main/docs/autocomplete.md) - [Configure another app](https://github.com/box/boxcli/blob/main/docs/configure.md#box-configureenvironmentsadd-path) - [Switch accounts](https://github.com/box/boxcli/blob/main/docs/configure.md#box-configureenvironmentsswitch-user-userid) - [Token cache](https://github.com/box/boxcli/blob/main/docs/configure.md#box-configureenvironmentsupdate-name) **Reference:** https://developer.box.com/guides/cli/ --- ## Untitled *Type: guide | Category: Collaborations * Sharing with Groups To share a file or folder with a group of users, create a collaboration using the group ID, the ID of the file or folder… # Sharing with Groups To share a file or folder with a group of users, create a collaboration using the group ID, the ID of the file or folder, and the role or permissions level the group should have when accessing the file or folder. The collaboration roles are `editor`, `viewer`, `previewer`, `uploader`, `previewer uploader`, `viewer uploader`, `co-owner`, or `owner`. For a full description of each role, please refer to our [support documentation](https://support.box.com/hc/en-us/articles/360044196413-Understanding-Collaborator-Permission-Levels). ## Nested objects When creating a collaboration there are two nested objects within the request body: `accessible_by` and `item`. The `accessible_by` object specifies who the item should be shared with and includes a group `id` and the `type`. The `type` field should always be set to `group`. The `item` object specifies what is being shared. It includes a `type` field which should be set as `file` or `folder`, and an `id` for that file or folder. **Reference:** https://developer.box.com/guides/collaborations/groups/ --- ## Untitled *Type: guide | Category: Collaborations * Get Pending Collaborations To get the pending collaborations for a user, call the GET /collaborations API with a status of pending. This… # Get Pending Collaborations To get the pending collaborations for a user, call the `GET /collaborations` API with a `status` of `pending`. This only returns the current list of pending collaborations for a user. This API does not allow for returning all collaborations for a user. **Reference:** https://developer.box.com/guides/collaborations/pending/ --- ## Untitled *Type: guide | Category: Collaborations * Collaborations Collaborations define access permissions for users and groups to files and folders, similar to access control lists. A… # Collaborations Collaborations define access permissions for users and groups to files and folders, similar to access control lists. A collaboration object grants a user or group access to a file or folder with permissions defined by a specific role. The collaboration roles are `editor`, `viewer`, `previewer`, `uploader`, `previewer uploader`, `viewer uploader`, `co-owner`, or `owner`. For a full description of each role, please refer to our [support documentation](https://support.box.com/hc/en-us/articles/360044196413-Understanding-Collaborator-Permission-Levels). **Reference:** https://developer.box.com/guides/collaborations/ --- ## Untitled *Type: guide | Category: Comments * Create Comment To create a comment, call the POST /comments API with the message of the comment, as well as the ID of the file to leave the… # Create Comment To create a comment, call the [`POST /comments`](e://post_comments) API with the message of the comment, as well as the ID of the file to leave the comment on. A comment's message can also mentions users using the `@` sign. To do so, add the string `@[userid:name]` anywhere within the message. The `user_id` is the target user's ID, where the `name` can be any custom phrase. In the Box UI this name will link to the user's profile. Then, pass this string as the `tagged_message` instead of the `message`. **Reference:** https://developer.box.com/guides/comments/create-comment/ --- ## Untitled *Type: guide | Category: Collaborations * Share Content with User To share content with a user, create a collaboration using the user ID or email address, the ID of the content, and… # Share Content with User To share content with a user, create a collaboration using the user ID or email address, the ID of the content, and the role or permissions level the user should have when accessing the content. The collaboration roles are `editor`,`viewer`, `previewer`, `uploader`, `previewer uploader`, `viewer uploader`,`co-owner`, or `owner`. For a full description of each role, please refer to our [support documentation](https://support.box.com/hc/en-us/articles/360044196413-Understanding-Collaborator-Permission-Levels). ## Nested objects When creating a collaboration there are two nested objects within the request body: `accessible_by` and `item`. The `accessible_by` object specifies who the item should be shared with and includes a group `id` and the `type`. The `type` field should always be set to `user`. The `item` object specifies what is being shared. It includes a `type` field which should be set as `file` and an `id` for that file. **Reference:** https://developer.box.com/guides/collaborations/share-content/ --- ## Untitled *Type: guide | Category: Comments * Create Reply To create a reply to a previous comment, call the POST /comments API with the message of the new comment, as well as the ID of… # Create Reply To create a reply to a previous comment, call the [`POST /comments`](e://post_comments) API with the message of the new comment, as well as the ID of the previous comment to leave the reply on. A reply's message can also mentions users using the `@` sign. To do so, add the string `@[userid:name]` anywhere within the message. The `user_id` is the target user's ID, where the `name` can be any custom phrase. In the Box UI this name will link to the user's profile. Then, pass this string as the `tagged_message` instead of the `message`. **Reference:** https://developer.box.com/guides/comments/create-reply/ --- ## Untitled *Type: guide | Category: Comments * Comments Comments are messages generated by Box users on files, allowing users to collaborate together on a file, discussing any feedback… # Comments Comments are messages generated by Box users on files, allowing users to collaborate together on a file, discussing any feedback they might have on the content. Each comment is tied to a specific file and user, and comments can be created as independent comments or as replies to previous comments. **Reference:** https://developer.box.com/guides/comments/ --- ## Untitled *Type: guide | Category: Collections * Add Item to Collection To add an item to a collection, call the PUT endpoint for that specific type of item and pass along a list of… # Add Item to Collection To add an item to a collection, call the `PUT` endpoint for that specific type of item and pass along a list of collection IDs. The only collection that is available via the API is the "Favorites" collection. The ID of this collection is different for every user. ## Add file to collection To add a file to a collection, call the `PUT /files/:id` API and pass along a list of collection IDs. ## Add folder to collection To add a folder to a collection, call the `PUT /folders/:id` API and pass along a list of collection IDs. ## Add web link to collection To add a web link to a collection, call the `PUT /web_links/:id` API and pass along a list of collection IDs. **Reference:** https://developer.box.com/guides/collections/add/ --- ## Untitled *Type: guide | Category: Collections * Collections Collections in Box are a way to group files, folders, and web links without putting them all into a folder together. The only… # Collections Collections in Box are a way to group files, folders, and web links without putting them all into a folder together. The only collection that is available via the API is the "Favorites" collection. The ID of this collection is different for every user. **Reference:** https://developer.box.com/guides/collections/ --- ## Untitled *Type: guide | Category: Collections * List Items in Collections To list all files, folders and web links in a folder call the GET /collections/:id/items API. The only collection… # List Items in Collections To list all files, folders and web links in a folder call the [`GET /collections/:id/items`](e://get_collections_id_items) API. The only collection that is available via the API is the "Favorites" collection. The ID of this collection is different for every user. **Reference:** https://developer.box.com/guides/collections/list-items/ --- ## Untitled *Type: guide | Category: Collections * List User's Collections To list all collections for a user, call the GET /collections API. The only collection that is available via the API… # List User's Collections To list all collections for a user, call the [`GET /collections`](e://get_collections) API. The only collection that is available via the API is the "Favorites" collection. The ID of this collection is different for every user. ## Favorites Collection The only collection that can items can currently be added and removed to via the API is the "Favorites" collection. The ID of the favorites collection is different for every user. To find the user's collection ID for their favorites, list all the user's collections and then find the collection with a `collection_type` of `favorites`. ``` { "entries": [ { "collection_type": "favorites", "id": "12345678", "name": "Favorites", "type": "collection" } ], "limit": 100, "offset": 0, "total_count": 1 } ``` **Reference:** https://developer.box.com/guides/collections/list/ --- ## Untitled *Type: guide | Category: Collections * Remove Item from Collection To remove an item from a collection, call the PUT endpoint for that specific type of item and pass along a list… # Remove Item from Collection To remove an item from a collection, call the `PUT` endpoint for that specific type of item and pass along a list of collection IDs that does not include the ID of the collection that needs to be removed. The only collection that is available via the API is the "Favorites" collection and therefore to remove an item from this collection can be achieved by passing the API an empty array of collections. ## Remove file from collection To remove a file from a collection, call the `PUT /files/:id` API and pass an empty array of collection IDs. ## Remove folder from collection To remove a folder from a collection, call the `PUT /folders/:id` API and pass an empty array of collection IDs. ## Remove web link from collection To remove a web link from a collection, call the `PUT /web_links/:id` API and pass an empty array of collection IDs. **Reference:** https://developer.box.com/guides/collections/remove/ --- ## Untitled *Type: guide | Category: Box Doc Gen * Get started with Box Doc Gen To start generating documents with Box Doc Gen API you will need a platform application and a developer token… # Get started with Box Doc Gen To start generating documents with Box Doc Gen API you will need a platform application and a developer token to authenticate your calls. You also need a Doc Gen template that will serve as an input source for your document. ## Enable Box Doc Gen To use Box Doc Gen, make sure it is enabled by an admin in the Admin Console. If you are a Box Admin, you will find the necessary information in [Enterprise Settings: Content & Sharing Tab](https://support.box.com/hc/en-us/articles/4404822772755-Enterprise-Settings-Content-Sharing-Tab#h_01FYQGK5RW42T07GV985MQ9E9A) documentation. ## Create and upload a Box Doc Gen template To use Box Doc Gen API to generate documents, a Box Doc Gen template must already exist in Box. You have the following options to create a template: - Install the [Box Doc Gen Template Creator add-in for Microsoft Word](https://support.box.com/hc/en-us/articles/36587535449747-Installing-Box-Doc-Gen-Add-in). - Create a Box Doc Gen template [using a JSON file](https://support.box.com/hc/en-us/articles/36148012877843-Creating-a-Box-Doc-Gen-Template-using-JSON-data) or manually create [template tags](https://support.box.com/hc/en-us/articles/36151895655059-Creating-A-Box-Doc-Gen-Template-Manually). ## Create a platform application First you need to create a platform application you will use to make calls. To create an application, follow the guide on [creating platform apps](g://applications/app-types/platform-apps). ## Generate a developer token You need a developer token to authenticate your app when sending requests. To generate a token: 1. Go to **Developer Console** > **My Platform Apps**. 2. Click the **Options menu** button (…) on the right. 3. Select **Generate Developer Token**. The token will be automatically generated and saved to clipboard. You can also open your app, go to **Configuration** > **Developer Token** and generate the token. A developer token is only valid for one hour. For additional details, see [developer token](g://authentication/tokens/developer-tokens). After you generate the token, you can use it in cURL or other clients, such as [Postman](g://tooling/postman), to make calls. ## Use webhooks You can create webhooks to monitor Doc Gen events and automate your business process or workflow. Follow the instructions for [adding webhooks](g://webhooks/v2/create-v2/). Your content type is your Doc Gen template file or folder. The supported [events](g://webhooks/triggers) are: - `DOCGEN_DOCUMENT_GENERATION_STARTED` - `DOCGEN_DOCUMENT_GENERATION_SUCCEEDED` - `DOCGEN_DOCUMENT_GENERATION_FAILED` Information that is posted in a notification: - Trigger name. - Webhook trigger timestamp. - Template file ID. - Template file version ID. - Template file name. - Destination folder. - Generated file ID (if the document generation process succeeds). - Output type (DOCX or PDF). - Reason (if the document generation process fails). **Reference:** https://developer.box.com/guides/docgen/docgen-getting-started/ --- ## Untitled *Type: guide | Category: Box Doc Gen * Box Doc Gen jobs A Box Doc Gen job runs when you make a request to generate a document. The document_generation_data parameter in the POST… # Box Doc Gen jobs A Box Doc Gen job runs when you make a request to generate a document. The `document_generation_data` parameter in the `POST` request is an array of entries that represent Box Doc Gen jobs run to generate a document. Box Doc Gen API allows you to get information about the Box Doc Gen jobs. ## Prerequisites Before you start using Box Doc Gen API, follow the steps listed in the [get started with Box Doc Gen](g://docgen/docgen-getting-started) guide to create a platform app and a Box Doc Gen template. ## List all Box Doc Gen jobs To get a list of all Box Doc Gen jobs that were run, use the `GET /2.0/docgen_jobs` endpoint. You don't have to provide any additional parameters. ## Get a Box Doc Gen job by ID To get a specific Box Doc Gen job, use the `GET /2.0/docgen_jobs_id` endpoint and provide the `job_id`. ## Get Box Doc Gen jobs in batch with a specific ID A single request can generate several documents. In such a case, a separate generation job is run for each document and all these jobs are included in one "batch" meaning a request. To get all jobs performed within one request, use the `GET /2.0/docgen_batch_jobs_id` endpoint and provide the `batch_id`. **Reference:** https://developer.box.com/guides/docgen/docgen-jobs/ --- ## Untitled *Type: guide | Category: Box Doc Gen * Box Doc Gen templates Box Doc Gen API allows you to retrieve information related to Box Doc Gen templates. Prerequisites Before you start… # Box Doc Gen templates Box Doc Gen API allows you to retrieve information related to Box Doc Gen templates. ## Prerequisites Before you start using Box Doc Gen API, follow the steps listed in the [get started with Box Doc Gen](g://docgen/docgen-getting-started) guide to create a platform app and a Box Doc Gen template. ## List Box Doc Gen templates To get a list of all created Box Doc Gen templates, use the `GET /2.0/docgen_templates` endpoint. You don't have to provide any additional parameters. The response will contain an `entries` array listing the already created Box Doc Gen templates. ## Get a Box Doc Gen template by ID To get a specific Box Doc Gen template, use the `GET /2.0/docgen_templates_id` endpoint and provide the `template_id`. The response will contain details of a file that was used as a Box Doc Gen template. ## List all document generation jobs for a template To get a list of all created Box Doc Gen templates, use the `GET /2.0/docgen_template_jobs_id` endpoint and provide the `template_id`. The response will contain a list of Box Doc Gen jobs that were run to generate documents. **Reference:** https://developer.box.com/guides/docgen/docgen-templates/ --- ## Untitled *Type: guide | Category: Box Doc Gen * Generate documents The POST /2.0/docgen_batches endpoint allows you to generate a document using Box Doc Gen template as input… # Generate documents The `POST /2.0/docgen_batches` endpoint allows you to generate a document using Box Doc Gen template as input. ## Prerequisites Before you start using Box Doc Gen API, follow the steps listed in the [get started with Box Doc Gen](g://docgen/docgen-getting-started) guide to create a platform app and a Box Doc Gen template. ## Send a request To generate a document or a set of documents, use the `POST /2.0/docgen_batches` endpoint. ### Parameters To make a call, you need to pass the following parameters. Mandatory parameters are in **bold**. | Parameter | Description | Example | | --- | --- | --- | | file.id | ID of the file to be marked as Box Doc Gen template. | 12345678 | | file.type | The type of provided input. The value is always file. | file | | file_version | The file version of a template. | 12345 | | input_source | The input source for generated document. The value has to be api for all the API-based document generation requests. | api | | output_type | The output file type. | docx, pdf | | destination_folder.id | The ID of the folder where the generated document will be stored. | 12345678 | | destination_folder.type | The type of the destination item. Since the generated files are stored in folders, the value is always folder. | file | | document_generation_data.generated_file_name | The name of the generated file. | New_Template | | document_generation_data.user_input | The JSON data to be used to generate document. | {"id": 2, "name": "Ink Cartridge", "type": "non-fragile"} | ## Use case When your Box Doc Gen template and JSON data is ready, you can make a request to Box Doc Gen API to generate documents. A sample call looks as follows: When the request is being processed, each entry in the `document_generation_data` array is treated as a separate document generation job that Box Doc Gen adds to the document generation queue. Generated documents will be saved in the designated folder. **Reference:** https://developer.box.com/guides/docgen/generate-document/ --- ## Untitled *Type: guide | Category: Box Doc Gen * Box Doc Gen Box Doc Gen allows you to generate business documents such as offer letters, sales contracts, invoices or agreements. You can… # Box Doc Gen Box Doc Gen allows you to generate business documents such as offer letters, sales contracts, invoices or agreements. You can generate documents based on Box Doc Gen templates uploaded to Box, with data fields that can be dynamically filled using Box Doc Gen API. Box Doc Gen only supports the ability to leverage English template tags when using Box Doc Gen templates. We recommend that customers test and review that Box Doc Gen supports their desired language requirements. ## Prerequisites - access to Microsoft Word To use Box Doc Gen, you must have access to Microsoft Word, as it is required for creating and authoring your document templates. You can utilize the Box Doc Gen Add-in for a code-free experience or apply tagging scripts within Word to prepare your documents. Box Doc Gen is designed to facilitate the dynamic generation of business documents, but it is important to note that Box does not have control over users’ access to Microsoft Word. Users must ensure they have the necessary permissions and access to Microsoft Word to create and author document templates effectively. ## Box Doc Gen API capabilities Box Doc Gen API allows you to: - mark documents as Box Doc Gen templates, - generate documents based on Box Doc Gen templates you store in Box, - examine the details of Box Doc Gen templates and document generation jobs. ## Box Doc Gen API version Box Doc Gen API was released in Box API version `2025.0`. All API requests to Box Doc Gen API endpoints must specify a valid API version by setting the `box-version` header to `2025.0`. For more details, see [Box API versioning](g://api-calls/api-versioning-strategy). ## Box Doc Gen workflow 1. Author your Doc Gen Template - Use [Doc Gen Add-in for Microsoft Word](https://support.box.com/hc/en-us/articles/36587535449747-Installing-Box-Doc-Gen-Add-in) to create a template without any code. - You can also leverage [Doc Gen's tagging script](https://support.box.com/hc/en-us/articles/36149723736723-Template-tags-reference) to author the template. 1. [Add the template to Box](https://support.box.com/hc/en-us/articles/36587432368275-Managing-Box-Doc-Gen-Templates-in-Relay) using the Box Doc Gen UI. At this point, you can: - Mark an existing file In Box as Doc Gen template. - Create or Upload a document and mark it as a Box Doc Gen template. 1. [Generate the document](g://docgen/generate-document) using Box Doc Gen API. **Reference:** https://developer.box.com/guides/docgen/ --- ## Untitled *Type: guide | Category: Box Doc Gen * Mark file as Box Doc Gen template You can mark an existing document as a Box Doc Gen template and use it to generate documents. Before you… # Mark file as Box Doc Gen template You can mark an existing document as a Box Doc Gen template and use it to generate documents. ## Before you start Before you start using Box Doc Gen API, follow the steps listed in the [get started with Box Doc Gen](g://docgen/docgen-getting-started) guide to create a platform app and a Box Doc Gen template. ## Send a request To send a request containing your question, use the `POST /2.0/docgen_templates` endpoint and provide the mandatory parameters. ### Parameters To make a call you need to pass the following parameters. Mandatory parameters are in **bold**. | Parameter | Description | Example | | --- | --- | --- | | file.id | ID of the file to be marked as the Box Doc Gen template. | 12345678 | | file.type | The type of provided input. The value is always file. | file | ## Use cases ### Mark a file as Box Doc Gen template The following sample show you how to mark a file to ensure it is recognized as a Box Doc Gen template. The file must be in `.docx` format. ### Remove Box Doc Gen template marking from a file To make sure a file is no longer marked as a Box Doc Gen template, use the `DELETE 2.0/docgen_templates/:template_id` request. **Reference:** https://developer.box.com/guides/docgen/mark-template/ --- ## Untitled *Type: guide | Category: Downloads * Download File To download a file, pass the GET /files/:id/content the ID of the file to get the content for. Download URL When not using the… # Download File To download a file, pass the [`GET /files/:id/content`](e://get_files_id_content) the ID of the file to get the content for. ## Download URL When not using the SDKs, this API call will return a `HTTP 302 Found` status code, with a `location` header containing a link to the download URL, which looks something like this. ``` https://dl.boxcloud.com/d/1/[long-random-string]/download ``` By using the `-L` flag in cURL we are able to automatically follow this redirect. In our SDKs this will result in the binary data to be downloaded. In the API this will result in a download URL being returned via the `location` header. It is possible to [get the download URL](g://downloads/get-url) via the SDKs as well. ## Download URL expiry Although this download URL can be passed to a user's browser to allow them to download the file, the URL does expire and should be requested again for any further downloads. ## File not ready If the file is not ready to be downloaded yet a `retry-after` header will be returned indicating the time in seconds after which the file will be available for the client to download. This response can occur when the file was uploaded immediately before the download request. **Reference:** https://developer.box.com/guides/downloads/file/ --- ## Untitled *Type: guide | Category: Downloads * Download File Version To download a specific file version, pass the GET /files/:id/content the ID of the file to get the content for, as… # Download File Version To download a specific file version, pass the [`GET /files/:id/content`](e://get_files_id_content) the ID of the file to get the content for, as well as its version ID. ## Download URL When not using the SDKs, this API call will return a `HTTP 302 Found` status code, with a `location` header containing a link to the download URL, which looks something like this. ``` https://dl.boxcloud.com/d/1/[long-random-string]/download ``` By using the `-L` flag in cURL we are able to automatically follow this redirect. In our SDKs this will result in the binary data to be downloaded. In the API this will result in a download URL being returned via the `location` header. It is possible to [get the download URL](g://downloads/get-url) via the SDKs as well. ## Download URL expiry Although this download URL can be passed to a user's browser to allow them to download the file, the URL does expire and should be requested again for any further downloads. ## File not ready If the file is not ready to be downloaded yet a `retry-after` header will be returned indicating the time in seconds after which the file will be available for the client to download. This response can occur when the file was uploaded immediately before the download request. **Reference:** https://developer.box.com/guides/downloads/file-version/ --- ## Untitled *Type: guide | Category: Downloads * Download All Files in Folder using SDKs Sometimes an application might want to download all files for a folder. To do so with the SDKs and… # Download All Files in Folder using SDKs Sometimes an application might want to download all files for a folder. To do so with the SDKs and the CLI requires traversing the folder tree, finding every file and downloading it accordingly. To download a ZIP archive, follow [this](g://downloads/zip-archive) guide. ``` using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Box.V2.Config; using Box.V2.JWTAuth; namespace BoxDownloadAllFiles { class Program { static void Main (string[] args) { ExecuteMainAsync ().Wait (); } private static async Task ExecuteMainAsync () { using (FileStream fs = new FileStream ($"./config.json", FileMode.Open)) { var session = new BoxJWTAuth (BoxConfig.CreateFromJsonFile (fs)); var client = session.AdminClient (session.AdminToken ()); var folderId = "987654321"; var folder = await client.FoldersManager.GetInformationAsync (folderId); var folderName = folder.Name; var localFolderPath = Path.Combine (Directory.GetCurrentDirectory (), folderName); ResetLocalFolder (localFolderPath); var items = await client.FoldersManager.GetFolderItemsAsync (folderId, 1000, autoPaginate : true); var fileDownloadTasks = new List<Task> (); var files = items.Entries.Where (i => i.Type == "file"); foreach (var file in files) { fileDownloadTasks.Add (client.FilesManager.DownloadStreamAsync (file.Id).ContinueWith ((t) => { var localFile = File.Create (Path.Combine (localFolderPath, file.Name)); return t.Result.CopyToAsync (localFile); })); } await Task.WhenAll (fileDownloadTasks); } } private static void ResetLocalFolder (string localFolderPath) { if (!Directory.Exists (localFolderPath)) { Directory.CreateDirectory (localFolderPath); } else { foreach (var file in Directory.EnumerateFiles (localFolderPath)) { File.Delete (Path.Combine (localFolderPath, file)); } Directory.Delete (localFolderPath); Directory.CreateDirectory (localFolderPath); } } } } ``` ``` package com.box; import com.box.sdk.BoxConfig; import com.box.sdk.BoxDeveloperEditionAPIConnection; import com.box.sdk.BoxFile; import com.box.sdk.BoxFolder; import com.box.sdk.BoxItem; import java.io.BufferedReader; import java.io.FileOutputStream; import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; public class Playground { public static void main(String[] args) throws Exception { Path configPath = Paths.get("config.json"); Path currentDir = Paths.get("").toAbsolutePath(); try (BufferedReader reader = Files.newBufferedReader(configPath, Charset.forName("UTF-8"))) { BoxConfig boxConfig = BoxConfig.readFrom(reader); BoxDeveloperEditionAPIConnection client = BoxDeveloperEditionAPIConnection.getAppEnterpriseConnection(boxConfig); String folderId = "987654321"; BoxFolder folder = new BoxFolder(client, folderId); String folderName = folder.getInfo().getName(); Path localFolderPath = currentDir.resolve(Paths.get(folderName)); if (!Files.exists(localFolderPath)) { localFolderPath = Files.createDirectory(localFolderPath); } else { localFolderPath = resetLocalFolder(localFolderPath); } for (BoxItem.Info itemInfo: folder) { if (itemInfo instanceof BoxFile.Info) { BoxFile.Info fileInfo = (BoxFile.Info) itemInfo; BoxFile file = new BoxFile(client, fileInfo.getID()); String localFilePath = localFolderPath.resolve(Paths.get(fileInfo.getName())).toAbsolutePath().toString(); FileOutputStream stream = new FileOutputStream(localFilePath); file.download(stream); stream.close(); } } } } static Path resetLocalFolder(Path localFolderPath) throws IOException { Files.list(localFolderPath).forEach(file -> { System.out.println(file.getFileName()); try { Files.delete(file.toAbsolutePath()); } catch (IOException e) {} }); Files.delete(localFolderPath); localFolderPath = Files.createDirectory(localFolderPath); return localFolderPath; } } ``` ``` "use strict"; const box = require("box-node-sdk"); const fs = require("fs"); const util = require("util"); const path = require("path"); let configFile = fs.readFileSync("config.json"); configFile = JSON.parse(configFile); let session = box.getPreconfiguredInstance(configFile); let client = session.getAppAuthClient("enterprise"); client._useIterators = true; let folderId = "987654321"; let folderName; let localFolderPath; client.folders .get(folderId, null) .then(folderInfo => { folderName = folderInfo.name; return client.folders.getItems(folderId, { limit: 1000 }); }) .then(folderItemsIterator => { return autoPage(folderItemsIterator); }) .then(folderItems => { console.log(folderName); console.log(folderItems.length); let files = folderItems.filter(item => { return item.type === "file"; }); console.log(files); localFolderPath = createLocalFolder(folderName); let downloadPromises = []; files.forEach(file => { downloadPromises.push( client.files.getReadStream(file.id, null).then(stream => { let output = fs.createWriteStream( path.join(localFolderPath, file.name) ); stream.pipe(output); }) ); }); return Promise.all(downloadPromises); }) .then(() => { console.log("Downloaded all files..."); console.log(fs.readdirSync(localFolderPath)); }); function createLocalFolder(folderName) { let localFolderName = path.join(__dirname, folderName); try { fs.mkdirSync(localFolderName); } catch (e) { if (e.code === "EEXIST") { resetLocalFolder(localFolderName); fs.mkdirSync(localFolderName); } else { throw e; } } return localFolderName; } function resetLocalFolder(localFolderName) { if (fs.existsSync(localFolderName)) { fs.readdirSync(localFolderName).forEach(localFileName => { console.log(localFileName); fs.unlinkSync(path.join(localFolderName, localFileName)); }); fs.rmdirSync(localFolderName); } } function autoPage(iterator) { let collection = []; let moveToNextItem = () => { return iterator.next().then(item => { if (item.value) { collection.push(item.value); } if (item.done !== true) { return moveToNextItem(); } else { return collection; } }); }; return moveToNextItem(); } ``` It is important to remember that an application needs to have the permissions to access and download the files and folders in question. When the authenticated user does not have access to any of the files or folders, a `HTTP 404 Not Found` error will occur. Lear more about [User Types](page://platform/user-types) in our guides on authentication. **Reference:** https://developer.box.com/guides/downloads/folder/ --- ## Untitled *Type: guide | Category: Downloads * Get Download URL The official Box SDKs return the binary data when downloading a file. To get the download URL for the data instead, use the… # Get Download URL The official Box SDKs return the binary data when downloading a file. To get the download URL for the data instead, use the following SDK methods. # Redirects If you are not using one of our SDKs its important to make sure your HTTP client does not automatically follow HTTP redirects. When redirects are automatically followed, your code will detect the `location` header returned by the API and follow it to get the binary data. ## Download URL expiry Although this download URL can be passed to a user's browser to allow them to download the file, the URL does expire and should be requested again for any further downloads. In most cases the download URL is valid for 15 minutes, after which a new URL needs to be requested. This expiration time may be subject to change without any prior notification. **Reference:** https://developer.box.com/guides/downloads/get-url/ --- ## Untitled *Type: guide | Category: Downloads * Download in Browser Sometimes an application wants to embed a file into a page as a HTML element. An example would be when working with an… # Download in Browser Sometimes an application wants to embed a file into a page as a HTML element. An example would be when working with an audio player. ``` <audio controls> <source src="..." type="audio/mp3"> </audio> ``` In this case, using the regular [download URL](g://downloads/get-url) does not work because the `dl.boxcloud.com` domain does not support [Cross Origin Resource Sharing](g://security/cors). Instead an application can use the following format. ``` https://api.box.com/2.0/files/[FILE_ID]/content?access_token=[ACCESS_TOKEN] ``` # CORS For this to work the application needs to have the domain of the web site hosting this file added to the list of allowed domains in the CORS settings. # Downscope Token Using this method would expose the Access Token to the end user, allowing them to potentially use this token to do more than intended. For this reason we recommend [downscoping](g://authentication/tokens/downscope) this token accordingly. **Reference:** https://developer.box.com/guides/downloads/in-browser/ --- ## Untitled *Type: guide | Category: Downloads * Downloads The Box API allows for downloading files to the application's server, or directly by the end user in a browser. When not to… # Downloads The Box API allows for downloading files to the application's server, or directly by the end user in a browser. ## When not to download Downloading a file is not always the desired solution, especially if the file is only being downloaded for the user to preview, comment, or annotate. In those cases we recommend using one of the ways to embed the Box experience straight into your application. Learn more about embedding Box ## Access Errors It is important to realize that the application needs to have access to the file that is to be downloaded. When the application is a authenticated through JWT or App Tokens, the user is authenticated as a Service Account. This service account does not have access to files besides their own. If this user does not have access to the file the application will receive a `404 Not Found` error. Learn more about different User Types **Reference:** https://developer.box.com/guides/downloads/ --- ## Untitled *Type: guide | Category: Downloads * Download Shared Link To download the file for a Shared Link, first determine the file for the link. You cannot use the shared link to… # Download Shared Link To download the file for a [Shared Link](g://shared-links), first determine the file for the link. You cannot use the shared link to download folders. Create and download the ZIP archive instead. Once the file ID has been determined, the file can be downloaded by passing the `BoxAPI` header to the API. To get the Shared Link for an item the user must have at least viewer-level access to the item. **Reference:** https://developer.box.com/guides/downloads/shared-link/ --- ## Untitled *Type: guide | Category: Downloads * Download ZIP Archive To download all files in a folder or an entire folder structure, you have to create and download a ZIP archive. Create… # Download ZIP Archive To download all files in a folder or an entire folder structure, you have to create and download a ZIP archive. ## Create a ZIP Archive First, you need to create a ZIP archive containing the files or the folder structure. You can include up to 10,000 file and folder IDs unless you reach the account’s upload limit. The response will look similar to the following: ``` { "download_url": "https://dl.boxcloud.com/2.0/zip_downloads/25gvaXcIE4QJlinNiw2oHAQ==ZFs3Q2Xpd7pKBz7OyzXNrUaoW3aJxQRN5znAvyM-KpdEEPdWcQDKU-Dl85Ew/content", "status_url": "https://api.box.com/2.0/zip_downloads/25gvaXcIE4QJlinNiw2oHAQ==ZFs3Q2Xpd7pKBz7OyzXNrUaoW3aJxQRN5znAvyM-KpdEEPdWcQDKU-Dl85Ew/status", "expires_at": "2023-02-28T10:23:54Z", "name_conflicts": [] } ``` ## Extract the ZIP download ID To download the ZIP archive, you need the ZIP download ID. You can find it in the response you got when you created the archive. Go to `status_url` and copy the ID located between`zip_downloads` and `content`: ``` 25gvaXcIE4QJlinNiw2oHAQ==ZFs3Q2Xpd7pKBz7OyzXNrUaoW3aJxQRN5znAvyM-KpdEEPdWcQDKU-Dl85Ew ``` Download URL is valid only for a the time specified in `expires_at` parameter. ## Download files Place the download ID in the file location URL as in the sample below to point to the right files. For downloads that take longer, you can monitor the download status using the [status endpoint](e://get-zip-downloads-id-status). This allows you to inspect the progress of the download as well as the number of items that might have been skipped. If you want to use SDKs to download the contents of the folder, follow [this](g://downloads/folder) guide. **Reference:** https://developer.box.com/guides/downloads/zip-archive/ --- ## Untitled *Type: guide | Category: Events * Events The event feed provides a way for an application to subscribe to any actions performed by any user, users, or service in an… # Events The event feed provides a way for an application to subscribe to any actions performed by any user, users, or service in an enterprise. ## User vs Enterprise events Depending upon the `stream_type`, the [`GET /events`](e://get_events) API enables subscribing to live events across the enterprise or for a particular user, or querying historical events across the enterprise. ### User events User events provides a low latency stream of events relevant to the currently authenticated user. This event stream is how Box keeps [Box Drive](https://www.box.com/drive) up to date, but is also made available for developer use. The emphasis of this feed is to return the complete results quickly, which means that Box may return events more than once or out of chronological order. Duplicate events can be identified by their event IDs. Unlike the enterprise events streams, the user events stream does not support filtering for specific events. The three user events stream_types return slightly different subsets of the user events dataset for different purposes. | Stream Type | | | --- | --- | | all | Returns everything for a user (default) | | changes | Returns events that may cause file tree changes such as file updates or collaborations | | sync | Is similar to changes but only applies to synced folders | ### Enterprise events Enterprise events provide an event feed for all users and content in an enterprise Box instance. Depending upon the `stream_type` your application can either subscribe to live events or query historical events. Access to these stream types is limited to users with admin permissions to **Run new reports and access existing reports**. Unlike the user events stream, the enterprise events stream supports filtering based on event type but does not support long polling. Across the two stream types the dataset is exactly the same. Events can be deduplicated across the two stream types using their event IDs. | Stream Type | | | --- | --- | | admin_logs | Enables querying historical events up to one year | | admin_logs_streaming | Enables subscribing to live events in near real time | #### Live Monitoring To monitor recent events that have been generated within Box across the enterprise, set the `stream_type` to `admin_logs_streaming`. This is also known as the Enterprise Event Stream API. The emphasis for this feed is on low latency rather than chronological accuracy, which means that Box may return events more than once and out of chronological order. Events are returned via the API in near real time after they are processed by Box. A small delay/buffer ensures that new events are not written after your cursor position. Only two weeks of events are available via this `stream_type`. #### Historical Querying To query historical events across the enterprise up to one year old, set the `stream_type` to `admin_logs`. This is also known as the Enterprise Event History API. The emphasis for this feed is on completeness over latency, which means that Box will deliver admin events in chronological order and without duplicates, but with higher latency than the user or `admin_logs_streaming` feed. Consuming events in near real time may lead to missed events as events can arrive later than your filtering window. **Reference:** https://developer.box.com/guides/events/ --- ## Untitled *Type: guide | Category: Embed Box * Box Embed Box Embed is a HTML-based framework that allows embedding the entire Box Web App experience in a custom-made application. Box… # Box Embed Box Embed is a HTML-based framework that allows embedding the entire Box Web App experience in a custom-made application. Box Embed provides the ability to upload, search, comment, share, tag, and edit files using Box Edit. ## Before you start To create a widget, you need to: - Set an embeddable element, such as a **folder**, **file**, **Hub**, **note**, or **app** for sharing. - Have at least **Viewer** [permissions](https://support.box.com/hc/en-us/articles/360044196413-Understanding-Collaborator-Permission-Levels). ## Using web app To fetch the Box Embed widget code from the Box web app, perform the following steps. ### Files and folders 1. Navigate to the chosen file or folder. 2. Click on the ellipsis next to the folder. 3. Go to **More Actions** > **Embed Widget**. ### Hubs 1. Navigate to the chosen Hub. 2. Click on the ellipsis menu in the top right corner. 3. Click **Embed Hub**. ### Notes 1. Navigate to the chosen Box Note. 2. Click on the ellipsis menu. 3. Click **Embed Box Note**. ### Apps 1. Navigate to the chosen Box App or Box App View. 2. Click on the ellipsis menu. 3. Click **Embed**. In the next step, configure the parameters of an embeddable element. | Files | Folders | Hubs | Notes | Apps | | --- | --- | --- | --- | --- | | Size of the widget. | Size of the widget, sorting of the files in a folder, hiding the navigation path and sidebar. | Size of the widget, hiding the parent navigation path and sidebar. | Size of the widget, skipping cloud game (results in note being in read only mode), hiding notes navigation. | Size of the widget. | When you are done customizing the embed widget, copy and paste the embed code into your site or web application. ## Programmatically If you want to customize Box Embed even further, you can do it programmatically. The format for an embed snippet is as follows: ``` <iframe src="https://{custom_domain}.app.box.com/embed/s/{shared link value}?view={list or icon}&sortColumn={name, date, or size}&sortDirection=ASC" width="{pixels}" height="{pixels}" frameborder="0" allowfullscreen webkitallowfullscreen msallowfullscreen ></iframe> ``` ### Finding your shared link value The first step to building an embed `iframe` programmatically is to generate or find the value for the shared link. One way to find this value is by using the Box web app. Another way is to create a shared link with API using the [`PUT /files/:file_id`](e://put-files-id--add-shared-link) or [`PUT /files/:file_id`](e://put-folders-id--add-shared-link). Then you can find this shared link value using the [`GET /files/:id`](e://get-files-id) or [`GET /folders/:id`](e://get-folders-id) endpoint and passing in the query parameter `fields=shared_link`. ``` curl https://api.box.com/2.0/folders/12345?fields=shared_link \ -H "authorization: Bearer ACCESS_TOKEN" ``` ``` "shared_link": { "url": "https://app.box.com/s/dsbJFzdO7qZxdfOHFzdO7qZxdfOH", "download_url": null, "vanity_url": null, ... } ``` You can also set the page to Root Folder/All Files page. Set the URL to `/folder/0` instead of the share link: `<iframe src=“https://app.box.com/embed/folder/0”….></iframe>` ### Parameters Next, you will want to choose your view customization options. The following is a list of optional parameters you can configure. | | | | --- | --- | | hideHubsGallery | Hide or show navigation chevron button to go back to Hubs gallery. Can be true or false (default). | | hideNavigationControls | Hide or show navigation controls in Box Notes. | | showItemFeedActions | Hide or show file comments or tasks. Can be true (default) or false. | | showParentPath | Hide or show the folder path in the header of the frame. Can be true or false (default). | | sortColumn | The order the files or folders are sorted in. Can be name, date (default), or size. | | sortDirection | The sort direction of files or folders. Can be ASC (default) or DESC. | | view | The view type for your files or folders. Can be list (default) or icon. For logged-in users the view type from user preferences takes precedence. | | uxLite | Show the limited content preview (Preview Light), with no cloud game. Works only for shared files and Box Notes. | When you use `uxLite` with Box Notes, navigation controls are not displayed, regardless of the `hideNavigationControls` setting. All custom search parameters from the first-party app URL are passed to the embed widget modal and Content Preview. ### Full screen capabilities To enable full screen capabilities for the Box Embed snippet, include one or more of the following parameters if you want the object to be viewable in full screen within an `<iframe>`: - `allowfullscreen` - `webkitallowfullscreen` - `mozallowfullscreen` - `oallowfullscreen` - `msallowfullscreen` ## Expiring embed links For files, another option is to call the [`GET /files/:id`](e://get-files-id) and request an `expiring_embed_link` using the `fields` query parameter. ``` curl https://api.box.com/2.0/files/12345?fields=expiring_embed_link \ -H "authorization: Bearer ACCESS_TOKEN" ``` ``` { "etag": "1", "expiring_embed_link": { "token": { "access_token": "1!rFppcinUwwwDmB4G60nah7z...", "expires_in": 3646, "restricted_to": [ { "object": { "etag": "1", "file_version": { "id": "34567", "sha1": "1b8cda4e52cb7b58b354d8da0068908ecfa4bd00", "type": "file_version" }, "id": "12345", "name": "Image.png", "sequence_id": "1", "sha1": "1b8cda4e52cb7b58b354d8da0068908ecfa4bd00", "type": "file" }, "scope": "base_preview" }, ... ], "token_type": "bearer" }, "url": "https://cloud.app.box.com/preview/expiring_embed/...." }, "id": "12345", "type": "file" } ``` The `url` attribute can be used in an `<iframe>` to embed an auto expiring Box Embed interface. ``` <iframe src="YOUR-GENERATED-BOX-EMBED-LINK" width="{pixels}" height="{pixels}" frameborder="0" allowfullscreen webkitallowfullscreen msallowfullscreen /> ``` ### Parameters Extra parameters can be added to this URL as well to customize the UI. To do so, add the following parameters to the `url` as query parameters. The eventual URL would look something like this. ``` https://app.box.com/preview/expiring_embed/[HASH]?[parameterName]=true ``` | | | | --- | --- | | showDownload | Shows the download button in the embedded header bar if the viewer has permissions to download the file. Document file types will also show a print button since print and download are governed by the same permissions. Defaults to false. | | showAnnotations | Enables users with permission Preview and above to annotate document and image previews. Also shows annotations that are already on the document. To learn more about the file types that annotations is available on as well as the types of annotations, you can refer to our Annotations page. Annotations are available today on web browsers only. On mobile browsers, users will be able to view annotations but not create new ones. | ## Cloud game The cloud game is a widget created to prevent [clickjacking](https://support.box.com/hc/en-us/articles/360043691034-How-Does-Box-Prevent-Clickjacking). It's shown for embedded sites that aren’t partner integrations. In cloud game, user must drag a cloud to the correct location before an interaction is allowed. It makes clickjacking difficult, as the position of the cloud and its destination are randomly generated. `postMessage()` is used on the iframe to retrieve both the embed and the `showCloudGame` status. If embedded, `document.hasStorageAccess()` shows if Box has access to cookies. If yes and the user is logged in, the cloud game is displayed. If the `showCloudGame` status is `false`, user is navigated to the login page. ## Custom logo Paid Box customers have the option to remove the Box logo in the file Preview. To do so, visit the **Admin Console**, **Enterprise Settings**, Custom Setup and toggle the **Embed widget customization** off to hide the Box logo. ## Limitations Box Embed is not optimized for mobile browsers and should not be used in web experiences designed for mobile devices. Many UI elements, like the **download** and **print** options might not show in mobile browsers. **Reference:** https://developer.box.com/guides/embed/box-embed/ --- ## Untitled *Type: guide | Category: Embed Box * Embed Box Box supports a few different ways to embed files and folders straight into your front-end application. # Embed Box Box supports a few different ways to embed files and folders straight into your front-end application. **Reference:** https://developer.box.com/guides/embed/ --- ## Untitled *Type: guide | Category: Files * Get File Information To get a file's information, not it's content, call the GET /files/:id API with the id of the file. File ID The id for… # Get File Information To get a file's information, not it's content, call the [`GET /files/:id`](e://get-files-id) API with the `id` of the file. ## File ID The `id` for any file can be determined by visiting a file in the web application and copying the `id` from the URL. For example, for the URL `https://*.app.box.com/file/123` the `file_id` is `123`. ## Additional fields To get more of the fields for a file, make sure to pass along the `fields` query parameter. Learn about requesting extra fields **Reference:** https://developer.box.com/guides/files/get/ --- ## Untitled *Type: guide | Category: Files * Files Files, together with Folders, are at the core of the Box API. Files can be uploaded and downloaded, as well as hold important metadata… # Files Files, together with [Folders](g://folders), are at the core of the Box API. Files can be [uploaded](g://uploads) and [downloaded](g://downloads), as well as hold important metadata information about the content. **Reference:** https://developer.box.com/guides/files/ --- ## Untitled *Type: guide | Category: Files * Limitations A few limitations exists when working with files. Name restrictions There are some restrictions to a file's name. Names… # Limitations A few limitations exists when working with files. ## Name restrictions There are some restrictions to a file's name. Names containing non-printable ASCII characters, forward and backward slashes (`/`, `\`), and protected names like `.` and `..` are automatically sanitized by removing the non-allowed characters. **Reference:** https://developer.box.com/guides/files/limitations/ --- ## Untitled *Type: guide | Category: File Requests * Copy a File Request To create a copy of an existing template file request, all you need is its unique ID, and the ID of the folder to apply… # Copy a File Request To create a copy of an existing [template](g://file-requests/template) file request, all you need is its unique ID, and the ID of the folder to apply the new file request to. The ID of a folder and file request can be determined by visiting the Box web app and inspecting the URL. The folder ID is the number at the end of the URL when visiting a folder, for example `app.box.com/folder/123` is the URL for the folder with ID `123`. For a file request, please [check our guide](g://file-requests/template) on setting up a file request template to learn how to determine a file request ID. ## Updating a file request on copy It is possible to make some basic changes to a file request when copying from a template. The file request's title, description, status and a few more other settings can be updated when the file request is copied from the template. ``` curl -i -X POST "https://api.box.com/2.0/file_requests/2342235/copy" \ -H "Authorization: Bearer <ACCESS_TOKEN>" \ -d '{ "title": "Please upload required documents", "description": "Please upload required documents", "status": "active", "is_email_required": true, "is_description_required": false, "folder": { "id": "342323423" } }' ``` For more details on the different fields that can be updated when creating a template, please see the reference documentation for the [`POST /file-requests/:id/copy`](e://post_file_requests_id_copy) API. **Reference:** https://developer.box.com/guides/file-requests/copy/ --- ## Untitled *Type: guide | Category: File Requests * Delete a File Request To delete a file request, all you need is its unique ID. The ID of a file request can be determined by visiting the… # Delete a File Request To delete a file request, all you need is its unique ID. The ID of a file request can be determined by visiting the Box web app and inspecting the URL. Please [check our guide](g://file-requests/template) on setting up a file request template to learn how to determine a file request ID. **Reference:** https://developer.box.com/guides/file-requests/delete/ --- ## Untitled *Type: guide | Category: File Requests * Get information for a File Request To get details about an existing file request, all you need is its unique ID. The ID of a file request… # Get information for a File Request To get details about an existing [file request](g://file-requests/template), all you need is its unique ID. The ID of a file request can be determined by visiting the Box web app and inspecting the URL. Please [check our guide](g://file-requests/template) on setting up a file request template to learn how to determine a file request ID. **Reference:** https://developer.box.com/guides/file-requests/get/ --- ## Untitled *Type: guide | Category: File Requests * File Requests Box File Request gives you a fast and secure way to request files and associated metadata from anyone. Using a drag-and-drop… # File Requests Box File Request gives you a fast and secure way to request files and associated metadata from anyone. Using a drag-and-drop graphic interface, you can create a web form that enables you to: - Securely request files from anyone, no matter whether they have a Box account, without adding collaborators on a folder. - Solicit additional information with metadata form fields, which you can set as required/optional. - Enable extra security and tracking with link settings. - Kick off automated workflows using Box Relay. The **File Request API** gives you the power to create new file requests based on an existing file request, update file request settings, activate and deactivate, and delete file requests programmatically. ## Use Cases The File Request API is especially useful in situations where folders are automatically created when a new case, project, or customer is created in an external system. For each of these folders a file request can be automatically generated, allowing customers and other users to upload documents to the project. For example. - **Case management** - In this situation each customer support case has its own associated folder and a new file request can be sent out to a customer, requesting them to submit case related documents to the folder. - **Project management** - In this situation each project has its own associated folder for each project defined in an external system. The API can be used to automatically create a **"Client Documents"** folder within this folder, and a link can be sent to the client to allow them to upload documents on demand. - **Employee "drop" folder** - In this situation every new loan advisor in a bank has a personal folder in Box. To help them work with their clients a "drop" subfolder is automatically generated for them with an associated file request, allowing their clients to automatically upload documents to their advisor's personal folder. ## Limitations Currently, the API only allows the creation of new file requests by copying an existing file request associated to another folder. Please check our guide to create a template folder with a template file request. **Reference:** https://developer.box.com/guides/file-requests/ --- ## Untitled *Type: guide | Category: File Requests * Create a template File Request Currently, the API only allows the creation of new file requests by copying an existing file request… # Create a template File Request Currently, the API only allows the creation of new file requests by copying an existing file request associated to another folder. Please check our below guide on creating a template folder with a template file request. ## 1. Template folder First off, create a template folder that you can associate the template file request to. This can be any folder really, but we recommend using a folder that is only used for this purpose. A folder can be created through the Box web or mobile interface, or through the [`POST /folders`](e://post_folders) API. When using server-side JWT authentication, we recommend creating folder that is owned by the application's [Service Account](page://platform/user-types/#service-account). This way, the folder can't be accidentally deleted by a regular Box user. ## 2. Template file request To create a file request, navigate to the template folder in the web app an d click the 3 dots at the top of the page. Then, select **File Request** from the menu. In the next configuration panel, click the **Edit** button to visit the file request configuration page. On the configuration page, you can set configure the title and description, before saving the page. This page will also have the ID of the file request in the URL. Make a note of the URL as the number at the end is the file request's ID, which you will need when working with the API. ``` https://*.app.box.com/filerequest/2338423584 ``` If your template folder belongs to a JWT user, you will need to [invite](e://post-collaborations) a managed Box user to the folder before it can be seen in the web app. Invite yourself to the folder to allow yourself to add a file request to the folder via the app. ## 3. Using a template A template file request can be used to create new copies using the [`POST /file-requests/:id/copy`](e://post_file_requests_id_copy) API. With the ID of a new folder in hand, you can copy the existing request, including its title, description and form-configuration to a new folder [with one API call](g://file-requests/copy). **Reference:** https://developer.box.com/guides/file-requests/template/ --- ## Untitled *Type: guide | Category: Folders * Folders Folders, together with Files, are at the core of the Box API. Folders can be uploaded and downloaded, as well as hold important… # Folders Folders, together with [Files](g://files), are at the core of the Box API. Folders can be uploaded and downloaded, as well as hold important metadata information about the content. **Reference:** https://developer.box.com/guides/folders/ --- ## Untitled *Type: guide | Category: Start Here * Create your first application Once you have your Developer Enterprise set up, you can start creating applications. Click the Dev Console… # Create your first application Once you have your Developer Enterprise set up, you can start creating applications. Click the [Dev Console](https://cloud.app.box.com/developers/console) button once you're logged in to your Developer Enterprise. If this is your first application, you will be prompted to get started. ## Choose application type The first step is to select the application type you want to create. In this guide we use the **Platform App** option, as most of our partners use this type for their integrations. ## Choose the authentication method After you choose the **Platform App**, you need to select an authentication method that your app will use. In this guide we use the [**User Authentication (OAuth 2.0)**](g://authentication/oauth2) option, as most of our partners use this type for their integrations. ## Name the app Choose an app name. It can be changed later on and it is not publicly visible until you publish your app. We recommend choosing a name that is recognizable, as the admins and customers will see the name of the app when it's published. Click the **Create App** button to finish creating your app. Next, configure the application settings. ## General settings Begin with the most basic settings. Open the **General Settings** tab and check or fill in below fields: - **App Name** - the name you set up during the app creation, you can change it here if needed; - **Contact Email** - this is set to the developer of the application by default. Keep in mind that once you publish your app, this email is publicly visible to Box users who view your app in the [Integrations](g://applications/integrations). We recommend to change it to a support email address, so that users can reach out to support in case of any issues with the integration; - **Collaborators** - add other developers that can work on this integration, so that they can access the developer interface in case they need to adjust any settings. The developers you add here need to be Box users already. If they don't have Box account, you can [create them](https://support.box.com/hc/en-us/articles/360043694594-Add-Users) in your Developer Enterprise. ## Advanced settings Go to the **Configuration** tab. Here you can specify the app details, generate a developer token, check your OAuth 2.0 credentials, add and edit OAuth 2.0 redirect URI, choose the application scopes, set app advanced features, and add CORS domains. In the next tabs you can create webhooks, web app integrations, submit app for enablement for access to the Enterprise, submit your app to the [Box Integrations](g://applications/integrations), and generate a report to view this application’s activity. ## Test your app You can now make some API calls to test your app and get to know the response format in Box. Use the [developer token](g://authentication/tokens/developer-tokens/#create-developer-token) authenticated to your current developer account. This token is valid for an hour from being generated. Start with the [Postman collection](g://tooling/postman) to check specific calls and see what responses they return. To see example API calls, browse our [API reference documentation](page://reference). You can also use [Box CLI tool](https://github.com/box/boxcli) if you prefer to use the terminal. **Reference:** https://developer.box.com/guides/getting-started/first-application/ --- ## Untitled *Type: guide | Category: File Requests * Update a File Request To update some of the basic details for an existing file request, all you need is its unique ID. For more details on… # Update a File Request To update some of the basic details for an existing file request, all you need is its unique ID. For more details on the different fields that can be updated when creating a template, please see the reference documentation for the [`POST /file-requests/:id/update`](e://put_file_requests_id) API. The ID of a file request can be determined by visiting the Box web app and inspecting the URL. Please [check our guide](g://file-requests/template) on setting up a file request template to learn how to determine a file request ID. **Reference:** https://developer.box.com/guides/file-requests/update/ --- ## Untitled *Type: guide | Category: Start Here * Start Here Our documentation is divided into five main sections: Learn Box Platform: A getting started walk through of all things Platform… # Start Here Our documentation is divided into five main sections: - **Learn Box Platform**: A getting started walk through of all things Platform. - **Developer Guides**: In-depth documentation on developer portal, API working, usage of Box functionalities. - **API Reference**: A list of API endpoints and resources, with request and response examples. - **SDK and Tools**: A list of official Box SDKs and tools. - **Sample Code Catalog**: Consolidated list of several code repositories. If you are new to development with the Box API, first familiarize yourself with the content in the [Learn Box Platform](page://platform) section. In this section, you will: - learn the [Box Platform concepts](page://platform/box-platform-101), - evaluate if your [use case](page://platform/use-cases) is a good fit for Box, - understand the different [types of Box users](page://platform/user-types), - select the applicable [application type](page://platform/application-types), - check the available [authentication methods](page://platform/authentication-methods) and choose the best one for your use case, - find out where you can get [support](page://platform/support), - check the [tools](page://platform/tools) needed for developing applications in Box. After learning the basics, we recommend you to follow the steps below. 1. Create your first [Box application](g://getting-started/first-application). 2. Configure your application. 3. Explore [endpoints](page://reference) and make API calls using [common values](page://platform/appendix/locating-values). 4. [Publish](g://getting-started/publish-app) your application. If you have any questions, checkout our [forum](https://forum.box.com). **Reference:** https://developer.box.com/guides/getting-started/ --- ## Untitled *Type: guide | Category: Start Here * Publish your app When you finish the platform app configuration and testing, you can publish it in the Box Integrations. It allows customers… # Publish your app When you finish the platform app configuration and testing, you can publish it in the [Box Integrations](https://cloud.app.box.com/integrations). It allows customers to find your app and add it to their Box accounts. Only apps that use the OAuth 2.0 authentication have a built-in publication option. In case you use a different auth type for your integration, you can still create and publish an OAuth 2.0 platform app that will serve as a marketing listing in the **Integrations** and redirect customers to your website. To publish your platform app: Open the **Dev Console**. Select the platform app you want to publish. Go to the **Publishing** tab. Read through the submission checklist and check the confirmation checkbox if your app meets all the requirements. The next step is the marketing section of the app, where you need to add the following app information: - **General Info** - choose correct categories and platforms for your app to make it easier to find in the Integrations, **Description** - make sure it contains all information about your app that the customers may find useful, - **Short Description** appears next to your app logo, together with your app name; - **Long Description** is what the users see after they select your app to view its details. You can add clickable links to the long description. 1. **Screenshots** and **Icon** - provide the screenshots to allow the users see how the app looks like and how it integrates with Box. The icon is needed to represent your app in the list of Integrations. 2. **Support Resources** - A list of links and supplementary information that will help the users to work with your app. Before submitting your app, preview it to see if you included all the necessary information. Submit your app for approval. Box will review your app and publish it under **Integrations**. If you have any questions or issues, contact the **Partners** team: [`integrate@box.com`](mailto:integrate@box.com). **Reference:** https://developer.box.com/guides/getting-started/publish-app/ --- ## Untitled *Type: guide | Category: Start Here * Sandboxes in Box Platform Developer sandboxes in Box provide a secure, controlled environment where developers can build, test, and… # Sandboxes in Box Platform Developer sandboxes in Box provide a secure, controlled environment where developers can build, test, and collaborate on applications without affecting the actual data of your enterprise. They offer a safe space to experiment with Box APIs, test settings, try new integrations, and work alongside external partners. ## What is a developer sandbox A developer sandbox is an environment isolated from your production (live) enterprise setup that keeps development activities separate from real business data. ## Why use a sandbox Using a sandbox allows you to: - Safely build apps within an enterprise-linked environment instead of a free standalone account. - Collaborate securely with both internal teams and external collaborators. Contractors, partners, and other external users can join the sandbox without accessing live systems and compromising security. - Test apps under realistic testing conditions, as sandboxes inherit your enterprise's plan and add-ons at creation time. If there are any changes to the plan or add-ons, they can be [manually synced](https://support.box.com/hc/en-us/articles/360043697274-Managing-developer-sandboxes-for-Box-admins#:~:text=in%20a%20sandbox.-,Synchronizing,-sandbox%20with%20production) by an enterprise admin. ## Accessing your sandbox Sandboxes in Box are created by enterprise admins. See [this document](https://support.box.com/hc/en-us/articles/360043697274-Managing-developer-sandboxes-for-Box-admins) to learn how to create a sandbox. When you are assigned as a primary admin of a sandbox, you receive a log-in email from Box with your sandbox user ID. To access the [Dev Console](https://cloud.app.box.com/developers/console) inside the sandbox environment and set up a password, click the link in the email. You can also access your sandbox by logging in with your sandbox credentials on [developer.box.com](https://developer.box.com). The sandbox's primary admin can create individual sandbox accounts, giving developers access to a new Box environment that matches the plan of their parent Box enterprise. ### Accessing multiple sandboxes As a primary admin, you can log into multiple sandboxes. If you want to use this functionality for existing sandboxes, you can remove the unique email address and get a system generated email. **Reference:** https://developer.box.com/guides/getting-started/sandbox/ --- ## Untitled *Type: guide | Category: Integration Mappings * Integration Mappings With Integration Mappings, you can manage where content from partner apps is stored in Box. # Integration Mappings With [Integration Mappings](r://integration-mappings), you can manage where content from partner apps is stored in Box. **Reference:** https://developer.box.com/guides/integration-mappings/ --- ## Untitled *Type: guide | Category: Legal Holds * Get Legal Hold Policy To get the information for a specific Legal Hold policy that has been created in an enterprise, call the GET /legal… # Get Legal Hold Policy To get the information for a specific Legal Hold policy that has been created in an enterprise, call the [`GET /legal_hold_policies/:id`](e://get_legal_hold_policies_id) API endpoint with the `id` of the policy. ## Required Scopes Before using any of the Legal Hold APIs, an application must have the right scopes enabled. See [Required Scopes](g://legal-holds#required-scopes) for more details. **Reference:** https://developer.box.com/guides/legal-holds/get/ --- ## Untitled *Type: guide | Category: Legal Holds * Legal Holds A legal hold is a process that an enterprise can use to preserve all forms of potentially relevant information when litigation… # Legal Holds A legal hold is a process that an enterprise can use to preserve all forms of potentially relevant information when litigation is pending or reasonably anticipated. Applying a hold to items prevents any user from deleting them from Box. Legal Holds can be managed and assigned to folders and files through the Box APIs. Legal Holds are a feature of the [Box Governance](https://www.box.com/security/governance-and-compliance) package, which can be added on to any Business Plus, Enterprise Advanced or Enterprise account. ## Policies, Assignments, and Holds Working with Legal Hold Policies requires a developer to work with three distinct resources. - **Policies:** A [Legal Hold Policy](r://legal_hold_policy) describes the general behavior of the hold. It determines which files should be affected, based on the date and time the files were created or updated. - **Assignments:** A [Legal Hold Policy Assignment](r://legal-hold-policy-assignment) is a relation between a policy and custodian. In this case, as custodian can be a user, folder, file, or file version. Creating an assignment puts a hold on all the file versions that belong to the custodian. For example, if an assignment is created on a folder the policy is applied to all file versions within that folder. - **Holds**: A [File Version Legal Hold](r://file_version_legal_hold) represents all the policies that are assigned to a specific file version. Note that every file version can have a maximum of one file version legal hold and this hold contains a list of every assigned policy. ## Example Use Case If an order of discovery is received or the customer is part of an ongoing litigation, a legal hold policy can be created to keep track of everything that needs to be held. The actual holding is done by assigning a policy to a specific files or folder. When the holds are no longer needed, the policy can be released by deleting the assignment. ## Required Scopes Before using any of the Legal Hold APIs, an application must have the GCM and Manage Legal Hold scopes enabled. These are not available in the Developer Console and need to instead be enabled by contacting customer support. **Reference:** https://developer.box.com/guides/legal-holds/ --- ## Untitled *Type: guide | Category: Legal Holds * List All Legal Hold Policies To list all Legal Hold Policies that have been created in an enterprise, call the GET /legal_hold_policies API… # List All Legal Hold Policies To list all Legal Hold Policies that have been created in an enterprise, call the [`GET /legal_hold_policies`](e://get_legal_hold_policies) API endpoint. ## Required Scopes Before using any of the Legal Hold APIs, an application must have the right scopes enabled. See [Required Scopes](g://legal-holds#required-scopes) for more details. **Reference:** https://developer.box.com/guides/legal-holds/list/ --- ## Untitled *Type: guide | Category: Metadata * Classifications Box enables users and applications to apply a security classification label to files, and cascade a classification label to… # Classifications Box enables users and applications to apply a security classification label to files, and cascade a classification label to folders and their contents. Classifications help protect shared sensitive content from negligent access through Box's Governance and Shield products. The Classification API can be used to create new classification labels and to assign classifications to files and folders. Classifications use the [Metadata APIs](g://metadata) to create classification labels, and to assign classifications to files and folders. For more details on metadata templates and instances, please explore our guides on [Metadata](g://metadata). ## Classifications & Metadata Working with classifications requires a developer to work with metadata templates and instances. - **The Classification Template:** to work with classifications, an enterprise needs to have a classification metadata template with at least one classification. This template needs to have a `scope`/`templateKey` of `enterprise.securityClassification-6VMVochwUWo`. This template will hold the possible classification levels, their label names, descriptions, and `colorID` values. - **Template Instances**: to apply a classification to a file or folder, a developer applies an instance of the `enterprise.securityClassification-6VMVochwUWo` template to the item. When the template is applied, one of the classifications is selected from the list of classifications on the template. **Reference:** https://developer.box.com/guides/metadata/classifications/ --- ## Untitled *Type: guide | Category: Metadata * Metadata Metadata allows users and applications to define and store custom data associated with files and folders. Metadata consists of key… # Metadata Metadata allows users and applications to define and store custom data associated with files and folders. Metadata consists of key/value pairs that are assigned to a file or a folder. For example, an important contract may have the key/value pairs of `clientNumber: 820183` and `clientName: bioMedicalCorp`. ## Metadata terminology Working with metadata requires a developer to work with a few distinct types of resources. - **Templates:** A [metadata template](g://metadata/templates) describes a re-usable set of key/value pairs that can be assigned to a file. For example, an `invoiceData` template might hold data about an invoice, having a field for the invoice ID as well as the customer ID. - **Fields:** A [metadata template field](g://metadata/fields) describes a specific piece of data within a metadata template. For example, the ID of an invoice might be represented as an `id` field on an `invoiceData` template. - **Instances:** A [metadata instance](g://metadata/instances) describes the relation between a template and a file or folder, including the values that are assigned for every field. For example, a user might have assigned an `invoiceData` metadata template to a file and provided 2 values, one for the invoice ID and one for the customer ID. You can assign up to 100 templates to specific file or folder. - **Cascade Policies**: A [metadata cascade policy](g://metadata/cascades) describes how metadata instances applied to a folder should be applied to any item within that folder. For example, a user might assign the same `invoiceData` metadata template to a project folder (including the 2 values), allowing them to automatically apply to all the files and folders within that project folder. - **Queries:** A [metadata query](g://metadata/queries) provides a way to find files and folders by searching for the metadata attached to them. For example, to find all the files for an invoice with a certain ID, the query would look for all files and folders with the `invoiceData` template attached to it and a value of `id = :id`, where `:id` would be the value of the invoice. ## The purpose of metadata Metadata can be used for many purposes. Enterprises may want to have a better way to organize their digital assets for their marketing teams, or developers may want to provide advanced content functionality such as facilitating workflows or approvals. For example, a `marketingCollateral` template may define where and when specific marketing content should be used. Users can see a representation of the template in the Box web application while navigating to a file preview. To learn more, please visit [Box community articles](https://support.box.com/hc/en-us/articles/360044196173-Using-Metadata). **Reference:** https://developer.box.com/guides/metadata/ --- ## Untitled *Type: guide | Category: Metadata * Metadata template scopes Metadata templates are grouped into two distinct groups, or scopes. global: a group of templates that is available… # Metadata template scopes Metadata templates are grouped into two distinct groups, or **scopes**. - **`global`**: a group of templates that is available to everyone using Box, regardless of the enterprise they are in. An example is the `global.properties` template that serves as a place to put free-form key/value `string` pairs without any additional schema associated with it. - **`enterprise`** or **`enterprise_*`**: a group of templates defined by a user within an enterprise. These templates are either created by admin's in the web application, or by applications using the API. When accessing or creating templates within the authenticated user's enterprise a short-hand of `enterprise` can be used. When accessing templates that belong to another enterprise - for example when accessing metadata on files belonging to other enterprises - the scope `enterprise_*` is used where `*` is the ID of the enterprise the template belongs to. # Permissions It is important to note that no metadata templates can be created within the `global` scope, and that metadata templates made within the user's enterprise can only be accessed by users with access to that enterprise. **Reference:** https://developer.box.com/guides/metadata/scopes/ --- ## Untitled *Type: guide | Category: Mobile * Mobile The Box mobile SDKs provide native access to the Box API from within your iOS and Android projects. # Mobile The Box mobile SDKs provide native access to the Box API from within your iOS and Android projects. **Reference:** https://developer.box.com/guides/mobile/ --- ## Untitled *Type: guide | Category: Mobile * Deep Linking Box’s mobile applications support deep linking into folder and file objects. From a web page or native app it is possible to… # Deep Linking Box’s mobile applications support deep linking into folder and file objects. From a web page or native app it is possible to deep link to open an object directly in Box. The following URLs are supported in Box’s mobile applications: | Application | Object Type | Deep Link URL | iOS & Android | | --- | --- | --- | --- | | Box | Folder | boxapp://folder?id=[folderid] | Version 3.7+ | | | File | boxapp://file?id=[fileid] | | | | Shared Link | boxapp://sharedlink?url=[sharedlink] | | | | | | | | Box for EMM | Folder | boxemm://folder?id=[folderid] | Version 3.7+ | | | File | boxemm://file?id=[fileid] | | | | Shared Link | boxemm://sharedlink?url=[sharedlink] | | **Reference:** https://developer.box.com/guides/mobile/mobile-deep-linking/ --- ## Untitled *Type: guide | Category: Representations * Download File Representation Use theurl_template that was received when selecting the representation to download the representation. Replace… # Download File Representation Use the`url_template` that was received when [selecting the representation](guide://representations/request-a-representation) to download the representation. Replace the `{+asset_path}` depending on the type of representation. ## Paginated representations For paged representations like PDFs replace `{+asset_path}` with the desired page number and the file extension, for example `1.pdf`. ``` curl https://dl.boxcloud.com/api/2.0/internal_files/123/versions/345/representations/pdf/content/3.pdf \ -H "authorization: Bearer ACCESS_TOKEN" ``` ## Non-paginated representations For non-paged representations, replace the `{+asset_path}` with an empty string. ``` curl https://dl.boxcloud.com/api/2.0/internal_files/123/versions/345/representations/jpg_32x32/content/ \ -H "authorization: Bearer ACCESS_TOKEN" ``` ## Optional query parameters When fetching the representation the following optional headers are supported. | Parameter | Options | Default | | --- | --- | --- | | set_content_disposition_type | inline / attachment | null | Sets the `content-disposition` header in the API response with the specified value. A disposition type of `attachment` causes most web browsers to prompt the user to save the response to their device, where the type `inline` will open the file in the browser. If not supplied, the `content-disposition` header is not included in the response. | Parameter | Options | Default | | --- | --- | --- | | set_content_disposition_filename | Filename without extension | null | Allows the application to define the downloaded representation's file name. If not defined, the file name is derived from the source file name in Box, replacing the extension with the representation's file type. **Reference:** https://developer.box.com/guides/representations/download-a-representation/ --- ## Untitled *Type: guide | Category: Representations * Representations A representation is an alternative asset for a file stored in Box. These assets can be PDFs, thumbnails, or text extractions… # Representations A representation is an alternative asset for a file stored in Box. These assets can be PDFs, thumbnails, or text extractions. Representations are automatically generated for the supported file types, either when uploading to Box or when requesting the asset. These representations are exposed through the `GET /files/:id` endpoint by using the `fields=representations` query parameter and the `x-rep-hints` header. **Reference:** https://developer.box.com/guides/representations/ --- ## Untitled *Type: guide | Category: Representations * List All Representations for File To see what representations are available for a file you can call the GET /files/:id endpoint while… # List All Representations for File To see what representations are available for a file you can call the [`GET /files/:id`](endpoint://get-files-id) endpoint while requesting the [`representations`](resource://file#param-representations) field. ``` curl https://api.box.com/2.0/files/123?fields=representations \ -H "authorization: Bearer ACCESS_TOKEN" ``` The response will include a list of representations in the following format ``` ... { "info": { "url": "https://api.box.com/2.0/internal_files/123/versions/345/representations/jpg_thumb_32x32" }, "properties": { "dimensions": "32x32", "paged": "false", "thumb": "true" }, "representation": "jpg" } ... ``` ## Response fields Every representation will include a set of properties and the type of representation. The optional `dimensions` field represents the file's dimensions in pixels as width by height. The optional `paged` field specifies if this representation is a paged document. Some images and PDFs will often be paged documents. The optional `thumb` field specifies if this representation is suitable as a file thumbnail. **Reference:** https://developer.box.com/guides/representations/list-all-representations/ --- ## Untitled *Type: guide | Category: Representations * Get PDF Representation A PDF representation provides a predictable way to embed documents in mobile and web applications. PDF… # Get PDF Representation A PDF representation provides a predictable way to embed documents in mobile and web applications. PDF representations support watermarks if the original file has been watermarked. PDF representations are generated upon uploading the source file to Box, though a watermarked PDF is generated upon fetching the watermarked file for the first time. ## The process To get a PDF representation follow the following steps - [List all representations](guide://representations/list-all-representations) - [Request a PDF](guide://representations/request-a-representation) by passing the `x-rep-hints`-header for the desired file type `[pdf]`. - [Download the PDF](guide://representations/download-a-representation) by calling the `url_template`, replacing the `{+asset_path}` with an the page of the PDF to request, for example `1.pdf`. ## Watermarked PDFs To retrieve a watermarked PDF the underlying file itself needs to be watermarked in Box. You can watermark a file either via the Box web application or using the [`PUT /files/:id/watermark/`](endpoint://put-files-id-watermark) API. Once watermarked, a watermarked PDF representation of the file is generated. **Reference:** https://developer.box.com/guides/representations/pdf/ --- ## Untitled *Type: guide | Category: Representations * Request Desired Representation To select a specific representation call the GET /files/:id endpoint with a x-rep-hints-header defining the… # Request Desired Representation To select a specific representation call the [`GET /files/:id`](endpoint://get-files-id) endpoint with a [`x-rep-hints`](endpoint://get-files-id#param-x-rep-hints)-header defining the required representation format. ``` curl https://api.box.com/2.0/files/123?fields=representations \ -H "x-rep-hints: [pdf]" \ -H "authorization: Bearer ACCESS_TOKEN" ``` ## Multiple dimensions Some formats will require the `dimensions` to be passed in to select a specific size. This can be achieved by appending the `dimensions` to the header. ``` curl https://api.box.com/2.0/files/123?fields=representations \ -H "x-rep-hints: [jpg?dimensions=94x94]" \ -H "authorization: Bearer ACCESS_TOKEN" ``` ## Multiple representations Multiple representations can be fetched by chaining the different types in the `x-rep-hints`-header. ``` curl https://api.box.com/2.0/files/123?fields=representations \ -H "x-rep-hints: [pdf][jpg?dimensions=94x94]" \ -H "authorization: Bearer ACCESS_TOKEN" ``` ## API Response This API call will result in one or more representations with a `url_template` value that includes a `{+asset_path}` value. ``` { "etag": "1", "id": "123", "representations": { "entries": [ { "content": { "url_template": "https://dl.boxcloud.com/api/2.0/internal_files/123/versions/345/representations/pdf/content/{+asset_path}" }, "info": { "url": "https://api.box.com/2.0/internal_files/123/versions/345/representations/pdf" }, "properties": {}, "representation": "pdf", "status": { "state": "success" } } ] }, "type": "file" } ``` The `url_template` in this response is an **opaque** URL. This URL format might change over time and no assumptions should be made about its format except for the presence of the `{+asset_path}` variable. **Reference:** https://developer.box.com/guides/representations/request-a-representation/ --- ## Untitled *Type: guide | Category: Representations * Supported File Types The following file types are supported by the representation API. 3D Graphics and Modeling Files File Type PDF Support… # Supported File Types The following file types are supported by the representation API. ## 3D Graphics and Modeling Files | File Type | PDF Support? | Thumbnails? | Text? | | --- | --- | --- | --- | | .3ds | No | No | No | | .box3d | No | No | No | | .dae | No | No | No | | .fbx | No | No | No | | .mtl | No | No | No | | .obj | No | No | No | | .ply | No | No | No | | .stl | No | No | No | ## Audio | File Type | PDF Support? | Thumbnails? | Text? | | --- | --- | --- | --- | | .aac | No | No | No | | .aif | No | No | No | | .aifc | No | No | No | | .aiff | No | No | No | | .amr | No | No | No | | .au | No | No | No | | .flac | No | No | No | | .m4a | No | No | No | | .mp3 | No | No | No | | .ogg | No | Yes | No | | .ra | No | No | No | | .wav | No | No | No | | .wma | No | No | No | ## CAD | File Type | PDF Support? | Thumbnails? | Text? | | --- | --- | --- | --- | | .dwg | Yes | Yes | No | ## Documents | File Type | PDF Support? | Thumbnails? | Text? | | --- | --- | --- | --- | | .boxnote | No | No | Yes | | .doc | Yes | Yes | Yes | | .docx | Yes | Yes | Yes | | .gdoc | Yes | Yes | Yes | | .html | Yes | No | Yes | | .msg | Yes | No | Yes | | .odt | Yes | Yes | Yes | | .pages | Yes | Yes | No | | .pdf | Yes | Yes | Yes | | .rtf | Yes | Yes | Yes | | .wpd | Yes | Yes | Yes | | .xhtml | Yes | No | Yes | | .xml | Yes | No | Yes | | .xsd | Yes | No | Yes | | .xsl | Yes | No | Yes | | .xbd | Yes | Yes | Yes | | .xdw | Yes | Yes | Yes | ## Drawing | File Type | PDF Support? | Thumbnails? | Text? | | --- | --- | --- | --- | | .ai | No | Yes | No | | .boxcanvas | No | No | Yes | | .indd | Yes | Yes | No | | .psd | No | Yes | No | | .svg | No | Yes | No | ## Image | File Type | PDF Support? | Thumbnails? | Text? | | --- | --- | --- | --- | | .bmp | No | Yes | No | | .cr2 | No | Yes | No | | .crw | No | Yes | No | | .dcm | No | Yes | No | | .dicm | No | Yes | No | | .dicom | No | Yes | No | | .dng | No | Yes | No | | .eps | No | Yes | No | | .gif | No | Yes | No | | .heic | No | Yes | No | | .indd | Yes | Yes | No | | .idml | Yes | Yes | No | | .indt | Yes | Yes | No | | .inx | Yes | Yes | No | | .jpeg | No | Yes | No | | .jpg | No | Yes | No | | .nef | No | Yes | No | | .png | No | Yes | No | | .ps | No | Yes | No | | .psd | No | Yes | No | | .raf | No | Yes | No | | .raw | No | Yes | No | | .svg | No | Yes | No | | .svs | No | Yes | No | | .tif | No | Yes | No | | .tiff | No | Yes | No | | .tga | No | Yes | No | | .webp | No | Yes | No | ## Presentation | File Type | PDF Support? | Thumbnails? | Text? | | --- | --- | --- | --- | | .gslide | Yes | Yes | Yes | | .gslides | Yes | Yes | Yes | | .key | Yes | Yes | No | | .odp | Yes | Yes | Yes | | .otp | Yes | Yes | Yes | | .ppt | Yes | Yes | Yes | | .pptx | Yes | Yes | Yes | ## Spreadsheet | File Type | PDF Support? | Thumbnails? | Text? | | --- | --- | --- | --- | | .csv | Yes | Yes | Yes | | .gsheet | Yes | Yes | Yes | | .numbers | Yes | Yes | No | | .ods | Yes | Yes | Yes | | .tsv | Yes | Yes | Yes | | .xls | Yes | Yes | Yes | | .xlsm | Yes | Yes | Yes | | .xlsx | Yes | Yes | Yes | | .xlsb | Yes | Yes | Yes | ## Text-Based Files | File Type | PDF Support? | Thumbnails? | Text? | | --- | --- | --- | --- | | .as | Yes | No | Yes | | .as3 | Yes | No | Yes | | .asm | Yes | No | Yes | | .bat | Yes | No | Yes | | .c | Yes | No | Yes | | .cc | Yes | No | Yes | | .cmake | Yes | No | Yes | | .cpp | Yes | No | Yes | | .cs | Yes | No | Yes | | .css | Yes | No | Yes | | .cxx | Yes | No | Yes | | .diff | Yes | No | Yes | | .erb | Yes | No | Yes | | .fdx | Yes | No | Yes | | .groovy | Yes | No | Yes | | .h | Yes | No | Yes | | .haml | Yes | No | Yes | | .hh | Yes | No | Yes | | .java | Yes | No | Yes | | .js | Yes | No | Yes | | .json | Yes | No | Yes | | .less | Yes | No | Yes | | .log | Yes | No | Yes | | .m | Yes | No | Yes | | .make | Yes | No | Yes | | .md | Yes | No | Yes | | .ml | Yes | No | Yes | | .mm | Yes | No | Yes | | .php | Yes | No | Yes | | .pl | Yes | No | Yes | | .plist | Yes | No | Yes | | .properties | Yes | No | Yes | | .py | Yes | No | Yes | | .rb | Yes | No | Yes | | .rst | Yes | No | Yes | | .sass | Yes | No | Yes | | .scala | Yes | No | Yes | | .scm | Yes | No | Yes | | .script | Yes | No | Yes | | .sh | Yes | No | Yes | | .sml | Yes | No | Yes | | .sql | Yes | No | Yes | | .txt | Yes | No | Yes | | .vi | Yes | No | Yes | | .vim | Yes | No | Yes | | .vtt | Yes | No | Yes | | .webdoc | Yes | No | Yes | | .yaml | Yes | No | Yes | ## Video | File Type | PDF Support? | Thumbnails? | Text? | | --- | --- | --- | --- | | .3g2 | No | Yes | No | | .3gp | No | Yes | No | | .avi | No | Yes | No | | .flv | No | Yes | No | | .m2v | No | Yes | No | | .m2ts | No | Yes | No | | .m4v | No | Yes | No | | .mkv | No | Yes | No | | .mov | No | Yes | No | | .mp4 | No | Yes | No | | .mpeg | No | Yes | No | | .mpg | No | Yes | No | | .mts | No | Yes | No | | .ogg | No | Yes | No | | .qt | No | Yes | No | | .ts | No | Yes | No | | .swf | No | Yes | No | | .wmv | No | Yes | No | **Reference:** https://developer.box.com/guides/representations/supported-file-types/ --- ## Untitled *Type: guide | Category: Representations * Get Text Representation A text representation provides a way to extract plain text from a document. Text is generated for all document file… # Get Text Representation A text representation provides a way to extract plain text from a document. Text is generated for all document file types including plain text and code files supported by Box. This does not include image files as these do not have a text layer. Text representations are generated upon upload of the file, similarly to PDFs and thumbnails. They are not generated for files larger than 500 megabytes. ## The process To get a text representation follow the following steps - [List all representations](guide://representations/list-all-representations) - [Request a text representation](guide://representations/request-a-representation) by passing the `x-rep-hints`-header with the value `[extracted_text]`. - [Download the text](guide://representations/download-a-representation) by calling the `url_template`, replacing the `{+asset_path}` with an empty string. **Reference:** https://developer.box.com/guides/representations/text/ --- ## Untitled *Type: guide | Category: Representations * Get Basic Thumbnail A thumbnail is a small image, either as .png or as .jpg that can be used in an application as a representation of the… # Get Basic Thumbnail A thumbnail is a small image, either as `.png` or as `.jpg` that can be used in an application as a representation of the file, for example as a placeholder for a link that downloads or previews the file. The preferred way to get a thumbnail for a file is using the [representations API](guide://representations/thumbnail-representation). ## Requesting To request a file thumbnail use the [`GET /files/:id/thumbnail.:extension`](endpoint://get_files_id_thumbnail_id) endpoint. When a thumbnail was successfully created, this will return the thumbnail in the body of the response as binary data. ## Asynchronous thumbnail creation Sometimes the thumbnail can not be created directly. Instead, the API will return a `HTTP 202` with a `location` response header. The location is for a temporary image that can be used while the thumbnail is being generated. A `retry-after` response header is also provided to present you with an estimated amount of seconds before retrying this endpoint. ## Supported file sizes The following formats and sizes of thumbnails are available. | File Type | Dimensions | | --- | --- | | JPG | 32x32, 94x94, 160x160, 320x320, 1024x1024, 2048x2048* | | PNG | 1024x1024*, 2048x2048* | Some restrictions apply to the sizes marked as `*`. ## File size restrictions ### Original file size Thumbnails are not scaled up. If the original file size of the file uploaded to Box is smaller than the representation dimensions, the resulting thumbnail is capped at the size of the original file. ## Supported file types At this time the following file types are supported. | File Type | File Extensions | | --- | --- | | Documents | doc, docx, gdoc, gsheet, gslide, gslides, odp, ods, odt, pdf, ppt, pptx, rtf, wpd, xls, xlsm, xlsx, key, pages, numbers | | Images | ai, bmp, dcm, dicm, eps, gif, idml, indd, indt, inx, jpeg, jpg, png, ps, psd, svg, svs, tif, tiff, tga | | Audio | aac, aifc, aiff, amr, au, flac, m4a, mp3, ogg, ra, wav, wma | | Video | 3g2, 3gp, avi, m2v, m2ts, m4v, mkv, mov, mp4, mpeg, mpg, mts, ogg, qt, wmv | **Reference:** https://developer.box.com/guides/representations/thumbnail/ --- ## Untitled *Type: guide | Category: Representations * Get Thumbnail Representation A thumbnail is a small image, either as .png or as .jpg that can be used in an application as a representation… # Get Thumbnail Representation A thumbnail is a small image, either as `.png` or as `.jpg` that can be used in an application as a representation of the file, for example as a placeholder for a link that downloads or previews the file. All thumbnail representations except `1024x1024` and `2048x2048` PNGs are generated upon uploading the source file to Box. An deprecated way to get a thumbnail for a file is using the [thumbnail API](guide://representations/thumbnail). ## The process To get a thumbnail representation follow the following steps - [List all representations](guide://representations/list-all-representations) - [Request a thumbnail](guide://representations/request-a-representation) by passing the `x-rep-hints`-header for the desired thumbnail format and size, for example `[jpg?dimensions=32x32]`. - [Download the thumbnail](guide://representations/download-a-representation) by calling the `url_template`, replacing the `{+asset_path}` with an empty string. Sometimes the thumbnail can not be created directly. Instead, the API will return a `HTTP 202` with a `location` response header. The location is for a temporary image that can be used while the thumbnail is being generated. A retry-after response header is also provided to present you with an estimated amount of seconds before retrying this endpoint. ## Examples The following a some example `x-rep-hints`-header values | x-rep-hints: [jpg?dimensions=32x32] | | --- | | Returns a 32x32 JPEG thumbnail | | x-rep-hints: [jpg?dimensions=32x32][jpg?dimensions=1024x1024] | | --- | | Returns 32x32 and 1024x1024 JPEG thumbnails | | x-rep-hints: [jpg?dimensions=32x32][png?dimensions=2048x2048] | | --- | | Returns a 32x32 JPEG and a 2048x2048 PNG thumbnail | | x-rep-hints: [jpg?dimensions=2048x2048,png?dimensions=2048x2048] | | --- | | Returns a 2048x2048 JPEG or a 2048x2048 PNG thumbnail, returning the first representation that is available. If neither is available it returns no representations | ## Supported file sizes The following formats and sizes of thumbnails are available. | File Type | Dimensions | | --- | --- | | JPG | 32x32, 94x94, 160x160, 320x320, 1024x1024, 2048x2048* | | PNG | 1024x1024*, 2048x2048* | Some restrictions apply to the sizes marked as `*`. ## File size restrictions ### JPEG 2048x2048 The JPEG `2048x2048` size is only available when the original file is a JPEG. We recommend either requesting a PNG or both a PNG and a JPEG for this dimension. ### Video files JPEG `2048x2048`, PNG `2048x20148` and PNG `1024x1024` representations are not available for video files. ### Original file size Thumbnails are not scaled up. If the original file size of the file uploaded to Box is smaller than the representation dimensions, the resulting thumbnail is capped at the size of the original file. ## Supported file types At this time the following file types are supported. | File Type | File Extensions | | --- | --- | | Documents | doc, docx, gdoc, gsheet, gslide, gslides, odp, ods, odt, pdf, ppt, pptx, rtf, wpd, xls, xlsm, xlsx, key, pages, numbers | | Images | ai, bmp, dcm, dicm, eps, gif, idml, indd, indt, inx, jpeg, jpg, png, ps, psd, svg, svs, tif, tiff, tga | | Audio | aac, aifc, aiff, amr, au, flac, m4a, mp3, ogg, ra, wav, wma | | Video | 3g2, 3gp, avi, m2v, m2ts, m4v, mkv, mov, mp4, mpeg, mpg, ogg, mts, qt, wmv | For **document** file types the representation returned will be a placeholder icon and not an actual thumbnail. **Reference:** https://developer.box.com/guides/representations/thumbnail-representation/ --- ## Untitled *Type: guide | Category: Retention Policies * Get Retention Policy To get the information for a specific Retention Policy that has been created in an enterprise, call the GET /retention… # Get Retention Policy To get the information for a specific Retention Policy that has been created in an enterprise, call the [`GET /retention_policies/:id`](e://get_retention_policies_id) API endpoint with the `id` of the policy. ## Required Scopes Before using any of the Retention Policy APIs, an application must have the right scopes enabled. See [Required Scopes](g://retention-policies#required-scopes) for more details. **Reference:** https://developer.box.com/guides/retention-policies/get/ --- ## Untitled *Type: guide | Category: Retention Policies * Retention Policies A retention policy blocks permanent deletion of content for a specified amount of time. Admins can create retention… # Retention Policies A retention policy blocks permanent deletion of content for a specified amount of time. Admins can create retention policies and then assign them to specific folders or their entire enterprise. Retention policies can be used to keep data for as long as is needed, and then automatically delete the content permanently when the data can no longer be legally held. Retention Policies are a feature of the [Box Governance](https://www.box.com/security/governance-and-compliance) package, which can be added on to any Business Plus or Enterprise account. ## Policies, Assignments, and Retentions Working with Retention Policies requires a developer to work with three distinct resources. - **Policies:** A [Retention Policy](r://retention_policy) describes the general behavior of the retention policy. It determines how long a retention should stay in place, if it can be extended, and what happens when the retention policy ends. - **Assignments:** A [Retention Policy Assignment](r://retention_policy_assignment) is a relation between a policy and folder or enterprise. Creating an assignment puts a retention on all the file versions that belong to that folder or enterprise. For example, if an assignment is created on a folder the policy is applied to all file versions within that folder. - **Retentions**: A [File Version Retention](r://file_version_retention) represents all the policies that are assigned to a specific file version. Note that every file version can have a maximum of one file version retention and that this resource contains a list of every assigned policy. The [file version retention](r://file_version_retention) section of the Box API is now deprecated. Instead, you can use [files under retention](e://get-retention-policy-assignments-id-files-under-retention) or [file versions under retention](e://get-retention-policy-assignments-id-file-versions-under-retention) endpoints. ## File Deletion with Retention Policies Files under retention can be deleted from folders, but they will be retained in the trash until the retention expires. When the retention expires, you can choose to have the content automatically deleted or for the policy to be removed. ## Extend Retention for a File Files under retention can have their retention date extended by [updating](e://put-files-id/#param-disposition_at) the `disposition_at` field's value with a future date. Once the date has been extended, it cannot be reverted or changed to be prior to the new date. ## Required Scopes Before using any of the Retention Policy APIs, an application must have the GCM and Manage Retention Policies scopes enabled. These are not available in the Developer Console and need to instead be enabled by contacting customer support. **Reference:** https://developer.box.com/guides/retention-policies/ --- ## Untitled *Type: guide | Category: Retention Policies * List All Retention Policies To list all Retention Policies that have been created in an enterprise, call the GET /retention_policies API… # List All Retention Policies To list all Retention Policies that have been created in an enterprise, call the [`GET /retention_policies`](e://get_retention_policies) API endpoint. ## Required Scopes Before using any of the Retention Policy APIs, an application must have the right scopes enabled. See [Required Scopes](g://retention-policies#required-scopes) for more details. **Reference:** https://developer.box.com/guides/retention-policies/list/ --- ## Untitled *Type: guide | Category: Search * Enterprise-wide search By default, a search is only performed against the content that the authenticated user has access to. In some cases… # Enterprise-wide search By default, a search is only performed against the content that the authenticated user has access to. In some cases, administrators might want to search against all content owned by all users. For this use-case the `scope` query parameter can be set to a value of `enterprise_content`. ``` curl -i -X GET "https://api.box.com/2.0/search?query=sales&scope=enterprise_content" \ -H "Authorization: Bearer <ACCESS_TOKEN>" ``` ``` long offsetValue = 0; long limitValue = 10; BoxSearch boxSearch = new BoxSearch(api); BoxSearchParameters searchParams = new BoxSearchParameters(); searchParams.setQuery("sales"); searchParams.setScope("enterprise_content"); PartialCollection<BoxItem.Info> searchResults = boxSearch.searchRange(offsetValue, limitValue, searchParams); ``` ``` BoxCollection<BoxItem> results = await client.SearchManager .QueryAsync("sales", mdFilters: filters, scope: "enterprise_content"); ``` ``` client.search().query("sales", metadata_filters=metadata_search_filters, scope="enterprise_content") ``` ``` client.search.query( 'sales', { scope: "enterprise_content" }) .then(results => { // ... }); ``` The `enterprise_content` scope can be requested by an admin through our [support channels](p://support). Once this scope has been enabled for a user, it will allow that user to query for content across the entire enterprise and not only the content that they have access to. **Reference:** https://developer.box.com/guides/search/enterprise/ --- ## Untitled *Type: guide | Category: Search * Requesting extra fields By default, the search API returns the standard format of a File, Folder, or Web Link. Each of these resources… # Requesting extra fields By default, the search API returns the **standard** format of a [File](r://file), [Folder](r://folder), or [Web Link](r://web_link). Each of these resources supports additional fields that can be requested through the `fields` query parameter. ``` curl -i -X GET "https://api.box.com/2.0/search?query=sales&fields=name,tags" \ -H "Authorization: Bearer <ACCESS_TOKEN>" ``` ``` long offsetValue = 0; long limitValue = 10; BoxSearch boxSearch = new BoxSearch(api); BoxSearchParameters searchParams = new BoxSearchParameters(); searchParams.setQuery("sales"); final List<String> fields = new ArrayList<String>(); fields.add("name"); fields.add("tags"); searchParams.setFields(fields) PartialCollection<BoxItem.Info> searchResults = boxSearch.searchRange(offsetValue, limitValue, searchParams); ``` ``` IEnumerable<string> fields = new List<string>() { "name", "tags"}; BoxCollection<BoxItem> results = await client.SearchManager .QueryAsync("sales", fields: fields); ``` ``` client.search().query("sales", metadata_filters=metadata_search_filters, fields=["name", "tags"]) ``` ``` client.search.query( 'sales', { fields: "name,tags" }) .then(results => { // ... }); ``` For more details on these fields, please check out the [full File](r://file--full), [full Folder](r://folder--full), and [full Web Link](r://web_link) resources. When the `fields` parameter is used to query additional information about the items, only those fields and a few **base** fields (`id`, `type`, `name`, etc) are returned. Any fields that were originally in the response would now have to be requested explicitly. **Reference:** https://developer.box.com/guides/search/fields/ --- ## Untitled *Type: guide | Category: Search * Filter search results The GET /search API supports a variety of different ways to filter the results returned by the API. Filter by content… # Filter search results The [`GET /search`](e://get_search) API supports a variety of different ways to filter the results returned by the API. ## Filter by content type By default, a search returns items for which either the name, description, file content, tags, or comments match the query provided. By setting the `content_types` parameter the search can be narrowed down to only the items that match the query for the content type defined. ``` curl -i -X GET "https://api.box.com/2.0/search?query=sales&content_types=name,tags" \ -H "Authorization: Bearer <ACCESS_TOKEN>" ``` ``` long offsetValue = 0; long limitValue = 10; BoxSearch boxSearch = new BoxSearch(api); BoxSearchParameters searchParams = new BoxSearchParameters(); searchParams.setQuery("sales"); final List<String> contentTypes = new ArrayList<String>(); contentTypes.add("name"); contentTypes.add("tags"); searchParams.setContentTypes(contentTypes) PartialCollection<BoxItem.Info> searchResults = boxSearch.searchRange(offsetValue, limitValue, searchParams); ``` ``` var contentTypes = new List<string>(); contentTypes.Add("name"); contentTypes.Add("tags"); BoxCollection<BoxItem> results = await client.SearchManager .QueryAsync("sales", contentTypes: contentTypes); ``` ``` client.search().query("sales", content_types=["name", "tags"]) ``` ``` client.search.query( 'sales', { content_types: [ "name", "tags" ] }) .then(results => { // ... }); ``` | Content type | | | --- | --- | | name | The name of the item, as defined by its name field. | | description | The description of the item, as defined by its description field. | | file_content | The actual content of the file. | | comments | The content of any of the comments on a file or folder. | | tags | Any tags that are applied to an item, as defined by its tags field. | ## Filter by date By default, search returns files created at any time, and updated at any time. It is possible to filter results by both the date the file or folder was last updated or when it was created. ``` curl -i -X GET "https://api.box.com/2.0/search?query=sales&created_at_range=2014-05-15T13:35:01Z,2015-05-15T13:35:01&updated_at_range=2014-05-15T13:35:01," \ -H "Authorization: Bearer <ACCESS_TOKEN>" ``` ``` long offsetValue = 0; long limitValue = 10; BoxSearch boxSearch = new BoxSearch(api); BoxSearchParameters searchParams = new BoxSearchParameters(); searchParams.setQuery("sales"); String createdFromDateString = "2014-05-15T13:35:01Z"; String createdToDateString = "2015-05-15T13:35:01Z"; Date createdFromDate = sdf.parse(createdFromDateString); Date createdToDate = sdf.parse(createdToDateString); DateRange createdRange = new DateRange(createdFromDate, createdToDate); searchParams.setCreatedRange(createdRange) String updatedFromDateString = "2014-05-15T13:35:01Z"; Date updatedFromDate = sdf.parse(updatedFromDateString); DateRange updatedRange = new DateRange(updatedFromDate, null); searchParams.setUpdatedRange(updatedRange) PartialCollection<BoxItem.Info> searchResults = boxSearch.searchRange(offsetValue, limitValue, searchParams); ``` ``` var createdAtRangeFromDate = new DateTime(1988, 11, 18, 9, 30, 0, DateTimeKind.Utc); var createdAtRangeToDate = new DateTime(2018, 11, 18, 9, 30, 0, DateTimeKind.Utc); var updatedAtRangeFromDate = new DateTime(1988, 11, 18, 9, 30, 0, DateTimeKind.Utc); BoxCollection<BoxItem> results = await client.SearchManager .QueryAsync("sales", contentTypes: contentTypes, createdAtRangeFromDate: createdAtRangeFromDate, createdAtRangeToDate: createdAtRangeToDate, updatedAtRangeFromDate: updatedAtRangeFromDate); ``` ``` client.search().query("sales", created_at_range=["2014-05-15T13:35:01Z", "2015-05-15T13:35:01Z"], updated_at_range=["2014-05-15T13:35:01Z", null]) ``` ``` client.search.query( 'sales', { created_at_range: "2014-05-15T13:35:01Z,2015-05-15T13:35:01Z", updated_at_range: "2014-05-15T13:35:01Z," }) .then(results => { // ... }); ``` | Query parameter | | | --- | --- | | created_at_range | Defines a range of created_at dates for which to return results. The upper or lower bound can be left empty to create an open-ended range. | | updated_at_range | Defines a range of updated_at dates for which to return results. The upper or lower bound can be left empty to create an open-ended range. | ## Filter by file extension By default, a search returns items with any kind of file extension. It is possible to filter search results to only files with one or more specific file extensions using the `file_extensions` query parameter. ``` curl -i -X GET "https://api.box.com/2.0/search?query=sales&file_extensions=pdf,txt" \ -H "Authorization: Bearer <ACCESS_TOKEN>" ``` ``` long offsetValue = 0; long limitValue = 10; BoxSearch boxSearch = new BoxSearch(api); BoxSearchParameters searchParams = new BoxSearchParameters(); searchParams.setQuery("sales"); final List<String> fileExtensions = new ArrayList<String>(); fileExtensions.add("pdf"); fileExtensions.add("txt"); searchParams.setFileExtensions(fileExtensions) PartialCollection<BoxItem.Info> searchResults = boxSearch.searchRange(offsetValue, limitValue, searchParams); ``` ``` var fileExtensions = new List<string>(); fileExtensions.Add("pdf"); fileExtensions.Add("txt"); BoxCollection<BoxItem> results = await client.SearchManager .QueryAsync("sales", fileExtensions: fileExtensions); ``` ``` client.search().query("sales", file_extensions=["pdf", "txt"]) ``` ``` client.search.query( 'sales', { file_extensions: [ "pdf", "txt" ] }) .then(results => { // ... }); ``` ## Filter by file size By default, a search returns items of any file size. It is possible to filter search results to only files within a specific file size using the `size_range` query parameter. ``` curl -i -X GET "https://api.box.com/2.0/search?query=sales&size_range=10000,20000" \ -H "Authorization: Bearer <ACCESS_TOKEN>" ``` ``` long offsetValue = 0; long limitValue = 10; BoxSearch boxSearch = new BoxSearch(api); BoxSearchParameters searchParams = new BoxSearchParameters(); searchParams.setQuery("sales"); SizeRange sizeRange = new SizeRange(10000, 20000); searchParams.setSizeRange(sizeRange); PartialCollection<BoxItem.Info> searchResults = boxSearch.searchRange(offsetValue, limitValue, searchParams); ``` ``` BoxCollection<BoxItem> results = await client.SearchManager .QueryAsync("sales", sizeRangeLowerBoundBytes: 10000, sizeRangeUpperBoundBytes: 20000); ``` ``` client.search().query("sales", size_range=[10000,20000]) ``` ``` client.search.query( 'sales', { size_range: '10000,20000' }) .then(results => { // ... }); ``` ## Filter by file type By default, a search returns both files, folders, and web links. To narrow down the results to only one of these, a `type` query parameter can be set to either `file`, `folder` or `web_link`. ``` curl -i -X GET "https://api.box.com/2.0/search?query=sales&type=file" \ -H "Authorization: Bearer <ACCESS_TOKEN>" ``` ``` long offsetValue = 0; long limitValue = 10; BoxSearch boxSearch = new BoxSearch(api); BoxSearchParameters searchParams = new BoxSearchParameters(); searchParams.setQuery("sales"); searchParams.setType("file"); PartialCollection<BoxItem.Info> searchResults = boxSearch.searchRange(offsetValue, limitValue, searchParams); ``` ``` BoxCollection<BoxItem> results = await client.SearchManager .QueryAsync("sales", type: "file"); ``` ``` client.search().query("sales", type="file") ``` ``` client.search.query( 'sales', { type: "file" }) .then(results => { // ... }); ``` ## Filter by metadata It is possible to filter search results by their associated metadata, or even perform entire searches based on only the metadata, all using the `mdfilters` query parameter. Learn more about metadata filters ## Filter by owner By default, a search returns all the items the authenticated user has access to, regardless of who owns the items. To narrow down to only items owned by specific users, use the `owner_user_ids` query parameter. ``` curl -i -X GET "https://api.box.com/2.0/search?query=sales&owner_user_ids=34446362,462281242" \ -H "Authorization: Bearer <ACCESS_TOKEN>" ``` ``` long offsetValue = 0; long limitValue = 10; BoxSearch boxSearch = new BoxSearch(api); BoxSearchParameters searchParams = new BoxSearchParameters(); searchParams.setQuery("sales"); final List<String> userIds = new ArrayList<String>(); userIds.add("34446362"); userIds.add("462281242"); searchParams.setOwnerUserIds(userIds) PartialCollection<BoxItem.Info> searchResults = boxSearch.searchRange(offsetValue, limitValue, searchParams); ``` ``` var userIds = new List<string>(); userIds.Add("34446362"); userIds.Add("462281242"); BoxCollection<BoxItem> results = await client.SearchManager .QueryAsync("sales", ownerUserIds: userIds); ``` ``` client.search().query("sales", owner_user_ids=["34446362", "462281242"]) ``` ``` client.search.query( 'sales', { owner_user_ids: "34446362,462281242" }) .then(results => { // ... }); ``` ## Filter by parent folder By default, a search returns all the items in any folder the user has access to. To narrow down the results to only items in specific folders, use the `ancestor_folder_ids` query parameter. ``` curl -i -X GET "https://api.box.com/2.0/search?query=sales&ancestor_folder_ids=45235463,73445321" \ -H "Authorization: Bearer <ACCESS_TOKEN>" ``` ``` long offsetValue = 0; long limitValue = 10; BoxSearch boxSearch = new BoxSearch(api); BoxSearchParameters searchParams = new BoxSearchParameters(); searchParams.setQuery("sales"); final List<String> folderIds = new ArrayList<String>(); folderIds.add("45235463"); folderIds.add("73445321"); searchParams.setAncestorFolderIds(folderIds) PartialCollection<BoxItem.Info> searchResults = boxSearch.searchRange(offsetValue, limitValue, searchParams); ``` ``` var folderIds = new List<string>(); folderIds.Add("45235463"); folderIds.Add("73445321"); BoxCollection<BoxItem> results = await client.SearchManager .QueryAsync("sales", ancestorFolderIds: folderIds); ``` ``` client.search().query("sales", ancestor_folder_ids=["45235463", "73445321"]) ``` ``` client.search.query( 'sales', { ancestor_folder_ids: "45235463,73445321" }) .then(results => { // ... }); ``` **Reference:** https://developer.box.com/guides/search/filtering/ --- ## Untitled *Type: guide | Category: Search * Search The Box API provides a way to find content in Box using full-text search queries. Support for the Box search API is available in all… # Search The Box API provides a way to find content in Box using full-text search queries. Support for the Box search API is available in all our supported SDKs and the CLI. Explore the [reference documentation](e://get_search) to learn more about all the different features available to the search API. ## Query operators The search API supports a few different [search operators](g://search/query-operators), including `AND`, `OR`, `NOT` and `""`. These operators can be used to refine the search results to only return items that match a more complicated combination of search terms. ``` curl -i -X GET "https://api.box.com/2.0/search?query=box%20AND%20sales" \ -H "Authorization: Bearer <ACCESS_TOKEN>" ``` Learn more about using logical operators ## Search Indexing Box keeps a search index for any files or folder stored in Box. Every time a file or folder is changed, those words are added to the index. When a search is performed, the API looks in the search index for files and folders that match the query. When content is added, updated, or deleted in Box, the search index is updated accordingly. Learn more about the Box search index In some cases an index might not be updated even after 10 minutes. In those cases we recommend reaching out to [Box Support](page://support) to get the issue resolved. If your enterprise has full text search turned off (e.g. [Keysafe](https://www.box.com/security/keysafe) customers), characters within a document cannot be searched. If you need to find out if full text search is turned off, reach out to your account team. ## Comparison to Metadata Queries At the surface the search API seems very similar to the [Metadata Query API](g://metadata/queries), but there are several important differences in how they operate. At a high level the Metadata Queries are optimized for exactness and throughput, while regular search is optimized for relevance to a human user. | | Metadata Query API | Search API | | --- | --- | --- | | What is indexed? | This API only return files/folders based on the values in the metadata templates that are searched | This API returns files, folders and web links based on values in the item names, descriptions, contents (up to the first 10,000 bytes) as well as the associated metadata template instances | | Indexing time | This API will return accurate results as soon as metadata has been added, removed, updated or deleted for a file or folder | This API is subject to a search indexing delay, which is typically 10 minutes yet may be longer in some cases. This means that items may not be returned for more than 10 minutes after metadata has been updated | | Matching | This API uses exact matching based on SQL conventions. Results are returned based on a specified sort order | This API uses fuzzy matching and may return results that vary based on string tokenization, removal of special characters, and other search concepts. Result order is based on either relevance or the updated date of the item | | Conditional logic | This API supports multi-part boolean expressions with comparison operators | This API has limited support for querying by metadata. It only supports querying 1 metadata template at a time and only allows simple query operations. | | Response type | This API returns both the matched file/folder and the associated metadata matched by the query | This API only returns the matched item. A subsequent API call is needed to return each item's metadata | | Throughput | This API is currently subject to per-user rate limits and to a 10 requests per second per enterprise limit | This API supports 6 searches per second per user, up to 60 searches per minute and 12 searches per second per enterprise | | Scale | This API has no limit on the number of items with the specified metadata template that can be returned. It is recommended to only send queries which match no more than 2,000 results. | This API has no limit on number of items with the specified metadata template that can be returned, yet the response time increases significantly as the number of items matching the search grows. This API does have a limit of up to 10 million results for a query. It is recommended to only send queries which match no more than 50,000 results. | | Scope | This API is always limited to the content to which the user has access | This API may be either limited to the content to which the user has access (​user_content​) or to all content in the enterprise (​enterprise_content​). | Learn more about the metadata query API **Reference:** https://developer.box.com/guides/search/ --- ## Untitled *Type: guide | Category: Search * Search indexing Box keeps a search index for any files or folders stored in Box. Every time a file or folder is changed, those words are… # Search indexing Box keeps a search index for any files or folders stored in Box. Every time a file or folder is changed, those words are added to the index. When a search is performed, the API looks in the search index for files and folders that match the query. When content is added, updated, or deleted in Box, the search index is updated accordingly. ## Search Availability It can take time between uploading or modifying a file for it to be fully indexed and ready to be searched. In most cases, newly added or changed files can be expected to be available via Box search in 10 minutes. The current service load determines the index time and it may take more than 10 minutes in some cases. In some cases an index might not be updated even after 10 minutes. In those cases we recommend reaching out to [Box Support](page://support) to get the issue resolved. ## Search Access Only content that the authenticated user has access to (items they can preview and/or view) will be returned in the search results. In other words, a user needs to either own an item or be a collaborated in on an item for it to show up in the search results. If a user doesn't have access to an item, or if they have been shared the item via a shared link, then the item will also not appear in the search results. One exception is that items that have been recently accessed via a shared link can be requested in the search results by setting the `include_recent_shared_links` query parameter to `true`. ## Prefix Matching and Wildcard Search Trailing wildcards (also known as prefix matching) are implicitly included in search results because of the way text is indexed. Searching for `Bo` results in items with titles containing `Box` or `Boat` or `Boxer`. It is the equivalent of searching for `Bo*` or `Bo%` in traditional search engines. Traditional wildcard notation is not supported by Box, such as `%ox%`. While we support prefix matching on titles, we do not support prefix matching on body content, suffix matching in the title or body content, or infix (middle of the word) matching in the title or body content. For example, a search on `cal` would match results for a file named `California` but not `decal` or `recall`. It would not match results with **file body contents** of prefixes, infixes, or suffixes including `California`, `recall`, or `decal`. ## Stemming Box Search uses stemming to match terms from the query to terms in the index. Because of this, words that include the same stem may be included in the result set, even if the words do not contain the exact form in the query. For example, `run` and `running` map to the same stem, so a search on `running` may return a document containing `run` in the title. ## File Content Searching The content within files is also stored within the Box search index. The following file types allow searching for their content: | | | | | | | --- | --- | --- | --- | --- | | boxnote | csv | doc | docx | gdoc | | gsheet | gslide | gslides | htm | html | | msg | odp | odt | ods | pdf | | ppt | pptx | rtf | tsv | wpd | | xhtml | xls | xlsm | xlsx | xml | | xsd | xsl | as | as3 | asm | | bat | c | cc | cmake | cpp | | cs | css | cxx | diff | erb | | groovy | h | haml | hh | java | | js | json | less | log | m | | make | md | ml | mm | php | | pl | plist | properties | py | rb | | rst | sass | scala | script | scm | | sml | sql | sh | txt | vi | | vim | webdoc | yaml | | | ## Indexed Text per Document The Box search index stores up to 10,000 bytes (~10,000 characters in English) per document for accounts from Business level and above. This amount can vary from document to document because of language, Box’s indexing method, and document type. If your enterprise has full text search turned off (e.g. [Keysafe](https://www.box.com/security/keysafe) customers), characters within a document cannot be searched. If you need to find out if full text search is turned off, reach out to your account team. ## OCR Support Box does not currently perform OCR on its documents. ## Document Versions Search only indexes content from the current version of a document, so that you do not have to sift through hundreds of irrelevant search results of outdated documents. You cannot use search to query non-current versions of a document. ## Language Support Box search supports the following languages: Chinese, English, French, German, Italian, Japanese, and Spanish. Box does not support indexing of multiple languages within a single document. ## Trash Searching the trash is available via the API by using the `trash_content` query parameter. Check our community article with the latest details on Search in Box **Reference:** https://developer.box.com/guides/search/indexing/ --- ## Untitled *Type: guide | Category: Search * Metadata Query Filters The GET /search API allows for filtering search results by their associated metadata. A mdfilters query parameter… # Metadata Query Filters The [`GET /search`](e://get_search) API allows for filtering search results by their associated metadata. A `mdfilters` query parameter allows a developer to specify a metadata template and the desired values to query. ``` curl -i -X GET "https://api.box.com/2.0/search?query=sales&mdfilters=%5B%7B%22scope%22%3A%22enterprise%22%2C%22templateKey%22%3A%22contract%22%2C%22filters%22%3A%7B%22category%22%3A%22online%22%7D%7D%5D" \ -H "Authorization: Bearer <ACCESS_TOKEN>" ``` ``` long offsetValue = 0; long limitValue = 10; BoxSearch boxSearch = new BoxSearch(api); BoxSearchParameters searchParams = new BoxSearchParameters(); searchParams.setQuery("sales"); BoxMetadataFilter bmf = new BoxMetadataFilter(); bmf.setScope("enterprise"); bmf.setTemplateKey("contract"); bmf.setFilter("category", "online") searchParams.setMetadataFilter(bmf) PartialCollection<BoxItem.Info> searchResults = boxSearch.searchRange(offsetValue, limitValue, searchParams); ``` ``` var filter = new { category = "online" }; var filters = new List<BoxMetadataFilterRequest>() { new BoxMetadataFilterRequest() { Scope = "enterprise", TemplateKey = "contract", Filters: filter } }; BoxCollection<BoxItem> results = await client.SearchManager .QueryAsync("sales", mdFilters: filters); ``` ``` from boxsdk.object.search import MetadataSearchFilter, MetadataSearchFilters metadata_search_filter = MetadataSearchFilter(scope='enterprise', template_key='contract') metadata_search_filter.add_value_based_filter(field_key='category', value='online') metadata_search_filters = MetadataSearchFilters() metadata_search_filters.add_filter(metadata_search_filter) client.search().query("sales", metadata_filters=metadata_search_filters) ``` ``` client.search.query( 'sales', { mdfilters: [ { scope: 'enterprise', templateKey: 'contract', filters: { category: 'online' } } ] }) .then(results => { // ... }); ``` This example filters a search for any content that matches the query `sales` by any item that has `enterprise.contract` metadata attached to it and where the `category` field is set to `online`. ## Introduction to Metadata Metadata allows users and applications to define and store custom data associated with files and folders. Metadata consists of key/value pairs that are assigned to a file or a folder. For example, an important contract may have the key/value pairs of `clientNumber: 820183` and `category: online`. The `mdfilters` query parameter allows developers to find files and folders that have a specific piece of metadata attached to them. Learn more about metadata templates and instances ## Metadata Filter Syntax The `mdfilters` parameter can currently only contain one filter, although this may be expanded in the future. Each filter defines the `scope` and `templateKey` of the metadata template to filter on. ``` [ { "scope": "enterprise", "templateKey": "contract", "filters": {} } ] ``` To get the `scope` and `templateKey` for a template, either [list all metadata templates](g://metadata/templates/list) for an enterprise, or [list all metadata instances on an item](g://metadata/instances/list). With the template defined, the `filters` field accepts a few different filter formats. The format of the filter very much depends on the type of field being filtered by. ### Filter by string field To filter by a field of type `string` a filter will need to define the `key` of the field and the desired value to find items for. ``` [ { "scope": "enterprise", "templateKey": "contract", "filters": { "category": "online" } } ] ``` This example will find all files and folders that have an instance of the `enterprise.contract` template applied to it, and for which the field with the key `category` is set to the value `online`. ### Filter by float field To filter by a field of type `float`, you will need to define define a range by specifying a `gt` (greater-than) and/or `lt` (lower-than) value. To find an exact value, you can input the same value for both `gt` and `lt`. ``` [ { "scope": "enterprise", "templateKey": "contract", "filters": { "amount": { "gt": 10000, "lt": 20000 } } } ] ``` This example will find all files and folders that have an instance of the `enterprise.contract` template applied to it, and for which the field with the key `amount` is set to a value equal or higher than `10000` and equal or lower than `2000`. Note that `gt` and `lt` are inclusive and do not need to both be set. If you create a query based on numbers, do not exceed the range of -16777215 and +16777215. For metadata search using number attributes the index value is stored as FLOAT32. As a result, integers between -16777215 and +16777215 can be precisely represented. Any operation with numbers beyond the range can lose its precision. ### Filter by date field To filter by a field of type `date` a filter will need to define the `key` of the field and the desired range to find items by specifying a `gt` (greater-than) and `lt` (lower-than) value. Please note that `gt` and `lt` are inclusive. ``` [ { "scope": "enterprise", "templateKey": "contract", "filters": { "expirationDate": { "gt": "2016-08-01T00:00:00Z", "lt": "2017-08-01T00:00:00Z" } } } ] ``` This example will find all files and folders that have an instance of the `enterprise.contract` template applied and have an `expirationDate` set to a date after or exactly `2016-08-01T00:00:00Z` and before or exactly `2017-08-01T00:00:00Z`. ### Filter by enum field To filter by a field of type `enum` a filter will need to define the `key` of the field and the desired value to find items for. ``` [ { "scope": "enterprise", "templateKey": "contract", "filters": { "category": "online" } } ] ``` This example will find all files and folders that have an instance of the `enterprise.contract` template applied to it, and for which the field with the key `category` is set to the value `online`. ### Filter by multiSelect field To filter by a field of type `multiSelect` a filter will need to define the `key` of the field and any of the potential desired values to find items for. When performing a search, the query will essentially perform an `OR` operation to match any template where any of the provided values match this field. ``` [ { "scope": "enterprise", "templateKey": "contract", "filters": { "category": [ "online", "enterprise" ] } } ] ``` This example will find all files and folders that have an instance of the `enterprise.contract` template applied to it, and for which the field with the key `category` is set to the value `online` or `enterprise`. **Reference:** https://developer.box.com/guides/search/metadata-filters/ --- ## Untitled *Type: guide | Category: Search * Pagination The search API supports offset-based pagination using the offset and limit query parameters. Marker-based pagination is not… # Pagination The search API supports offset-based pagination using the `offset` and `limit` query parameters. Marker-based pagination is not supported. ## API Pagination To fetch the first page of search results, the API needs to be called either without the `offset` parameter, or with the `offset` set to `0`. The `limit` field is optional. ``` curl https://api.box.com/2.0/search?query=sales&offset=0&limit=100 \ -H "authorization: Bearer ACCESS_TOKEN" ``` To fetch the next page of entries the API needs to be called with an `offset` parameter that's equals the sum of the previous `offset` value and limit returned in the previous result, `previous_offset + previous_limit`. ``` curl https://api.box.com/2.0/search?query=sales&offset=100&limit=100 \ -H "authorization: Bearer ACCESS_TOKEN" ``` Note that the `offset` should be increased by the previous `limit` and not by the size of the entries in the response array, as this may be less than the limit. Generally we advise using the value of the `limit` in the response object to increase the `offset` value. The final page of items has been requested when the next `offset` value exceeds the `total_count` value in the response object. At this point there are no more items to fetch. Learn more about offset-based pagination ## SDK Pagination Each of our SDKs has built-in support for API pagination. The following code samples show how to use pagination in the search API. ``` long offsetValue = 0; long limitValue = 50; BoxSearch boxSearch = new BoxSearch(api); BoxSearchParameters searchParams = new BoxSearchParameters(); searchParams.setQuery("sales"); PartialCollection<BoxItem.Info> page1 = boxSearch.searchRange(offsetValue, limitValue, searchParams); offsetValue += 50; PartialCollection<BoxItem.Info> page2 = boxSearch.searchRange(offsetValue, limitValue, searchParams); ``` ``` BoxCollection<BoxItem> page1 = await client.SearchManager .QueryAsync("sales", limit: 50); BoxCollection<BoxItem> page2 = await client.SearchManager .QueryAsync("sales", limit: 50, offset: 50); ``` ``` page1 = client.search().query(query='sales', limit=50) page2 = client.search().query(query='sales', limit=50, offset=50) ``` ``` const page1 = await client.search.query('sales', { limit: 50 }) const page2 = await client.search.query('sales'. { limit: 50, offset: 50 }) ``` **Reference:** https://developer.box.com/guides/search/pagination/ --- ## Untitled *Type: guide | Category: Search * Query operators The GET /search API supports a variety of different query operators to help refining the results returned by the API. All of… # Query operators The [`GET /search`](e://get_search) API supports a variety of different query operators to help refining the results returned by the API. All of these operations are passed into the `query` parameter when creating the search. ## Exact matches with "" By wrapping a query in double quotes (`""`) only exact matches are returned by the API. Exact searches do not return search matches based on specific character sequences. Instead, they return matches based on phrases, that is, word sequences. For example, a search for `"Blue-Box"` may return search results including the sequence `"blue.box"`, `"Blue Box"`, and `"Blue-Box"`; any item containing the words `Blue` and `Box` consecutively, in the order specified. ## Matching multiple terms with AND When the `AND` operator is used, the search returns items that contain both the search terms on the left and right of the operator. For example, a search for `marketing AND BoxWorks` returns items that have both `marketing` and `BoxWorks` within its text in any order. It does not return a result that only has `BoxWorks` in its text. ## Matching either search term with OR When the `OR` operator is used, the search returns items that contain either the search terms on the left or right of the operator. For example, a search for `marketing OR BoxWorks` returns a result that has either `marketing` or `BoxWorks` within its text. Using this operator is not necessary as we implicitly interpret multi-word queries as `OR` unless another supported boolean term is used. ## Excluding search terms with NOT When the `NOT` operator is used, the search returns items that do not contain the term that follows the operator. For example, a search for `marketing AND NOT BoxWorks` returns a result that has only `marketing` within its text. Results containing `BoxWorks` are omitted. Please note that we do not support lower case (that is, `and`, `or`, and `not`) or mixed case (that is, `And`, `Or`, and `Not`) operators. Check our community article with the latest details on Search in Box **Reference:** https://developer.box.com/guides/search/query-operators/ --- ## Untitled *Type: guide | Category: Search * Finding recent shared links By default, the search API only returns items that are either owned by the user or items that the user has been… # Finding recent shared links By default, the search API only returns items that are either owned by the user or items that the user has been explicitly collaborated on. These search results do not include any items that a user might have accessed recently through a shared link. To enable shared links in the API, the `include_recent_shared_links` query parameter can be set to `true`. ``` curl -i -X GET "https://api.box.com/2.0/search?query=sales&include_recent_shared_links=true" \ -H "Authorization: Bearer <ACCESS_TOKEN>" ``` ``` client.search().query("sales", metadata_filters=metadata_search_filters, include_recent_shared_links=True) ``` ``` client.search.query( 'sales', { include_recent_shared_links: true }) .then(results => { // ... }); ``` Please be aware that this parameter is relatively new and therefore support for it in our Java and Windows SDKs is still a work in progress. Please be very aware that when this parameter has been set to true, the format of the response of this API changes to return a list of [Search Results with Shared Links](r://search-results-with-shared-links) **Reference:** https://developer.box.com/guides/search/shared-links/ --- ## Untitled *Type: guide | Category: Search * Searching trash By default, any content in the user's trash is ignored in the search results. To search the user's trash, the trash_content… # Searching trash By default, any content in the user's trash is ignored in the search results. To search the user's trash, the `trash_content` query parameter can be set to `trashed_only`. ``` curl -i -X GET "https://api.box.com/2.0/search?query=sales&trash_content=trashed_only" \ -H "Authorization: Bearer <ACCESS_TOKEN>" ``` ``` long offsetValue = 0; long limitValue = 10; BoxSearch boxSearch = new BoxSearch(api); BoxSearchParameters searchParams = new BoxSearchParameters(); searchParams.setQuery("sales"); searchParams.setTrashContent("trashed_only"); PartialCollection<BoxItem.Info> searchResults = boxSearch.searchRange(offsetValue, limitValue, searchParams); ``` ``` BoxCollection<BoxItem> results = await client.SearchManager .QueryAsync("sales", mdFilters: filters, trashContent: "trashed_only"); ``` ``` client.search().query("sales", metadata_filters=metadata_search_filters, trash_content="trashed_only") ``` ``` client.search.query( 'sales', { trash_content: "trashed_only" }) .then(results => { // ... }); ``` Currently the API only supports searching for content not in the trash (`non_trashed_only`, default) or in the user's trash (`trashed_only`). It is currently not possible to search for items in both locations at once. **Reference:** https://developer.box.com/guides/search/trash/ --- ## Untitled *Type: guide | Category: Security * Cross-Origin Resource Sharing (CORS) Cross-Origin Resource Sharing (CORS) is a security mechanism used by web browsers to prevent malicious… # Cross-Origin Resource Sharing (CORS) [Cross-Origin Resource Sharing (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) is a security mechanism used by web browsers to prevent malicious websites from accessing data on other sites (like the Box API) without explicit permission. CORS only applies to Box API requests made by a web page using a web browser, and it relies on the `HTTP Origin` header being passed along by the browser. It does not come in to play in a server-side environment. Visit the MDN Web Docs for more generic information about CORS. ## How CORS works When a browser on one domain (for example `company.com`) tries to fetch images, files, or even API resources from another domain (`box.com`), the web browser will prevent access to any of those assets unless the right CORS headers are present. When the browser makes a cross-origin request, an `Origin` request header is passed along with it that contains the domain of the site making that request. This header can not be changed and is part of your web browser's essential security. By default, a browser will not accept any asset loaded from another domain if there is no `Access-Control-Allow-Origin` response header present. Servers like Box can explicitly list the domains allowed to access resources on this server, or they can return a `*` value to allow any domain to access the API. ## How Box uses CORS Box uses the `Origin` request header and `Access-Control-Allow-Origin` response header to enforce CORS rules defined by the developer. ### Origin-header validation The Box API validates the `Origin` request header against the list of allowed domains set by the application developer. Multiple allowed origins can be set and any origin not on the list will return in a `HTTP 403` error. ``` { "type": "error", "status": 403, "code": "cors_origin_not_whitelisted", "context_info": { "origin": "https://company.com" }, "help_url": "https://developer.box.com/guides/api-calls/permissions-and-errors/common-errors/", "message": "Access denied - Did you forget to safelist your origin in the CORS config of your app?", "request_id": "4dsdfsa832213" } ``` If no origin is set, all requests to the Box API for this application return an error. ### Access-Control-Allow-Origin response header After the Box API has validated the `Origin` header, it will return the data requested as well as a `Access-Control-Allow-Origin` response header with the value `*`. ``` HTTP/1.1 200 OK Date: Wed, 23 Sep 2020 14:07:29 GMT Content-Type: application/json Transfer-Encoding: chunked Connection: keep-alive Strict-Transport-Security: max-age=31536000 Cache-Control: no-cache, no-store Access-Control-Allow-Origin: * Vary: Origin BOX-REQUEST-ID: 032cfb446dae4fd0b4c2bff80a1a97ba7 ``` By returning this header, the Box API informs the web browser that the response can be used in the site that requested the data. ## Enabling CORS for your domain To enable CORS for the domain your application runs on, head over to the developer console, select your application, and scroll down to the bottom of the **Configuration** panel to find the **CORS Domains** setting. Add a comma separated list of all the origins that you expect your application to be making Box API requests from. Domains require the schema (`http` or `https`) and can include wildcards for subdomains, for example `*.example.com`. If your site runs on a non-standard port, it will also need to include that. This is especially relevant for a site running on `localhost` or `127.0.0.1`. An example list of origins would be as follows. ``` https://company.com,https://*.internal.company.com,http://localhost:3000 ``` ## Debugging CORS There are a few different kind of CORS errors that might occur when making API calls to the Box API. ### HTTP 403 - No allowed origins defined You might get this error even after you provided a list of origins. Often, this is because of a typo in the provided origins. 1. **Check your origins** - Head back to the developer console and make sure your origins map the site your are making the API call from. Keep in mind that an origin includes the scheme (`http(s)`) but no path or trailing `/`. We recommend inspecting the page using your browser's debug console and checking the `Origin` request header value. This value should match one of the provided values in the developer console. 2. **Check your credentials** - Another reason for this error might be that you are authenticating as a different application than the one you have set the origin up for. Check that the credentials match the ones of the application you are intending to use. We recommend trying to make a call from a server-side script to validate that the API call works. ### Cross-Origin Request Blocked In some cases, you might get a Javascript error that mentions CORS. ``` Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://api.box.com/2.0/users/me. (Reason: CORS request did not succeed). ``` In many cases this has little to do with CORS. Instead we recommend checking the following. 1. **Check your authentication headers** - If the authorization header is not provided or malformed, then the API will return a generic error without the necessary `Access-Control-Allow-Origin` header. This in turn will cause the previously mentioned error to be raised by your browser. Make sure to pass in an access token using the `Authorization: Bearer ...` header. 2. **Check for requests blocked by VPN, Proxies, etc** - In some cases, the Box API might be blocked by your VPN, corporate proxy, a browser extension, your DNS provider, or any other service that can intercept network traffic. Any of these can intercept the request and return a whole new request that does not include the necessary `Access-Control-Allow-Origin` header. To test for this case, try to make the same API call from a non-browser environment, from an incognito window, or from a whole other (not company owned) device. ### Access-Control-Allow-Origin header issues If you encounter issues with the `Access-Control-Allow-Origin` header, do the following: 1. **Check if your domain is on the list of allowed origins** - Go to the developer console and open your application. Click on the **Configuration** tab and scroll down. You can add your domain to the list in section **CORS domains**. 1. **Check if your server is set up correctly** - Configure your server to handle cross-domain requests or use non-cross-domain requests if you receive a warning **No 'access-control-allow-origin' header is present on the requested resource**. **Reference:** https://developer.box.com/guides/security/cors/ --- ## Untitled *Type: guide | Category: Security * FedRAMP Overview FedRAMP is a certification program that allows federal agencies to use cloud providers for increasingly secure/sensitive… # FedRAMP ## Overview FedRAMP is a certification program that allows federal agencies to use cloud providers for increasingly secure/sensitive government or government-adjacent data. FedRAMP defines three categories regarding levels of security, Low, Moderate, and High. The higher the security level the more restrictions are in place. Box is currently certified as [FedRAMP High](https://marketplace.fedramp.gov/products/F1212191840A). ## Considerations In order to be FedRAMP compliant, your administrator must setup Box in very a very specific way. It is possible that the administrator has further restricted access to Box functionalities. Consult with your administrator to identify security restrictions in place that might affect the usage of the API. ## API usage in FedRAMP For FedRAMP compliance, you may use the below URLs for API entry points. | FedRAMP | | --- | | account.box.com | | api.box.com | | upload.box.com | | dl.boxcloud.com | | realtime.services.box.net | **Reference:** https://developer.box.com/guides/security/fedramp/ --- ## Untitled *Type: guide | Category: Security * Security Whether your are a developer getting started with the Box API or a Box Admin tasked with authorizing applications, it is critical… # Security Whether your are a developer getting started with the Box API or a Box Admin tasked with [authorizing](g://authorization/custom-app-approval) applications, it is critical you understand the security mechanisms in place to protect content stored in Box. The Box API follows the same security principals and restrictions as the Box web app. This means that you will not be able to bypass content [permissions](https://support.box.com/hc/en-us/articles/360044196413-Understanding-Collaborator-Permission-Levels), the [waterfall folder structure](https://support.box.com/hc/en-us/articles/360043697254-Understanding-Folder-Permissions), or Admin-only requirements by leveraging the Box API. ## Access Tokens At the core of every Box API call is an [Access Token](g://authentication/tokens). Because a username and password cannot be used, the Box servers need a way of validating user identity. The full capability of an Access Token encompasses user permissions, token permissions, and application settings. Access Tokens represent the authenticated user and determine what content a user can successfully call. Similar to using the Box Web App, you will only be able to successfully interact with content the user, associated with the Access Token, either owns or is a collaborator on. This can be further restricted by [downscoping](g://authentication/tokens/downscope) a token. [Access Tokens](g://authentication/tokens) are only valid for 60 minutes, but can be [revoked](e://post-oauth2-revoke) earlier if needed. Once an Access Token expires, when using an OAuth 2.0 application, a [Refresh Token](g://authentication/tokens/refresh) can be [exchanged](e://post-oauth2-token--refresh) for another Access Token. Refresh tokens are valid for 60 days or one use. Alternatively, when using a server authentication application, the [request Access Token endpoint](e://post-oauth2-token) must be called for a new Access Token. For security reasons we do not allow long-lived access tokens. Unsure why you are receiving a 404 error? A great place to start is checking to see what user is associated with your Access Token by using the get current user endpoint. ## Scopes [Scopes](g://api-calls/permissions-and-errors/scopes) are configured in the [Developer Console](https://app.box.com/developers/console) upon application creation. They determine which of the 150+ endpoints Access Tokens of an application can successfully call. Because scopes work in conjunction with user permissions, granting the write scope does not automatically provide a user with API access to all content in a Box enterprise. Instead, it means that the authenticated user can receive successful API responses when making write calls to content they have access to. For example, take an application with only the manage users and manage groups scopes enabled. If an Access Token of this application tried to make an API call to get information about a folder, even if the associated user owned it, it would receive a 403 error. This is because the read scope is required to preform this action. Access Tokens of this application could only receive successful responses on API calls related to users and groups. ## Restricted endpoints There are some API endpoints that only Admins or Co-Admins, granted the appropriate [permissions](https://support.box.com/hc/en-us/articles/360044194393-Granting-And-Modifying-Co-Admin-Permissions), can successfully use. As a general rule of thumb, if only an Admin or Co-Admin can perform an action via the Box Admin Console, an Access Token associated with one of these users is required to complete an API call for the same action. This is called out in our API [reference](page://reference) documentation for a given endpoint if it is required. Some Admin-restricted endpoints include: - Creating, deleting, or getting information about [users](e://resources/user) - Creating, deleting, or modifying [groups](e://resources/group) - Viewing user or enterprise [events](e://resources/event) Other endpoints can only be used by an Admin user's Access Token if the enterprise has purchased add-on products such as Box Governance or Box Shield. Some of these endpoints include: - Interacting with [security classifications](e://resources/classification) - Interacting with [legal hold policies](e://resources/legal-hold-policy) and [assignments](e://resources/legal-hold-policy-assignment) - Interacting with [retention policies](e://resources/retention-policies) and [assignments](e://resources/retention-policy-assignment) ## Application Access Application access is only configured in the [Developer Console](https://app.box.com/developers/console) for applications leveraging Server Authentication with [JWT)](g://authentication/jwt) or [Client Credentials Grant](g://authentication/client-credentials). This setting determines the [types of users](page://platform/user-types) that can be used with the application. The two options are **app access only** or **app + enterprise access**. Upon [authorizing](g://authorization/custom-app-approval) one of these applications in the Box Admin Console, a [Service Account](page://platform/user-types/#service-account) (`AutomationUser_xxxx_@boxdevedition.com`) representing the application is automatically generated. This account is an Admin-like user that can only be accessed via the API and can then be used to create user’s of the application called [App Users](page://platform/user-types/#app-user). If an application only needs to interact with the Service Account and App Users, **app only access** must be selected. If an application needs to interact with [managed users](page://platform/user-types/#managed-users) and their existing Box content, app + enterprise access must be selected. As an example, take a JWT application that has the read/write scopes, app only access, and is properly [authorized](g://authorization/custom-app-approval) in the Admin console. If a managed user obtains an Access Token and makes an API call to a folder they own, that call would receive a 400 error with the message “Cannot obtain token based on the enterprise configuration for your app”. Even though the user has access to the content, the correct scopes are enabled and the app is authorized, the selected application access only allows the application to interact with the Service Account and App Users. ## Enterprise settings and authorization There are a few enterprise settings to be aware of when it comes to the Box API. Platform applications fall into two categories: published and unpublished. Published applications are found in the [Box Integrations](https://app.box.com/services). Box Admins decide whether published and unpublished application are enabled by default and therefore can be used without approval. The status of these settings determines what actions are necessary to successfully [authorize](g://authorization/custom-app-approval) an application for use. Regardless of the settings above, in order for an application leveraging [JWT](g://authentication/jwt) or [Client Credentials Grant](g://authentication/client-credentials) to be used by an enterprise, an Admin must explicitly [authorize](g://authorization/custom-app-approval) it via the Box Admin console. The authorization is a snapshot in time. This means that if a developer revisits the Developer Console and changes the configuration, the Admin must re-authorize the application in order for generated Access Tokens to reflect the changes. If the setting **Disable unpublished apps by default** is turned on, an Admin must also explicitly [enable](g://authorization/custom-app-approval) any application leveraging [OAuth 2.0](g://authentication/oauth2) as the authentication method. Additionally, if this setting is turned on, Server Authenticated apps will also require enablement. **Reference:** https://developer.box.com/guides/security/ --- ## Untitled *Type: guide | Category: Security * Device Pinners Building on the login tracking feature – which allows admins to set limits on the number of devices a user can access Box… # Device Pinners Building on the login tracking feature – which allows admins to set limits on the number of devices a user can access Box from and sends alerts to them and the user whenever a new device is used to access that Box account – Box has additional device management functionality to increase security when accessing Box on mobile or desktop devices: device pinning. To learn more about device pinning, please see our community documentation. ## APIs The Box API allows for device pins to be inspected and removed. - [`GET /enterprise/:id/device_pinners`](e://get-enterprises-id-device-pinners): Retrieves all the device pins within an enterprise. - [`GET /device_pinners/:id`](e://get-device-pinners-id): Retrieves information about an individual device pin. - [`DELETE /device_pinners/:id`](e://delete-device-pinners-id): Deletes an individual device pin. **Reference:** https://developer.box.com/guides/security/device-pinners/ --- ## Untitled *Type: guide | Category: Shared Links * Create or Update Shared Link Shared links may be created or directly for file, folder, or web link resources to generate a read-only URL to… # Create or Update Shared Link Shared links may be created or directly for file, folder, or web link resources to generate a read-only URL to permit users with the appropriate access level to view the content. You may only have one active shared link for a file, folder, or web link at any time. At minimum the information needed to create a shared link will be: - The type of resource, either a file, folder, or web link. - The ID of that resource. Optionally when creating a shared link the following may be specified: The access level, which may be one of: - open: A public shared link. Anyone with the link may access the link. - company: Anyone within your enterprise may access the link. - collaborators: Anyone collaborated on the content may access the link. An expiration time when the shared link will automatically disable. A password required to access the resource. If an access level is not specified when creating a shared link it will use the default access level specified by the enterprise admin. ## Create or Update Shared Link for File To create a shared link on a file, specify the ID of the file with any optional shared link parameters. ## Create or Update Shared Link for Folder To create a shared link on a folder, specify the ID of the folder with any optional shared link parameters. ## Create or Update Shared Link for Web Link To create a shared link on a web link, specify the ID of the web link with any optional shared link parameters. **Reference:** https://developer.box.com/guides/shared-links/create-or-update/ --- ## Untitled *Type: guide | Category: Shared Links * Find Item from Shared Link The find item for shared link API is designed to accept a shared link as an input using a BoxApi header and… # Find Item from Shared Link The [find item for shared link](endpoint://get_shared_items) API is designed to accept a shared link as an input using a `BoxApi` header and return the file or folder object that the shared link is set for. To get the file or folder object associated with a shared link, supply the full shared link URL in the request. Please note that when the shared link is for a folder, the response of this API does not include the list of nested items within that folder. To further traverse the items in the folder, use the same `BoxApi` header to [get a nested folder's information](e://get-folders-id), list the items in those folders, get a nested file's information, or [download a file](e://get-files-id-content) **Reference:** https://developer.box.com/guides/shared-links/find-for-item/ --- ## Untitled *Type: guide | Category: Shared Links * Shared Links Shared links are URLs that are generated for files, folders, web links stored in Box, which provide direct, read-only access to… # Shared Links Shared links are URLs that are generated for files, folders, web links stored in Box, which provide direct, read-only access to the resource. Shared links which are set to an open access level allow anyone with the URL to access the item, while shared links with company or collaborators access levels may only be accessed by appropriate authenticated Box users. See [here](https://community.box.com/t5/Using-Shared-Links/Creating-Shared-Links/ta-p/19523) for all of the options and admin settings related to shared links. Users can access shared items by entering the shared link URL in a browser. Applications can also access shared items using the [Shared Item API](endpoint://get_shared_items). **Reference:** https://developer.box.com/guides/shared-links/ --- ## Untitled *Type: guide | Category: Shared Links * Shared Link Permissions The shared link resource has three permissions that can be updated using the permissions field: can_preview, can… # Shared Link Permissions The shared link resource has three permissions that can be updated using the `permissions` field: `can_preview`, `can_download`, `can_edit`. The `can_edit` option can only be `true` for files. Also, if the admin has restricted shared links from having edit access in the admin console, you will not be able to set `can_edit` to `true`. ``` curl -i -X PUT 'https://api.box.com/2.0/files/123456789?fields=shared_link' \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer [ACCESS TOKEN]' \ -d '{ "shared_link": { "permissions": { "can_preview": true, "can_download": true, "can_edit": true } } }' ``` **Reference:** https://developer.box.com/guides/shared-links/permissions/ --- ## Untitled *Type: guide | Category: Shared Links * Remove Shared Link A shared link may be removed from a resource by calling the update file or update folder or update weblink endpoint and… # Remove Shared Link A shared link may be removed from a resource by calling the [update file](endpoint://put_files_id) or [update folder](endpoint://put_folders_id) or [update weblink](endpoint://put_web_links_id) endpoint and setting the `shared_link` value to `null`. If you delete the shared link and create a new one, the new shared link will have a different URL and users with the old URL will not be able to access the resource. ## Remove Shared Link on File To remove a shared link on a file, specify the ID of file to set the `shared_link` field to `null`. ## Remove Shared Link on Folder To remove a shared link on a folder, specify the ID of folder to set the `shared_link` field to `null`. ## Remove Shared Link on Web Link To remove a shared link on a web link, specify the ID of web link to set the `shared_link` field to `null`. **Reference:** https://developer.box.com/guides/shared-links/remove/ --- ## Untitled *Type: guide | Category: Box Skills * Box Skills Box Skills are designed to allow custom processing of files uploaded to Box, with the intent of enhancing the underlying metadata… # Box Skills Box Skills are designed to allow custom processing of files uploaded to Box, with the intent of enhancing the underlying metadata of the file. The benefit towards this system is that it permits the storage of rich information about any files, which may then be used to automated tasks and future processes. The end-to-end process of a Skills application is as follows. 1. [Setup up an app](guide://skills/handle/setup) - Create a **Custom Skills** Box application that listens for files being uploaded within the entire enterprise or one or more folders. 2. [Configure the `invocation_url`](guide://skills/invocation-url) - After creating the **Custom Skills** app, an `invocation_url` will need to be configured. This URL will be called every time a new file is uploaded to Box. 3. [Parse the event payload](guide://skills/handle/payload) - When a file is uploaded, copied, or moved into a folder that the Box Skill listens to, an event payload is sent to the `invocation_url`. This payload contains two **Access Tokens** that can be used to access the uploaded file in Box and store metadata back onto the file. 4. [Verify Key Signatures](guide://webhooks/v2/signatures-v2) - Before the service that handles the Skill payload does other actions, it should verify the `invocation_url` was called by Box. See the link for examples of doing this manually or using the SDKs. 5. [Send the file for processing](https://github.com/box-community/Box-Custom-Skills-Starter) - The service that handles the Skill payload sends the file URL or file content to an external service for processing. This service can be a third party machine learning system, or an in-house service. 6. [Store the metadata on the file](guide://skills/handle/metadata) - Once the processing service has extracted the metadata for the file, those insights can be stored back on the uploaded file as custom metadata. To simplify your integration with Box Skills, a Skills Kit has been made available to obfuscate many complexities of the above steps. The Skills Kit is currently only available in Node. **Reference:** https://developer.box.com/guides/skills/ --- ## Untitled *Type: guide | Category: Box Skills * Invocation URL When creating a new Box Skills application you will be asked to supply an invocation_url. This URL is the public web address… # Invocation URL When creating a new [Box Skills application](guide://applications/app-types/custom-skills) you will be asked to supply an `invocation_url`. This URL is the public web address where Box will send the [event notification payload](guide://skills/handle/payload) when a file is uploaded, copied, or moved within a folder monitored by the Skills app. The website or application that is listening for those notifications functions as a bridge between the file on Box and the system that is being employed to garner insight from the file, such as the machine learning system. ## Requirements - The invocation URL should be publicly available. Notifications cannot be sent to `localhost` or `127.0.0.1` as this address is not accessible by Box's servers. - The server behind the invocation URL should be listening to HTTP `POST` requests. Box Skills will send the event notification via a `POST` request using a `JSON` body. ## Hosting Tips & Tricks There are a number of ways to quickly expose a an application on a public URL so that Box's servers can use this as the `invocation_url`. - **A local tunnel** - One of the quickest ways to expose a web application on a developer's machine to a public address is by using a local tunnel. Popular tunneling tools include [`ngrok`](https://ngrok.com) and [`localtunnel`](https://www.npmjs.com/package/localtunnel). - **Serverless functions** - A great way to set up a server that can handle a Box Skill is as a **serverless function**. Box Skills can generate a varied amount of invocations depending on the (lack of) activity in the folders being observed. Serverless functions such as [AWS Lambda](https://aws.amazon.com/lambda/), [Google Cloud Functions](https://cloud.google.com/functions/), or [Microsoft Azure Functions](https://azure.microsoft.com/en-us/services/functions/) are a natural fit for these kind of sporadic events. The serverless functions will only run and be billed when the event is being processed. - **Traditional application hosting** - Traditional application hosting solutions, such as [Heroku](https://www.heroku.com/), [Firebase](https://firebase.google.com/), [AWS](https://aws.amazon.com/) or [GCP](https://cloud.google.com/functions/) are other viable options if serverless technology is not preferred. These applications will be hosted on their respective services and have an publicly exposed URL for the app that will be used as the invocation URL. ## Application Server details Typically the application behind the invocation URL will need to perform the following tasks. 1. Capture the event notification from Box. 2. Send the binary data for the Box file (or its URL) to a processing service 3. Listen for the response from the processing service. 4. Format the response from the processing service into a Box metadata format. 5. Apply the new metadata back to the file stored on Box. **Reference:** https://developer.box.com/guides/skills/invocation-url/ --- ## Untitled *Type: guide | Category: SSO & App users * Connect SSO identities to app users Your SSO service will have a unique user record for each person using it within your company. When… # Connect SSO identities to app users Your SSO service will have a unique user record for each person using it within your company. When accessing a Box application through this SSO service, if we're creating a Box user for each SSO user, then we need to create an association between the SSO user and Box user records. When a user logs in to Box through the SSO service we will first search for the user by the association. If a Box user record is found we can begin making calls as that user to Box APIs. If there is no Box user found will then create a new Box user with the association to the unique SSO user account. Exploring the top level of a Box [user object](r://user) we can see the available options for adding the unique identifier from the SSO service user object into the Box user object to bind the two together. ``` { "address": "900 Jefferson Ave, Redwood City, CA 94063", "avatar_url": "https://www.box.com/api/avatar/large/181216415", "can_see_managed_users": true, "created_at": "2012-12-12T10:53:43-08:00", "enterprise": { .. }, "external_app_user_id": "my-user-1234", "hostname": "https://example.app.box.com/", "id": 11446498, "is_exempt_from_device_limits": true, "is_exempt_from_login_verification": true, "is_external_collab_restricted": true, "is_platform_access_only": true, "is_sync_enabled": true, "job_title": "CEO", "language": "en", "login": "ceo@example.com", "max_upload_size": 2147483648, "modified_at": "2012-12-12T10:53:43-08:00", "my_tags": [ .. ], "name": "Aaron Levie", "notification_email": { ... }, "phone": 6509241374, "role": "admin", "space_amount": 11345156112, "space_used": 1237009912, "status": "active", "timezone": "Africa/Bujumbura", "tracking_codes": [{ .. }], "type": "user" } ``` There are two recommended methods for creating a binding between a unique user within the SSO service and a Box user, placing the unique SSO user ID within the Box user `external_app_user_id` field, or using the unique SSO email address as the login email for the new user. ## Using external_app_user_id (recommended method) The `external_app_user_id` field was designed to hold a string identifier to associate a Box user record with an external service, such as an SSO provider user record. You can retrieve app users for a specific application only if such app users were created by this application. If you use one application to search for users created by a different one, no data will be returned. Using the `external_app_user_id` field for associating the unique SSO user account with a Box user account is the preferred method of connecting the two accounts over email, for a number of reasons: - Email association is only viable for [managed users](page://platform/user-types/#managed-users). [App users](page://platform/user-types/#app-user) are automatically assigned an email address by Box, meaning that you cannot assign the `login` to be the email from the SSO service. - Emails have to be unique in Box. This means that if your SSO service user signed up for Box using the same email address, which is not within your Box enterprise, then you will not be able to create a user with that email and won't be able to connect to that existing user. - The `external_app_user_id` field was designed for this purpose. ## Using login (alternative method) Using the `login` field of a user object to create an account association is viable under a few conditions: - Only the [managed users](page://platform/user-types/#managed-users) type is being used, not [app users](page://platform/user-types/#app-user). - All email addresses and Box account creation requests are managed by your enterprise, meaning that users cannot independently create Box accounts with those email addresses. Email addresses used for users in Box, under the `login` field, must be unique. Making a request to create a user with an email that already exists for another account will result in a `409 Conflict` error, stating that `user_login_already_used` **Reference:** https://developer.box.com/guides/sso-identities-and-app-users/connect-identities/ --- ## Untitled *Type: guide | Category: Box Skills * Box Skills Kit The Box Skills Kit is a Node wrapper that is designed to abstract many of the common complex operations required during the… # Box Skills Kit The Box Skills Kit is a Node wrapper that is designed to abstract many of the common complex operations required during the Box Skills development process. Visit the Skills Kit on GitHub **Reference:** https://developer.box.com/guides/skills/kit/ --- ## Untitled *Type: guide | Category: SSO & App users * Create connection between SSO identity and app user When a user signs in to a custom Box application for the first time using their SSO… # Create connection between SSO identity and app user When a user signs in to a custom Box application for the first time using their SSO provider credentials a new Box user will need to be created and associated with their SSO user record using some piece of unique information from that SSO user record. Typically the data that is used to make the association between those two accounts is either a unique ID or an email address. To make that association, a Box account may be created in a few ways: - Using the `external_app_user_id` field of a Box user to store the unique ID from the SSO provider. - Using the `login` field of a Box user to store the unique email from the SSO provider (managed users only). ## Create association with external_app_user_id Using the `external_app_user_id` field of a Box user record is a viable option for both app users and managed users, and is the recommended method when associating a user record from an SSO provider with a Box user account. ### App user To create a new Box app user with an `external_app_user_id` association to a SSO user record you will need two pieces of information from the SSO provider: - UID (required): The unique identifier from the SSO user record. - Name (optional): To maintain uniformity between the records, the SSO user name may be extracted to associate with the Box user record. Once available, make a request to create a new app user, supplying the optional `external_app_user_id` definition in the user parameters. ``` const ssoName = 'SSO User Name'; const ssoUID = 'SSO User Unique ID'; const spaceAmount = 1073741824; // Create app user client.enterprise.addAppUser( ssoName, { space_amount: spaceAmount, external_app_user_id: ssoUID } ).then(appUser => { console.log(`New user created: ${appUser.name}`); }); ``` ``` String ssoName = "SSO User Name"; String ssoUID = "SSO User Unique ID"; // Create app user CreateUserParams params = new CreateUserParams(); params.setExternalAppUserId(ssoUID); BoxUser.Info createdUserInfo = BoxUser.createAppUser(client, ssoName, params); outputString = "New user created: " + createdUserInfo.getName(); ``` ``` sso_name = 'SSO User Name' sso_uid = 'SSO User Unique ID' space = 1073741824 # Create app user user = box_client.create_user(sso_name, None, space_amount=space, external_app_user_id=sso_uid) print('New user created: {name}') ``` ### Managed user To create a new Box managed user with an `external_app_user_id` association to a SSO user record you will need two pieces of information from the SSO provider: - Email (required): The unique email from the SSO user record. - Name (optional): To maintain uniformity between the records, the SSO user may be extracted to associate with the Box user record. Once available, make a request to create a new managed user, supplying the SSO user record email address for the login. ``` const ssoName = 'SSO User Name'; const ssoEmail = 'ssouser@email.com'; const spaceAmount = 1073741824; // Create app user client.enterprise.addUser( ssoEmail, ssoName, { space_amount: spaceAmount } ).then(managedUser => { console.log(`New user created: ${managedUser.name}`); }); ``` ``` String ssoName = "SSO User Name"; String ssoEmail = "ssouser@email.com"; // Create managed user BoxUser.Info createdUserInfo = BoxUser.createEnterpriseUser(client, ssoEmail, ssoName); outputString = "New user created: " + createdUserInfo.getName(); ``` ``` sso_name = 'SSO User Name' sso_email = 'ssouser@email.com' space = 1073741824 # Create managed user user = box_client.create_user(sso_name, sso_email, space_amount=space) print('New user created: {name}') ``` ## Create association by email Creating a new [managed user](page://platform/user-types/#managed-users) that is associated by the SSO user email address is the same process as creating a standard managed user. After the user logs in via your SSO provider, if the user doesn't already exist as a Box user, extract the email address from the SSO user record and make a request to create a new Box managed user. **Reference:** https://developer.box.com/guides/sso-identities-and-app-users/create-app-user/ --- ## Untitled *Type: guide | Category: SSO & App users * Find app user for SSO identity When a user logs into a Box platform application with their SSO provider, the first step that should be taken… # Find app user for SSO identity When a user logs into a Box platform application with their SSO provider, the first step that should be taken is to see if that user already exists from a previous login attempt where a Box user record was already created. If a Box user is found you should [create a user access token](guide://authentication/jwt/user-access-tokens), or make [as user calls](guide://authentication/jwt/as-user), to access Box APIs as that user. If a Box user is not found you should create a new user with an association to the SSO user record. To search for existing users the [List Enterprise Users](e://get-users) endpoint may be used. Depending on whether you're using the `external_app_user_id` or `login` method your query will look slightly different. ## Find user by external_app_user_id To search for enterprise users by the stored `external_app_user_id` value you will need one piece of information from the SSO provider: - UID (required): The unique identifier from the SSO user record. Once available, make a request to the list enterprise users endpoint, supplying the `external_app_user_id` definition in the parameters. You can retrieve app users for a specific application only if such app users were created by this application. If you use one application to search for users created by a different one, no data will be returned. ``` const ssoUID = 'SSO User Unique ID'; // Check enterprise users for matching external_app_user_id against SSO UID client.enterprise.getUsers({ "external_app_user_id": ssoUID }) .then((users) => { if (users.total_count > 0) { // User found, fetch user ID const userId = users.entries[0].id; } else { // User not found - create new user record } }); ``` ``` String ssoUID = "SSO User Unique ID"; // Check enterprise users for matching external_app_user_id against SSO UID URL url = new URL("https://api.box.com/2.0/users?external_app_user_id=" + ssoUID); BoxAPIRequest request = new BoxAPIRequest(client, url, "GET"); BoxJSONResponse jsonResponse = (BoxJSONResponse) request.send(); JsonObject jsonObj = jsonResponse.getJsonObject(); JsonValue totalCount = jsonObj.get("total_count"); if (totalCount.asInt() > 0) { // User found, fetch // Fetch user ID JsonArray entries = (JsonArray) jsonObj.get("entries"); JsonObject userRecord = (JsonObject) entries.get(0); JsonValue userId = userRecord.get("id"); } else { // User not found - create new user record } ``` ``` sso_uid = 'SSO User Unique ID' # Validate is user exists url = f'https://api.box.com/2.0/users?external_app_user_id={sso_uid}' headers = {'Authorization': 'Bearer ' + access_token} response = requests.get(url, headers=headers) user_info = response.json() if (user_info['total_count'] == 0): # User not found - create new user record else: # User found, fetch user ID user = user_info['entries'][0] user_id = user['id'] ``` ## Find user by email address To search for enterprise users by their `login` email you will need one piece of information from the SSO provider: - Email (required): The unique email from the SSO user record. Once available, make a request to the list enterprise users endpoint, supplying the email address as the `filter_term`, which is made available to search by email or name. ``` const ssoEmail = 'ssouser@email.com'; client.enterprise.getUsers({filter_term: ssoEmail}) .then(users => { if (users.total_count > 0) { // User found, fetch user ID const userId = users.entries[0].id; } else { // User not found - create new user record } }); ``` ``` String ssoEmail = "ssouser@email.com"; Iterable<BoxUser.Info> users = BoxUser.getAllEnterpriseUsers(client, ssoEmail); ``` ``` sso_email = 'ssouser@email.com' users = client.users(user_type='all', filter_term=ssoEmail) if (users['total_count'] == 0): # User not found - create new user record else: # User found, fetch user ID user = users['entries'][0] user_id = user['id'] ``` **Reference:** https://developer.box.com/guides/sso-identities-and-app-users/find-app-user/ --- ## Untitled *Type: guide | Category: SSO & App users * SSO & App users Single Sign-On (SSO) services are often used as part of a company's Identity and Access Management (IAM) solution. When… # SSO & App users Single Sign-On (SSO) services are often used as part of a company's Identity and Access Management (IAM) solution. When deployed, these services grant users the ability to securely authenticate multiple applications by logging in once, with only one set of credentials (username and password). Box is one of the applications that can be connected to the SSO service of a company. Integrating these applications into your platform apps will allow you to provision Box users on the fly for any of your end users, without those users knowing they have a Box account. Popular SSO services include `Okta`, `Auth0`, `Microsoft Azure AD`, `OneLogin`, `G Suite`, and `Ping Identity`, but there are many others that may be deployed. ## Connecting SSO to apps When integrating SSO services into a Box application programmatically, we are referring to the following flow. 1. A user accesses your web or mobile application in a logged out state. 2. The user is redirected to your SSO provider to log in, typically via `OAuth 2` and `OpenID Connect`. 3. After login, the user is redirected back to your application along with their SSO identity credentials. 4. Your application does a check to see if an associated Box account already exists for this user. 5. If an existing Box account already exists for this user, the application uses the SSO identity to make API calls on behalf of that user in Box. 6. If there is not already an associated Box account for this user, a new Box user account is created based on the SSO identity. The unique user ID from the SSO service is then linked to the new Box user, creating an association between the two accounts. This new Box user is then API calls on behalf of that user in Box. # Box web app and SSO If you wish to connect your SSO service to the Box website instead of a Box application, Box provides a number of [integration options](https://support.box.com/hc/en-us/articles/360043696514-Setting-Up-Single-Sign-On-SSO-for-your-Enterprise) for supporting SSO integrations of the [Box web application](https://www.box.com) via SAML 2.0. **Reference:** https://developer.box.com/guides/sso-identities-and-app-users/ --- ## Untitled *Type: guide | Category: Tasks * Create a task To create a task, you will need to provide the POST /tasks API with the action for the task, as well as an item to represent… # Create a task To create a task, you will need to provide the [`POST /tasks`](e://post_tasks) API with the `action` for the task, as well as an `item` to represent the file to add the task to. ## Task actions Box currently supports two types of tasks defined by the `action` value: `review` tasks and `complete` tasks. The type of task determines the possible resolution states a task can be in and the interface shown to a user in the web and mobile apps. | Task action | Possible resolution states | | --- | --- | | review | incomplete, approved, rejected | | complete | incomplete, complete | A `review` task starts out in an `incomplete` state and can be marked as `incomplete`, `approved`, or `rejected`. In the user interface a user is provided with a text box and an pair of buttons to approve or reject the task. A `complete` task starts out in an `incomplete` state and can be marked `incomplete` or `completed`. Once a this task is marked completed, no further changes can be made to the task's state. In the user interface a user is provided with a text box and an button to mark the task as completed. ## Completion rules A task on a file can be assigned to more than one collaborator on the file, and a task has a `completion_rule` that can be used to define if all users who've been assigned the task (`all_assignees`) or only one assignee (`any_assignee`) need to complete the task. **Reference:** https://developer.box.com/guides/tasks/create/ --- ## Untitled *Type: guide | Category: Tasks * Delete a task To remove a task, call the DELETE /tasks/:task_id API with the id of the task. # Delete a task To remove a task, call the [`DELETE /tasks/:task_id`](e://delete_tasks_id) API with the `id` of the task. **Reference:** https://developer.box.com/guides/tasks/delete/ --- ## Untitled *Type: guide | Category: Tasks * Lists all tasks for a file To list all of the tasks for a specific file, call the GET /files/:id/tasks with the id of the file. # Lists all tasks for a file To list all of the tasks for a specific file, call the [`GET /files/:id/tasks`](e://get_files_id_tasks) with the `id` of the file. **Reference:** https://developer.box.com/guides/tasks/for-file/ --- ## Untitled *Type: guide | Category: Tasks * Get information about a task To get information about an assigned task, call the GET /tasks/:task_id API with the id of the task. # Get information about a task To get information about an assigned task, call the [`GET /tasks/:task_id`](e://get_tasks_id) API with the `id` of the task. **Reference:** https://developer.box.com/guides/tasks/get/ --- ## Untitled *Type: guide | Category: Tasks * Make changes to an existing task To update a task in Box you will need to call the PUT /tasks/:task_id API with the ID of the task. This API… # Make changes to an existing task To update a task in Box you will need to call the [`PUT /tasks/:task_id`](e://put_tasks_id) API with the ID of the task. This API can be used to change the `action` type of the task, add a `message`, or change the due date. ## Task actions Box currently supports two types of tasks defined by the `action` value: `review` tasks and `complete` tasks. The type of task determines the possible resolution states a task can be in and the interface shown to a user in the web and mobile apps. | Task action | Possible resolution states | | --- | --- | | review | incomplete, approved, rejected | | complete | incomplete, complete | A `review` task starts out in an `incomplete` state and can be marked as `incomplete`, `approved`, or `rejected`. In the user interface a user is provided with a text box and an pair of buttons to approve or reject the task. A `complete` task starts out in an `incomplete` state and can be marked `incomplete` or `completed`. Once a this task is marked completed, no further changes can be made to the task's state. In the user interface a user is provided with a text box and an button to mark the task as completed. ## Completion rules A task on a file can be assigned to more than one collaborator on the file, and a task has a `completion_rule` that can be used to define if all users who've been assigned the task (`all_assignees`) or only one assignee (`any_assignee`) need to complete the task. **Reference:** https://developer.box.com/guides/tasks/update/ --- ## Untitled *Type: guide | Category: Tasks * Tasks Tasks allow users to request collaborators on a file to review a file or complete a piece of work. Tasks can be used by developers to… # Tasks Tasks allow users to request collaborators on a file to review a file or complete a piece of work. Tasks can be used by developers to create file-centric workflows. Learn more about tasks from the Adding Comments and Tasks article on our support site. ## Task actions Box currently supports two types of tasks defined by the `action` value: `review` tasks and `complete` tasks. The type of task determines the possible resolution states a task can be in and the interface shown to a user in the web and mobile apps. | Task action | Possible resolution states | | --- | --- | | review | incomplete, approved, rejected | | complete | incomplete, complete | A `review` task starts out in an `incomplete` state and can be marked as `incomplete`, `approved`, or `rejected`. In the user interface a user is provided with a text box and an pair of buttons to approve or reject the task. A `complete` task starts out in an `incomplete` state and can be marked `incomplete` or `completed`. Once a this task is marked completed, no further changes can be made to the task's state. In the user interface a user is provided with a text box and an button to mark the task as completed. ## Completion rules A task on a file can be assigned to more than one collaborator on the file, and a task has a `completion_rule` that can be used to define if all users who've been assigned the task (`all_assignees`) or only one assignee (`any_assignee`) need to complete the task. **Reference:** https://developer.box.com/guides/tasks/ --- ## Untitled *Type: guide | Category: Tooling * Tooling Box offers several options for using our APIs, including the Box CLI, Postman, Salesforce Developer Toolkit, and various SDKs. # Tooling Box offers several options for using our APIs, including the [Box CLI](g://cli), [Postman](g://tooling/postman), [Salesforce Developer Toolkit](g://tooling/salesforce-toolkit), and [various SDKs](g://tooling/sdks). **Reference:** https://developer.box.com/guides/tooling/ --- ## Untitled *Type: guide | Category: Trash * Trash Before items are deleted they might end up in a user's trash. The trash can be managed by a user through any of the Box apps, and by… # Trash Before items are deleted they might end up in a user's trash. The trash can be managed by a user through any of the Box apps, and by an application via the API. ## Two-stage Deletion Process Box uses a two-stage process to remove or **trash** [files](e://delete_files_id), [folders](e://delete_folders_id), and [web links](e://delete_web_links_id) before permanently deleting them. By default, items can be restored after they are **trashed** within a 30 day time frame before they are **purged**. The purge window can be modified by an enterprise administrator. More details can be found in the Box community article on [Managing Trash](https://support.box.com/hc/en-us/articles/360044196093-Manage-Trash) **Reference:** https://developer.box.com/guides/trash/ --- ## Untitled *Type: guide | Category: Trash * Permanently Delete File Once a file has been moved to the trash, it will stay in the trash for 30 days by default before being purged… # Permanently Delete File Once a file has been moved to the trash, it will stay in the trash for 30 days by default before being purged. Administrators of Business or Enterprise accounts can alter the purge window. If you wish to permanently delete the file from the trash before the purge window expires, make a `DELETE` request to `/files/:file_id/trash` using the trashed file's `ID`. **Reference:** https://developer.box.com/guides/trash/permanently-delete-file/ --- ## Untitled *Type: guide | Category: Trash * Permanently Delete Web Link Once a web link has been moved to the trash, it will stay in the trash for 30 days by default before being… # Permanently Delete Web Link Once a web link has been moved to the trash, it will stay in the trash for 30 days by default before being purged. Administrators of Business or Enterprise accounts can alter the purge window. If you wish to permanently delete the web link from the trash before the purge window expires, make a `DELETE` request to `/web_links/:web_link_id/trash` using the trashed web link's `ID`. **Reference:** https://developer.box.com/guides/trash/permanently-delete-web-link/ --- ## Untitled *Type: guide | Category: Trash * Permanently Delete Folder Once a folder has been moved to the trash, it will stay in the trash for 30 days by default before being purged… # Permanently Delete Folder Once a folder has been moved to the trash, it will stay in the trash for 30 days by default before being purged. Administrators of Business or Enterprise accounts can alter the purge window. If you wish to permanently delete the folder from the trash before the purge window expires, make a `DELETE` request to `/folders/:folder_id/trash` using the trashed folder's `ID` . **Reference:** https://developer.box.com/guides/trash/permanently-delete-folder/ --- ## Untitled *Type: guide | Category: Trash * Restore File To restore a file that has been moved to the trash, but has not yet been purged, make a POST request to the /files/:file_id… # Restore File To restore a file that has been moved to the trash, but has not yet been purged, make a `POST` request to the `/files/:file_id` endpoint. This will place the file in the original folder if it is still available, or you optionally can specify a `parent` folder. **Reference:** https://developer.box.com/guides/trash/restore-file/ --- ## Untitled *Type: guide | Category: Trash * Restore Folder To restore a folder that has been moved to the trash, but has not yet been purged, make a POST request to the /folders… # Restore Folder To restore a folder that has been moved to the trash, but has not yet been purged, make a `POST` request to the `/folders/:folder_id` endpoint. This will place the folder in the original parent folder if it is still available, or you optionally can specify a `parent` folder. During a folder restoration operation, part of the file tree will be locked, such as the source folder for the request and all of its descendants, as well as the destination folder. During the restoration of the folder, no other move, copy, delete, or restore operation can be performed on the locked folders. **Reference:** https://developer.box.com/guides/trash/restore-folder/ --- ## Untitled *Type: guide | Category: Trash * Restore Web Link To restore a web link that has been moved to the trash, but has not yet been purged, make a POST request to the /web_links… # Restore Web Link To restore a web link that has been moved to the trash, but has not yet been purged, make a `POST` request to the `/web_links/:web_link_id` endpoint. This will place the web link in the original parent folder if it is still available, you optionally can specify a `parent` folder. **Reference:** https://developer.box.com/guides/trash/restore-web-link/ --- ## Untitled *Type: guide | Category: Uploads * Preflight Check The Pre-flight check API allows an application to verify that a file will be accepted by Box before it uploads any bytes. It… # Preflight Check The Pre-flight check API allows an application to verify that a file will be accepted by Box before it uploads any bytes. It can both be used for new files, as well as uploading new versions of existing files. ## Checklist Preflight checks perform all the same checks as if the file was actually uploaded including: - The permission of the application and the user to upload to the folder - Any file name collisions - Any file size caps and limits - Any folder and file name restrictions - Any folder and account storage quotas ## Check for new file To perform a check for a new file, call the [`OPTIONS /files/content`](e://options_files_content) API with the same parameters (except for the binary content) as if uploading an actual file. ## Check for new file version To perform a check for a new version of a file, call the [`OPTIONS /files/:id/content`](e://options_files_content) API with the same parameters (except for the binary content) as if uploading an actual file. ## Checks & Chunk Uploads When performing a [chunked upload](g://uploads/chunked), performing a preflight check is not required as [creating an Upload Session](g://uploads/chunked/create-session) also performs a preflight check. ## Response codes When the API call detects any problems, a HTTP `409 Conflict` status code is returned with a message to describe the possible conflict. If no problems were discovered, it returns a HTTP `200 OK` status code and the upload can proceed. A `200 OK` response does not guarantee that the upload call will actually succeed. Pre-flight checks have show to reduce failed uploads by over 99%, yet concurrency issues still come into play when uploading a file. Highly active folders, common filenames, and accounts near their quota limits may get a `200 OK` for the preflight check, and then have a real conflict during the actual upload. ## Response body In many cases, the preflight check will return valuable data in the API response when a conflict has been detected. For example, when a name collision has been detected, the application can use the `SHA-1` that is returned in the error response to check if the existing file is identical to the one it is trying to upload. **Reference:** https://developer.box.com/guides/uploads/check/ --- ## Untitled *Type: guide | Category: Uploads * Uploads The Box API supports two distinct methods of file upload. The direct file upload API supports files up to 50MB in size and sends all… # Uploads The Box API supports two distinct methods of file upload. The direct file upload API supports files up to 50MB in size and sends all the binary data to the Box API in 1 API request. The [chunked upload APIs](g://uploads/chunked) support files from 20MB in size and allow an application to upload the file in parts, allowing for more control to catch any errors and retry parts individually. To upload files to the Archive folder, you need to first enable the Global Content Manager (GCM) scope in the Developer Console. ## Upload limits Upload limits are dictated by the type of account of the authenticated user. More information can be found [in our community article on this topic](https://support.box.com/hc/en-us/articles/360043697314-Understand-the-Maximum-File-Size-You-Can-Upload-to-Box). ## Preflight check The Pre-flight check API allows an application to verify that a file will be accepted by Box before it uploads any bytes. It can both be used for new files, as well as uploading new versions of existing files. Learn more about pre-flight checks ## Upload domain Uploads to Box happen via a different domain (`upload.box.com`) than regular API calls. This is something to keep in mind when writing your own upload code. All the Box SDKs will take care of choosing the right domain for the right API call. **Reference:** https://developer.box.com/guides/uploads/ --- ## Untitled *Type: guide | Category: Users * Create App User App Users are programmatic user accounts that may only be created by apps that are using JWT authentication. They are… # Create App User App Users are programmatic user accounts that may only be created by apps that are using [JWT authentication](g://authentication/jwt/jwt-setup). They are designed to allow for users, groups, or processes to be represented behind the scenes in an application without the need for the user to have a Box account to log in. App users can only be accessed by the Box application through the APIs and do not have credentials to log in to `box.com` directly. ## Common App User Patterns Typically app users are created for a number of different patterns: - To represent a single application user or group of users without a `box.com` account. - To represent an application process, such as having the app user monitor all events within an enterprise. - To provide the application with the ability to completely control the file and folder structure of a user account without the possibility of that content being modified through the `box.com` web app. ## Creating a New App User To generate a new app user, the minimal information that will be required will be a name for the app user. To see all available optional parameters that may be set when creating an app user, see the [create user endpoint](endpoint://post-users). Before you can make any changes to the newly created account, you need to click the link you receive in the confirmation email. Once the app user is created a user object will be returned. Within the user object is an ID for the app user, which may be used to make API requests to modify the user in the future. **Reference:** https://developer.box.com/guides/users/create-app-user/ --- ## Untitled *Type: guide | Category: Users * Create Managed User To generate a new managed user, the minimal information that will be required will be a name and an email address for… # Create Managed User To generate a new managed user, the minimal information that will be required will be a name and an email address for the managed user. The email address supplied when creating a managed user must be unique. It cannot already be associated with an existing Box user. To see all available optional parameters that may be set when creating an app user, see the [create user endpoint](endpoint://post-users). Before you can make any changes to the newly created account, you need to click the link you receive in the confirmation email. A user object will be returned from the create user request. Within the user object is an ID for the managed user, which may be used to make API requests to modify the user in the future. Once a new managed user is created the email address used will receive an email from Box asking them to create a password for the account. The account will be in a `pending` state until that action has taken place. For security reasons passwords cannot be supplied when creating a new managed user **Reference:** https://developer.box.com/guides/users/create-managed-user/ --- ## Untitled *Type: guide | Category: Users * Delete User The process for deleting both app and managed users is the same. To delete a user account, supply the user ID for the account… # Delete User The process for deleting both app and managed users is the same. To delete a user account, supply the user ID for the account that should be removed. There are also two optional parameters that may be set when deleting a user account: - force: Whether the user should be deleted even if the account still has content in it. - notify: Whether the user will receive a notification that the account was deleted. The delete user request will fail if the user account still has content in it. To resolve this, either [transfer all files or folders](g://users/deprovision/transfer-folders) to another account or use the optional `force` parameter. **Reference:** https://developer.box.com/guides/users/delete-user/ --- ## Untitled *Type: guide | Category: Users * Users The Box API supports a variety of users, ranging from real employees logging in with their Managed User account, to applications using… # Users The Box API supports a variety of users, ranging from real employees logging in with their Managed User account, to applications using App Users to drive powerful automation workflows. Learn more about user types ## (De-)Provision Users Managing the onboarding and offboarding of employees, customers, and users is a common requirement in the lifespan of a Box application. During account provisioning the main tasks that will be needed will be: - How to create new app and managed user accounts to represent the users. - How to instantiate the new user account with common or repeatable folder and file architectures. During account deprovisioning the main tasks that will be needed will be: - How to transfer files and folders from one account to another for offboarding. - How to delete user accounts. **Reference:** https://developer.box.com/guides/users/ --- ## Untitled *Type: guide | Category: Web Links * Create Web Link To create a web link in Box, you will need to provide our API with a folder id and the url you want the web link to be… # Create Web Link To create a web link in Box, you will need to provide our API with a folder `id` and the `url` you want the web link to be linked to. The `url` must start with `http://` or `https://`. **Reference:** https://developer.box.com/guides/web-links/create/ --- ## Untitled *Type: guide | Category: Web Links * Web Links Web links are objects that point to URLs. These objects are also known as bookmarks within the Box web application. Web Links are… # Web Links Web links are objects that point to URLs. These objects are also known as bookmarks within the Box web application. Web Links are treated similarly to file objects, which means that they have the same support for creating shared links, allow for copying, permanent deletion, and restoration. **Reference:** https://developer.box.com/guides/web-links/ --- ## Untitled *Type: guide | Category: Web Links * Delete Web Link To remove a web link in Box you will need to provide our API with the ID of the web link. # Delete Web Link To remove a web link in Box you will need to provide our API with the ID of the web link. **Reference:** https://developer.box.com/guides/web-links/delete/ --- ## Untitled *Type: guide | Category: Webhooks * Webhooks Webhooks allow you to monitor Box content for events, and receive notifications to a URL of your choice when they occur. For… # Webhooks Webhooks allow you to monitor Box content for events, and receive notifications to a URL of your choice when they occur. For example, a workflow may include waiting for a file to be downloaded to delete a shared link. A webhook can be set on the file and upon notification of the download event, a script can launch to make an API call to delete the shared link. ## Versions There are two types of webhooks: V1 and V2, which are compared below. For the ease of use, better security, more event triggers to choose from, and automatic retries we recommend to use V2 webhooks. | V1 | V2 | | --- | --- | | Created in the Developer Console. | Created in the Developer Console or with an API call. | | Set at the root level. | Set on specific files/folders, but cannot set at the root. | | Select from 14 event triggers. | Select from 30+ event triggers. | | Provides selected callback parameters. | Payload includes full object response & additional context info. | | No retry mechanism after notification delivery failure. | Retries up to 10 times after notification delivery failure. | | Does not support payload verification. | Supports payload verification. | | Notification URL can be HTTP or HTTPS. | Notification URL must be HTTPS. | | Does not scale well. | Scales well and has increased reliability. | **Reference:** https://developer.box.com/guides/webhooks/ --- ## Untitled *Type: guide | Category: Webhooks * Webhook Event Triggers V2 Files and Folders The following is a list of events that can be configured to trigger a V2 webhook. Some events… # Webhook Event Triggers ## V2 ### Files and Folders The following is a list of events that can be configured to trigger a V2 webhook. Some events are only available for files, while others are only available for folders. | Event | Triggered | File? | Folder? | | --- | --- | --- | --- | | COLLABORATION.CREATED | A collaboration is created. | No | Yes | | COLLABORATION.ACCEPTED | A collaboration is accepted. | No | Yes | | COLLABORATION.REJECTED | A collaboration is rejected. | No | Yes | | COLLABORATION.REMOVED | A collaboration is removed. | No | Yes | | COLLABORATION.UPDATED | A collaboration is updated. | No | Yes | | COMMENT.CREATED | A comment object is created. | Yes | Yes | | COMMENT.UPDATED | A comment object is edited. | Yes | Yes | | COMMENT.DELETED | A comment object is removed. | Yes | Yes | | DOCGEN_DOCUMENT_GENERATION_FAILED | Doc Gen failed to generate a document. | Yes | No | | DOCGEN_DOCUMENT_GENERATION_STARTED | Doc Gen started to create a document. | Yes | No | | DOCGEN_DOCUMENT_GENERATION_SUCCEEDED | Doc Gen succeeded to create a document. | Yes | No | | FILE.UPLOADED | A file is uploaded or moved to this folder. | No | Yes | | FILE.PREVIEWED | A file is previewed. | Yes | Yes | | FILE.DOWNLOADED | A file is downloaded. | Yes | Yes | | FILE.TRASHED | A file is moved to trash. | Yes | Yes | | FILE.DELETED | A file is permanently deleted. | Yes | Yes | | FILE.RESTORED | A file is restored from trash. | Yes | Yes | | FILE.COPIED | A file is copied. | Yes | Yes | | FILE.MOVED | A file is moved from one folder to another. | Yes | Yes | | FILE.LOCKED | A file is locked. | Yes | Yes | | FILE.UNLOCKED | A file is unlocked. | Yes | Yes | | FILE.RENAMED | A file is renamed. | Yes | Yes | | FOLDER.CREATED | A folder is created | No | Yes | | FOLDER.RENAMED | A folder is renamed. | No | Yes | | FOLDER.DOWNLOADED | A folder is downloaded. | No | Yes | | FOLDER.RESTORED | A folder is restored from trash. | No | Yes | | FOLDER.DELETED | A folder is permanently removed. | No | Yes | | FOLDER.COPIED | A folder is copied. | No | Yes | | FOLDER.MOVED | A folder is moved to a different folder. | No | Yes | | FOLDER.TRASHED | A folder is moved to trash. | No | Yes | | METADATA_INSTANCE.CREATED | A new metadata template instance is associated with a file or folder. | Yes | Yes | | METADATA_INSTANCE.UPDATED | An attribute (value) is updated/deleted for an existing metadata template instance associated with a file or folder. | Yes | Yes | | METADATA_INSTANCE.DELETED | An existing metadata template instance associated with a file or folder is deleted. | Yes | Yes | | SHARED_LINK.DELETED | A shared link is deleted. | Yes | Yes | | SHARED_LINK.CREATED | A shared link is created. | Yes | Yes | | SHARED_LINK.UPDATED | A shared link is updated. | Yes | Yes | | TASK_ASSIGNMENT.CREATED | A task is created. | Yes | Yes | | TASK_ASSIGNMENT.UPDATED | A task assignment is changed. | Yes | Yes | | SIGN_REQUEST.COMPLETED | A sign request is completed. | Yes | Yes | | SIGN_REQUEST.DECLINED | A sign request is declined. | Yes | Yes | | SIGN_REQUEST.EXPIRED | A sign request is expired. | Yes | Yes | | SIGN_REQUEST.SIGNER_EMAIL_BOUNCED | A signer's email is bounced. | Yes | Yes | | WEBHOOK.DELETED | A webhook is deleted. | No | No | ## V1 Events that can be configured to trigger a v1 webhook: - Sent, - Created, - Uploaded, - Commented, - Downloaded, - Previewed, - Moved, - Copied, - Task assigned, - Responded to task, - Locked, - Unlocked, - Deleted, - Collaborator added. **Reference:** https://developer.box.com/guides/webhooks/triggers/ --- ## Untitled *Type: guide | Category: API Calls * Pagination The Box API supports two ways to paginate collections. The most common way to paginate is through offset-based pagination which… # Pagination The Box API supports two ways to paginate collections. The most common way to paginate is through offset-based pagination which is often used where the list of items is of a fixed, predetermined length. In some cases an API endpoint supports marker-based pagination, either as an alternative to offset-based pagination or as a full replacement. Marker-based pagination is often used in cases where the length of the total set of items is either changing frequently, or where the total length might not be known upfront. **Reference:** https://developer.box.com/guides/api-calls/pagination/ --- ## Untitled *Type: guide | Category: API Calls * Marker-based Pagination APIs that use marker-based paging use the marker and limit query parameters to paginate through items in a… # Marker-based Pagination APIs that use marker-based paging use the `marker` and `limit` query parameters to paginate through items in a collection. Marker-based pagination is often used in cases where the length of the total set of items is either changing frequently, or where the total length might not be known upfront. ## Paging To fetch the first page of entries in a collection the API needs to be called either without the `marker` parameter, or with the `marker` set to `0`. The `limit` parameter is optional. ``` curl https://api.box.com/2.0/folders/0/items?limit=100&usemarker=true \ -H "authorization: Bearer ACCESS_TOKEN" ``` APIs that support both offset-based pagination and marker-based pagination require the `usemarker` query parameter to be set to `true` to ensure marker-based pagination is used. To fetch the next page of entries the API needs to be called with an `marker` parameter that equals value of the `next_marker` value that was received in the API response. ``` curl https://api.box.com/2.0/folders/0/items?marker=34332423&limit=100&usemarker=true \ -H "authorization: Bearer ACCESS_TOKEN" ``` The final page of items has been requested when the next `next_marker` value is `null` in the response object. At this point there are no more items to fetch. With marker-based paging there is no way to determine the total number of entries in a collection except by fetching them all. Applications should not retain the `next_marker` value long-term as the internal implementation of the markers may change over time. ## Marker & Limit The following query parameters are used to paginate a collection. | Query parameter | Type | Default | | | --- | --- | --- | --- | | marker | String | | The first position in the collection to return results from. This should be a value that was returned in a previous request. | | limit | Integer | Depends on API | The maximum number of entries to return. If the value exceeds the maximum, then the maximum value will be used. | | usemarker | Boolean | | An optional query parameter that can be used with API endpoints that support both types of pagination to select pagination type. Set to true to enforce marker-based pagination. | ## Collections When paginating collections, the API returns an object that contains the set of results as an array, as well as some information about the current page of results. | Field | Type | | | --- | --- | --- | | entries | Array | The page of items for this page. This will be an empty array if there are no results. | | next_marker | String | The value that can be used as the marker value to fetch the next page of results. If this value is null or an empty string there are no more results to fetch. | | limit | Integer | The limit used for this page of results. This will be the same as the limit query parameter unless it exceeded the maximum value allowed for this API endpoint. | ## Example endpoints Some endpoints that support marker-based pagination are: - [List items for a folder](e://get_folders_id_items) - [List a file's collaborations](e://get-files-id-collaborations) - [List all webhooks for a user](e://get-webhooks) - [List all users in an enterprise](e://get-users) - [List all items in the trash](e://get-folders-trash-items) **Reference:** https://developer.box.com/guides/api-calls/pagination/marker-based/ --- ## Untitled *Type: guide | Category: API Calls * Offset-based Pagination APIs that use offset-based paging use the offset and limit query parameters to paginate through items in a… # Offset-based Pagination APIs that use offset-based paging use the `offset` and `limit` query parameters to paginate through items in a collection. Offset-based pagination is often used where the list of items is of a fixed and predetermined length. ## Paging To fetch the first page of entries in a collection the API needs to be called either without the `offset` parameter, or with the `offset` set to `0`. The `limit` field is optional. ``` curl https://api.box.com/2.0/folders/0/items?offset=0&limit=100 \ -H "authorization: Bearer ACCESS_TOKEN" ``` To fetch the next page of entries the API needs to be called with an `offset` parameter that equals the sum of the previous `offset` value and limit returned in the previous result, `previous_offset + previous_limit`. ``` curl https://api.box.com/2.0/folders/0/items?offset=100&limit=100 \ -H "authorization: Bearer ACCESS_TOKEN" ``` Note that the `offset` should be increased by the previous `limit` and not by the size of the entries in the response array, as this may be less than the limit. Generally we advise using the value of the `limit` in the response object to increase the `offset` value. The final page of items has been requested when the next `offset` value exceeds the `total_count` value in the response object. At this point there are no more items to fetch. ## Offset & Limit The following query parameters are used to paginate a collection. | Query parameter | Type | Default | | | --- | --- | --- | --- | | offset | Integer | 0 | The (zero-based) offset of the first item returned in the collection. In a zero-based offset 0 is a correct value. | | limit | Integer | Depends on API | The maximum number of entries to return. If the value exceeds the maximum, then the maximum value will be used. | The maximum `offset` for offset-based pagination is `9999`. Marker-based pagination is recommended when a higher offset is needed. ## Collections When paginating collections, the API returns an object that contains the set of results as an array, as well as some information about the current page of results. | Field | Type | | | --- | --- | --- | | entries | Array | The page of items for this page. This will be an empty array if there are no results. | | offset | Integer | The offset used for this page of results | | limit | Integer | The limit used for this page of results. This will be the same as the limit query parameter unless it exceeded the maximum value allowed for this API endpoint. | | total_count | Integer | One greater than the offset of the last item in the entire collection. The total number of items in the collection may be less than total_count. | ## Example endpoints Some endpoints that support offset-based pagination are: - [List items for a folder](endpoint://get_folders_id_items) - [List a file's comments](endpoint://get-files-id-comments) - [List all items in the trash](endpoint://get-folders-trash-items) **Reference:** https://developer.box.com/guides/api-calls/pagination/offset-based/ --- ## Untitled *Type: guide | Category: API Calls * App Diagnostics Report The App Diagnostics Report provides a spreadsheet detailing each API call made by a given application during a… # App Diagnostics Report The App Diagnostics Report provides a spreadsheet detailing each API call made by a given application during a specified timeframe. The report gives you access to the `API Request ID`, which can be given to Box Support for troubleshooting purposes. This report will not include: - API calls made within the last 48 hours - Authorization and Token API calls We are working on including the above and thereby making the report suitable for real-time troubleshooting in the future. ## 1. Click on the App Diagnostics Tab To run the App Diagnostics Report, click the **App Diagnostics** option located along the top of your application's configuration section in the [Developer Console](https://app.box.com/developers/console). ## 2. Click Run App Diagnostics Report to configure your report Clicking **Run App Diagnostics Report** will display a popup to select report filters and parameters. ## 3. Select Report Parameters and Click Run You can select a date in the last two weeks, starting 48 hours prior to today. A report can be run for up to a total of 24 hours. Click **Run** to generate a report. ## 4. Access the Box Reports Folder Navigate to [All Files](https://app.box.com/folder/0) and locate the Box Reports folder. If this is your first time running a report, this folder is automatically generated. If you are an Admin, this is the same folder you access [Admin Console Reports](https://support.box.com/hc/en-us/articles/360043696534-Running-Reports). Open the folder. ## 5. Find and Open the latest Platform App Diagnostics Report Folder You will see any reports you have run in the Box Reports folder. Locate and open the most recent Platform App Diagnostics run folder. ## 6. Check the Status The report will take time to complete. You can see the status in the box at the top of the folder. ## 7. Report Completed Upon completion, the status will change, and the `.csv` file will appear in the folder. ## 8. Open the Report You can open the file in your web browser or download it to view locally. If you need help from [Box Support](https://support.box.com/hc/en-us/requests/new), you can send in the report with your ticket by attaching a downloaded copy or via shared link. The report has nine columns. | Column | Description | | | --- | --- | --- | | App Name | The name of the application | | | Client ID | The Client ID of the application | | | HTTP Status Code | The response code returned from Box | | | API Request Duration (ms) | The amount of time from API call to response | | | API Request ID | An unique identifier that can be used by Box Support | | | Resource | The primary resource accessed in the API call | | | Sub Resource | The secondary resource accessed in the API call | | | HTTP Method | The type of HTTP method used in the API call | | | API Request Timestamp | The timestamp of the API call | | **Reference:** https://developer.box.com/guides/api-calls/permissions-and-errors/app-diagnostics-report/ --- ## Untitled *Type: guide | Category: API Calls * Token & URL Expiration Across the Box API there are a few tokens, codes, and URLs that automatically expire. The following is a quick… # Token & URL Expiration Across the Box API there are a few tokens, codes, and URLs that automatically expire. The following is a quick overview of their respective expiration times. | | | | --- | --- | | Authorization Codes | Expires after 30 seconds | | Access Tokens | expires after 60 minutes | | Refresh Tokens | Expires after 60 days or 1 use | | Download URLs | Expires after 15 minutes | See each respective guide for more details. **Reference:** https://developer.box.com/guides/api-calls/permissions-and-errors/expiration/ --- ## Untitled *Type: guide | Category: API Calls * Errors The Box APIs uses HTTP status codes to communicate if a request has been successfully processed or not. Client error Most client… # Errors The Box APIs uses [HTTP status codes](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html) to communicate if a request has been successfully processed or not. ## Client error Most client errors in the HTTP 4XX, and some server errors in the HTTP 5XX range returns a standard client error JSON object. ``` { "type": "error", "status": 400, "code": "bad_digest", "help_url": "https://developer.box.com/guides/api-calls/permissions-and-errors/common-errors/", "message": "The specified content-md5 did not match what we received", "request_id": "abcdef123456" } ``` See the [Client Error resource](resource://client_error) for more details. ## Common error codes Check our [Developer Troubleshooting Articles](https://support.box.com/hc/en-us/sections/360007552913-Troubleshooting-Box-Platform) for solution to common errors encountered when working with the Box APIs. ### 400 Bad Request | | | | --- | --- | | Error | bad_digest | | Message | The specified content-md5 did not match what we received. | | Solution | While uploading a file, a content-md5 header with the SHA-1 hash of the file can be supplied to ensure that the file is not corrupted in transit. The SHA-1 hash that was supplied in the request did not match what was received in the upload. Supply a valid SHA-1 hash of the uploaded file. | | | | | Error | bad_request | | Message | | | Solution | Required parameters supplied in the API request are either missing or invalid. Check the extended error message in the response body for more details. | | | | | Error | cannot_make_collaborated_subfolder_private | | Message | Cannot move a collaborated subfolder to a private folder unless the new owner is explicitly specified. | | Solution | Specify the ID of the user that the content should be transferred to by setting the owned_by.id field in the request. | | | | | Error | collaborations_not_available_on_root_folder | | Message | Root folder cannot be collaborated | | Solution | You cannot set collaborators on a user's root folder (folder ID 0). Use a different folder ID than the root folder. | | | | | Error | cyclical_folder_structure | | Message | Folder move creates cyclical folder structure | | Solution | The folder ID specified in the folder move would create a cyclical folder structure (for example moving a folder to a subfolder within itself). Change the folder ID for the folder move request. | | | | | Error | folder_not_empty | | Message | Cannot delete – folder not empty | | Solution | Delete all content from the folder before attempting to delete it. | | | | | Error | invalid_collaboration_item | | Message | Item type must be specified and set to 'folder' | | Solution | The item.type field of the collaboration item should be set to folder. | | | | | Error | invalid_grant | | Message | Verify the authorization code is set correctly in your request, or your application likely needs to get a new authorization code. | | Solution | The authorization code supplied in the API request is missing or no longer valid. Possible solutions are to verify that the access token is added correctly in the request. If correctly set, the access token may have expired. Attempt to refresh the access token or fetch a new one. | | | | | Error | invalid_grant | | Message | Current date time must be before the expiration date time listed in the 'exp' claim. | | Solution | This error occurs when the Unix time on your local machine and the Box server are out of sync. To fix this error, update the Unix time on your machine to match a synchronized time server, then try the request again. | | | | | Error | invalid_limit | | Message | Limit is not a valid number | | Solution | Add a valid numeric value for the supplied limit value. | | | | | Error | invalid_offset | | Message | Offset is not a valid number | | Solution | Add a valid numeric value for the supplied offset value. | | | | | Error | invalid_request_parameters | | Message | Invalid input parameters in request | | Solution | Invalid parameters were sent in the API request. Check the API reference documentation for the correct request parameters that should be supplied for the API operation. | | | | | Error | invalid_status | | Message | You can change the status only if the collaboration is pending | | Solution | The status of a collaboration can only be updated to accepted or rejected by the user specified in the accessible_by field when the current status is set to pending. | | | | | Error | invalid_upload_session_id | | Message | The upload session ID provided in the URL is not of a valid format. | | Solution | The session ID supplied when making a chunked upload API request was invalid. Use the same session ID from the session that was created. | | | | | Error | item_name_invalid | | Message | Item name invalid | | Solution | Verify that the file's name is valid. Box only supports file or folder names that are 255 characters or less. File names containing non-printable characters, names containing the characters /, \, <, >, :, ` | | | | | Error | item_name_too_long | | Message | Item name too long | | Solution | Shorten the length of the name that is being supplied for the item. The maximum length of a file or folder name in Box is 255 characters or less. | | | | | Error | metadata_after_file_contents | | Message | Metadata is included after file contents in a file upload request. | | Solution | Include the file metadata before the file's contents. | | Error | password_reset_required | | Message | User needs to reset password | | Solution | The user has not yet completed account setup steps. | | | | | Error | requested_page_out_of_range | | Message | Requested representation page out of range | | Solution | The range header supplied does not fit within the size of the specified item. Adjust the bounds to fit within the size of the item and try again. | | | | | Error | requested_preview_unavailable | | Message | Requested preview unavailable | | Solution | The thumbnail size requested for the file is not valid. See the reference docs for the API operation for available format sizes. | | | | | Error | sync_item_move_failure | | Message | Cannot move a synced item | | Solution | The item is set to be synced by the Box sync clients and cannot be moved. A possible solution is to set the sync_state of the item to not_synced. | | | | | Error | task_assignee_not_allowed | | Message | Assigner does not have sufficient privileges to assign task to assignee | | Solution | The user who is attempting to assign the task does not have the appropriate permissions to do so. Adjust the user permissions to allow the assignment of tasks. | | | | | Error | terms_of_service_required | | Message | User must accept custom terms of service before action can be taken | | Solution | The admin of your Box account has set custom terms of service and the user has not logged in to accept the terms yet. The user will need to accept the terms of service, or the admin will have to disable them, in order to proceed. More information is available here. | | | | | Error | user_already_collaborator | | Message | User is already a collaborator | | Solution | The user that you are attempting to collaborate in on an item is already collaborated on that item. This request is not needed. | | | | ### 401 Unauthorized | | | | --- | --- | | Error | unauthorized | | Message | Unauthorized | | Solution | Authorization token is not authorized, check extended error message in body for more details. | ### 403 Forbidden | | | | --- | --- | | Error | access_denied_insufficient_permissions | | Message | Access denied – insufficient permission | | Solution | The Access Token does not have the appropriate user permissions or scopes. See here for solution information. | | | | | Error | insufficient_scope | | Message | The request requires higher privileges than provided by the access token. | | Solution | This error is typically produced when scopes that are needed for the API operation are not enabled. Check your configured application scopes and reauthorize your application, if applicable. | | Error | access_denied_item_locked | | Message | Access denied – item locked | | Solution | You are attempting to access a locked item without appropriate permissions to access it. Unlock the item first, then try to access it again. | | | | | Error | access_from_location_blocked | | Message | | | Solution | You’re attempting to log in to Box from a location that has not been approved by your admin. Talk to your admin to resolve this issue. | | | | | Error | file_size_limit_exceeded | | Message | File size exceeds the folder owner’s file size limit | | Solution | See here for maximum file size limits based on account type. | | | | | Error | forbidden | | Message | | | Solution | Client does not have permission to upload to this session. Only the user who initiated the upload session may upload to it. | | | | | Error | forbidden_by_policy | | Message | Access denied – Blocked by Shield Access Policy | | Solution | Shield access policies applied on your enterprise have prevented this action. Contact your enterprise admin to adjust the applied Shield access policies. | | | | | Error | forbidden_by_policy | | Message | Access denied – Blocked by Shield Malware Detection Rule | | Solution | An active Shield malware detection rule prevents download or local editing of potentially malicious content, but preview and online editing remain available. Contact your enterprise admin to adjust the applied Shield policies. | | | | | Error | incorrect_shared_item_password | | Message | | | Solution | A password is required for the shared item, but it was either incorrect or not supplied. | | | | | Error | storage_limit_exceeded | | Message | Account storage limit reached | | Solution | The storage limit of the account has been reached. Either upgrade your account or permanently delete content to continue. Content that is simply moved to the trash will still count towards the account total until it is permanently deleted. | | | | | Error | user_email_confirmation_required | | Message | User needs to complete email confirmation | | Solution | The user has not yet completed steps for email confirmation. | | | | | Error | cors_origin_not_whitelisted | | Message | Access denied - Did you forget to safelist your origin in the CORS configuration of your app? | | Solution | Your application tried to access the Box API from a website. The application needs to explicitly allow Cross Origin Resource Sharing for the domain your site is hosted on. | ### 404 Not Found | | | | --- | --- | | Error | not_found | | Message | | | Solution | The resource could not be found. Check the extended error message in the response body for more details. | | | | | Error | not_trashed | | Message | Item is not trashed | | Solution | The item that is to be permanently deleted is not in the trash. Send the item to the trash first. | | | | | Error | preview_cannot_be_generated | | Message | Preview cannot be generated | | Solution | You are not able to generate a preview thumbnail for the specified file. | | | | | Error | trashed | | Message | Item is trashed | | Solution | The item that is to be accessed is in the trash and unavailable for modification. Move the item out of the trash and try again. | ### 405 Method Not Allowed | | | | --- | --- | | Error | method_not_allowed | | Message | Method Not Allowed | | Solution | The HTTP method used for the API operation is not allowed. Check the API reference documentation for the HTTP verb needed for the API operation. | ### 409 Conflict | | | | --- | --- | | Error | conflict | | Message | A resource with this value already exists | | Solution | This error may be produced when the resource to be created already exists. Check the extended error message in the response body for more details. | | | | | Error | item_name_in_use | | Message | Item with the same name already exists | | Solution | This error is produced when a resource with the same name already exists. Ensure that the resource name being added / modified is unique. | | | | | Error | name_temporarily_reserved | | Message | The item name is reserved by another processing item. Wait and then retry the request, or wait and check the parent folder to see if the name is in use. | | Solution | Two duplicate requests have been submitted at the same time. Box acknowledges the first and reserves the name, but a second duplicate request arrives before the first request has completed. Allow the first request to complete before sending the second. | | | | | Error | operation_blocked_temporary | | Message | The operation is blocked by another ongoing operation | | Solution | This error is returned when trying to access a folder that is blocked by another folder operation, such as a move or copy. Try again at a later interval. | | | | | Error | recent_similar_comment | | Message | A similar comment has been made recently | | Solution | A similar comment was recently made, and the API has flagged it as a potential duplicate. Verify that the comment was indeed made, or modify the comment content and try again. | | | | | Error | user_login_already_used | | Message | User with the specified login already exists | | Solution | A user with the same email already exists. Either refer to the existing user or specify a different email. | ### 410 Gone | | | | --- | --- | | Error | session_expired | | Message | | | Solution | The upload session associated with the given upload session ID has expired and can no longer be accessed. | | | | | Error | upload_failed | | Message | | | Solution | The upload session is in an unrecoverable state and cannot continue. This or other requests have resulted in the upload session reaching a bad state (for example parts overlapping). Possible situations where this may arise include when the maximum number of parts has been exceeded or when overlapping parts have been uploaded. | ### 411 Length Required | | | | --- | --- | | Error | length_required | | Message | content-length header was required, but not provided. | | Solution | Supply a content-length header within your API request. | ### 412 Precondition Failed | | | | --- | --- | | Error | precondition_failed | | Message | The resource has been modified. Retrieve the resource again and retry | | Solution | Check the extended error message in the response body for more details. | | | | | Error | sync_state_precondition_failed | | Message | The resource has been modified. Retrieve the resource again and retry | | Solution | Check the extended error message in the response body for more details. | ### 413 Request Entity Too Large | | | | --- | --- | | Error | request_entity_too_large | | Message | Request Entity too Large | | Solution | This error is produced when the size of the upload is more than the allowed maximum. Check the extended error message in the response body | ### 415 Unsupported Media Type | | | | --- | --- | | Error | unsupported_media_type | | Message | Previews for boxnote files are not yet supported. | | Solution | This error is produced when requested an embed preview of a Box Note. Embedded previews are currently unsupported for Box Notes. | ### 429 Too Many requests | | | | --- | --- | | Error | rate_limit_exceeded | | Message | Request rate limit exceeded, try again later. | | Solution | The client is performing operations too quickly and has been rate limited. Client is advised to retry their request after the amount of time specified by the retry-after header. There are four rate limits to be aware of. | ### 500 Internal Service Error | | | | --- | --- | | Error | internal_server_error | | Message | Internal Server Error | | Solution | Client should retry using exponential back-off strategy | ### 502 Bad Gateway | | | | --- | --- | | Error | bad_gateway | | Message | | | Solution | Client should retry using exponential back-off strategy | ### 503 Unavailable | | | | --- | --- | | Error | unavailable | | Message | Unavailable | | Solution | If a Retry-After header is provided in the response, the client should retry the request according to the header value. In rare situations, a write operation may eventually persist its changes after the 503 response is received by the client, so the client should handle this case upon retry. If the issue persists, check our Status Site for any known outage information. | **Reference:** https://developer.box.com/guides/api-calls/permissions-and-errors/common-errors/ --- ## Untitled *Type: guide | Category: API Calls * Rate Limits There are three common types of API call rate limitations that Box may use at its discretion to best protect network resources… # Rate Limits There are three common types of API call rate limitations that Box may use at its discretion to best protect network resources and preserve the quality of our customer experience. ## User based These rate limits protect our service from issues that may arise when a single user generates too much traffic. The number of API calls that a user can make in a minute is limited as described below. These limits apply to all Box user accounts and are the most common. Generally, they are initiated when a user exceeds approximately 1000 API calls/minute, but certain API endpoints may have different rate limits. ## Quality of service These rate limits are designed to protect the quality of service of our infrastructure. If there is resource contention in the infrastructure, we introduce automatic rate limits to prevent system degradation and outages. For instance, if an application happens to be accessing the same physical database server, such as the use of a file migration tool accessing related resources that access the same underlying physical resources, Box may impose temporary rate-limits when load spikes and adjust them as the system recovers. ## Licensing based All Box Business Plans come with a licensed number of permitted API calls per enterprise per month. These license based rate limits are designed to prevent excessive overages and misuse of network resources. If Box's infrastructure detects that a tool used by or on behalf of a customer has exceeded that customer's API license allocation or is intending to circumvent network controls, additional selective rate-limiting may be imposed. You can see the default API allocations licensed with a particular account level at our [pricing page](https://www.box.com/pricing), but note that some customers purchase Platform API Pricing plans that increase their allocation. ## Per API rate limits There are currently a few distinct rate limits in place within the Box API. General API calls - 1000 API requests per minute, per user Uploads - 240 file upload requests per minute, per user Search - 6 searches per second, per user, to the [search endpoint](e://get_search) Two additional limits are applied on top of the basic rate limit - 60 searches per minute, per user - 12 searches per second, per enterprise Box Sign - Create and resend sign request: 100 requests per minute, per user - Get sign request: 1000 requests per minute, per user ## Rate limit error When an application hits a rate limit, the API will return an API response with a HTTP status code of `429 Too Many Requests`. The response will include the following headers and JSON body. ``` retry-after: 100 ``` ``` { "type": "error", "status": 429, "code": "rate_limit_exceeded", "help_url": "https://developer.box.com/guides/api-calls/permissions-and-errors/common-errors/", "message": "Request rate limit exceeded, please try again later", "request_id": "abcdef123456" } ``` Please see the [Client Error resource](resource://client_error) for more details. The `retry-after` header provides guidance on the number of seconds to wait before the next API call can be retried. In general, we advise using an exponential back-off strategy for retrying API calls. **Reference:** https://developer.box.com/guides/api-calls/permissions-and-errors/rate-limits/ --- ## Untitled *Type: guide | Category: API Calls * Permissions and errors The following guides provide information on the permissions or errors related to the Box API. It includes pages on… # Permissions and errors The following guides provide information on the permissions or errors related to the Box API. It includes pages on [Common Errors](g://api-calls/permissions-and-errors/common-errors), [Rate Limits](g://api-calls/permissions-and-errors/rate-limits), [Scopes](g://api-calls/permissions-and-errors/scopes), [Token/URL Expiration](g://api-calls/permissions-and-errors/expiration), and the [App Diagnostic Report](g://api-calls/permissions-and-errors/app-diagnostics-report). **Reference:** https://developer.box.com/guides/api-calls/permissions-and-errors/ --- ## Untitled *Type: guide | Category: API Calls * Scopes When an application is created in the Developer Console, the user must configure application scopes. Similar to how users have… # Scopes When an application is created in the Developer Console, the user must configure application scopes. Similar to how users have permissions to access files and folders within Box, applications have their own set of permissions to perform certain actions on behalf of a Box user or a Box enterprise. The name for a set of permissions for an application is a "scope". In short, an application's scopes determine which [endpoints](page://reference) an application can successfully call and are reflected in the access provided by [Access Tokens](g://authentication/tokens) of the application. ## User permissions and scopes It is important to understand that even if an application has the right scopes to perform an action, the user associated with the Access Token making the call needs to have permission to perform the action as well and vice versa. For example, if your application is set up to read files, the authenticated user does need to have permission to read the file you are trying to access. To learn more about how scopes, token permissions, and user permissions work together, see our [security guide](g://security). ## Scopes & OAuth 2 authorization When sending a user through a client-side OAuth 2 flow to authorize your application it is possible to append a set of scopes to the authorization URL to further restrict the user's access token. For example, if you application has the `root_readonly` and `root_readwrite` scopes enabled, it is possible to restrict a user's access token to `root_readonly` by specifying this scope when redirecting the user. ``` GET https://account.box.com/api/oauth2/authorize?scope=root_readonly&client_id=.... ``` When the scope parameter is omitted the application will use the scopes that were set when the application was created. ## Self-service scopes These scopes are available through the Developer Console when configuring an application. Navigate to the **Application Scopes** section of the **Configuration** tab and select one or more of the following scope. ### Read all files and folders | | | | --- | --- | | OAuth Scope | root_readonly | | Application Scope | Read all files and folders stored in Box | Gives an application the ability to read all the files/folders for the authenticated user. Although this gives an application the permission to read files and folders, the user making the API call does need to have access to the items being accessed. In the case of a [JWT](g://authentication/jwt) application accessing a [Managed User's](page://platform/user-types/#managed-users) items, the Service Account's Token will need to either use the `as-user` [header](g://authentication/jwt/as-user) or create a [User Access Token](g://authentication/jwt/user-access-tokens) to directly authenticate as the user who has access to the content. ### Read and write all files and folders | | | | --- | --- | | OAuth Scope | root_readwrite | | Application Scope | Read and write all files and folders stored in Box | Gives an application write access for the authenticated user. This allows the application to upload files or new file versions, download content, create new folders, update or delete collaborations, create comments or tasks, and more. Although this gives an application read/write access to items, the user making the API call needs to have access to the content. ### Manage users The manage users scope in the Developer Console maps to two OAuth scopes. | | | | --- | --- | | OAuth Scope | manage_managed_users | | Application Scope | Manage users | Gives an application permission to manage [Managed Users](page://platform/user-types/#managed-users). It allows the app to change the user's primary login, reset their password, and change roles for managed users. Although this allows an application manage users, for client-side applications, the Access Token used must be associated with an Admin or Co-Admin with the correct permissions. Additionally, for JWT applications, the application must be configured with **App Access + Enterprise Access** [application access](g://authentication/jwt/jwt-setup/#application-access). | | | | --- | --- | | OAuth Scope | manage_app_users | | Application Scope | Manage users | Gives an application permission to manage [App Users](page://platform/user-types/#app-user), which means this scope only applies to server-side authenticated (JWT) applications. ### Manage groups | | | | --- | --- | | OAuth Scope | manage_groups | | Application Scope | Manage groups | Gives an application permission to manage an enterprise's groups. It allows the app to create, update, and delete groups, as well as manage group membership. Although this allows an application manage groups, for client-side applications, the Access Token used must be associated with an Admin Co-Admin with the correct permissions. Additionally, for JWT applications, the application must be configured with **App Access + Enterprise Access** [application access](g://authentication/jwt/jwt-setup/#application-access). ### Manage webhooks | | | | --- | --- | | OAuth Scope | manage_webhook | | Application Scope | Manage webhooks | Gives an application permission to create webhooks for a user. Please review webhook [limitations](g://webhooks/v2/limitations-v2). Most notably, there is a limit of 1000 webhooks per application, per user. ### Manage enterprise properties | | | | --- | --- | | OAuth Scope | manage_enterprise_properties | | Application Scope | Manage enterprise properties | Gives an application permission to view the enterprise event stream, as well as view and edit the enterprise's attributes and reports. It also allows the application to edit and delete device pins. Although this allows an application to enterprise properties, for client-side applications, the Access Token used must must be associated with an Admin Co-Admin with the correct permissions. ### Manage retention policies | | | | --- | --- | | OAuth Scope | manage_data_retention | | Application Scope | Manage retention policies | | Depends on | enterprise_content-scope | Gives an application permission to view and create retention policies with Box Governance. This requires the enterprise to have purchased [Box Governance](https://www.box.com/security/governance-and-compliance). This scope also requires the `enterprise_content` scope to function properly. These scopes can be requested by opening a ticket via our support channels. ### Manage signature requests | | | | --- | --- | | OAuth Scope | sign_requests.readwrite | | Application Scope | Manage signature requests | Gives an application permission to get, create, cancel, and resend sign requests. This scope requires the application to also have read/write scopes, which are automatically selected when enabled. In addition, an enterprise must have Sign enabled. ### Manage Box AI API | | | | --- | --- | | OAuth Scope | ai.readwrite | | Application Scope | Manage AI | Gives an application permission to send requests to Box AI API. ### Manage Box Relay | | | | --- | --- | | OAuth Scope | manage_triggers | | Application Scope | Manage Box Relay | Gives an application permission to get workflows and start flows of type `WORKFLOW_MANUAL_START` This scope requires the application to also have read/write scopes. ## Available on request There are some additional scopes that are only available upon request. To do so, please submit a ticket to our [support team](page://support). They will review these requests on an individual basis and only provide approval if the use case requires the scope. It is not possible to request extra scopes if your account is a free trial account. Before filing a support request for activation of the following scopes, log in to your paid enterprise account or [upgrade your free developer account](https://www.box.com/pricing/platform) to an enterprise account tier. ### Manage Legal Holds | | | | --- | --- | | OAuth Scope | manage_legal_holds | | Application Scope | Manage retention policies | | Depends on | enterprise_content-scope | Gives an application permission to view and create retention policies with Box Governance. This requires the enterprise to have purchased Box Governance. This scope depends on the `enterprise_content` scope to function properly. This scope can be requested by opening a ticket via our support channels. ### Suppress email notifications | | | | --- | --- | | Application Scope | Can suppress email notifications from API calls | Allows some types of [email notifications](g://api-calls/suppress-notifications) to be suppressed when API calls are made. ### Global Content Manager (GCM) | | | | --- | --- | | OAuth Scope | enterprise_content | | Application Scope | Global Content Manager | Allows Admins, [Co-Admins](%5Bhttps://support.box.com/hc/en-us/articles/1500005433721-Users-Groups-Settings#h_01GSE1DYJKTY9EXEWJEDKFHCNV%5D), and [Service Accounts](page://platform/user-types/#service-account) to retrieve any content they do not own or are not collaborators on within their enterprise, based on their enterprise settings. This scope is required to manage the retention policies and legal holds. # Side effects Enabling this scope on an application changes the behavior of some API calls, and most notably, makes it impossible to write content without explicitly authenticating as a user using the `as-user` header. Additionally, enabling this scope disables accessing content that is owned by users in another enterprise. For this reason, this scope will not be provisioned unless absolutely necessary. ## Scopes for downscoping In some cases an Access Token needs to be [downscoped](g://authentication/tokens/downscope) to a more strict permission level, especially when a token needs to be exposed to a client-side, public environment like a browser. The primary example for this is when using [Box UI Elements](https://github.com/box/box-ui-elements), which require an Access Token in the user's browser. The following is a list of **additional** scopes that can be used with the [`POST /oauth2/token`](endpoint://post-oauth2-token) endpoint to downscope an existing access token. | OAuth Scope | UI Element affected | Description | | --- | --- | --- | | annotation_edit | Preview | Allow user to edit & delete annotations | | annotation_view_all | Preview | Allows user to view annotations by all users | | annotation_view_self | Preview | Allows user to view their own annotations only | | base_explorer | Explorer | Allows access to content in the folder tree based on user/file/token permissions | | base_picker | Picker | Allows access to content in the folder tree based on user/file/token permissions | | base_preview | Preview | Allows the user to preview the file, nothing else | | base_sidebar | Sidebar | Allows the user to get basic file info needed for the sidebar UI element | | base_upload | Uploader | Allows upload into the folder specified under resource when downscoping the token | | item_delete | Explorer | Allows files and folders to be deleted | | item_download | Explorer, Preview | Allows files or a folder's content to be downloaded | | item_preview | Explorer | Enables preview of a file | | item_rename | Explorer | Allows files and folders to be renamed | | item_share | Explorer, Picker | Allows the item specified under resource of the token exchange to be shared | | item_upload | Picker | Allows upload in the content picker | The standard OAuth scopes are also supported when downscoping. | OAuth Scope | Description | | --- | --- | | ai.readwrite | Manage AI API | | manage_managed_users | Manage managed users | | manage_app_users | Manage app users | | manage_data_retention | Manage retention policies | | manage_enterprise_properties | Manage enterprise properties | | manage_groups | Manage groups | | manage_webhook | Manage webhooks | | sign_requests.readwrite | Manage sign requests | **Reference:** https://developer.box.com/guides/api-calls/permissions-and-errors/scopes/ --- ## Untitled *Type: guide | Category: API Calls * Versioning errors Box provides versioning capabilities for selected API endpoints. The version control system guarantees seamless… # Versioning errors Box provides versioning capabilities for selected API endpoints. The version control system guarantees seamless functioning of existing endpoint versions, even if Box introduces new ones. API versioning empowers Box to continually enhance its platform, while also offering third-party developers a reliable avenue for feature updates and deprecations. To stay informed about the API modifications, monitor the [Changelog](page://changelog) and maintain a current email address in the **App Info** section of the Developer Console. ## Error examples When using versioned API calls, you can encounter versioning-related errors. This reference lists the most common cases when errors appear and provides you with examples of such errors. ## Calling with incorrect box-version header If you call an API using an incorrect `box-version` header, the API will respond with an `HTTP error code 400 - Bad Request` error and provide the supported versions in the response message. The response will include one of the following status messages in `message` field: | Details | Message | | --- | --- | | box-version value is an unsupported API version or was sent malformed. | Invalid API version specified in 'box-version' header. | | The request headers did not include box-version header when versioned only endpoint was called. | Missing required box-version header. | | box-version is empty. | Invalid (empty) API version specified in 'box-version' header. | | box-version contained multiple version. It requires only one version per request. | The 'box-version' header supports only one header value per request, do not use comas. | | An unsupported API version is used for an existing endpoint. | Unsupported API version specified in 'box-version' header. | An example of a response with an incorrect `box-version` header: ``` { "type": "error", "status": 400, "code": "invalid_api_version", "help_url": "https://developer.box.com/reference/error-codes/invalid-api-version", "message": "Invalid API version specified in 'box-version' header. Supported API versions: [2024.0].", "request_id": "abcdef123456" } ``` ## Calling an incorrect API version in the URL Box documentation specifies API URLs. For instance, the Sign Requests endpoints are accessed via: `https://api.box.com/2.0/sign_requests/`. If you mistakenly make a call to an incorrect version, such as `https://api.box.com/3.0/sign_requests/`, the response returns an `HTTP error code 404 - Not Found` error. ## Calling a deprecated API When you use an API version that Box has marked as deprecated, the API will respond as usual. Additionally, it will append a `Deprecation` header, stating the deprecation date. For example: ``` Deprecation: date="Fri, 11 Nov 2026 23:59:59 GMT" Box-API-Deprecated-Reason: https://developer.box.com/reference/deprecated ``` You should monitor API responses to verify if the `Deprecation` header is present to accordingly plan the transition to a new API version. ## Calling a non-existent version If you attempt to use an outdated API version, such as `2025.0` which has reached its end-of-life, the response will return an `HTTP error code 404 - Not Found`. See [Calling an incorrect API version in the URL](#calling-an-incorrect-api-version-in-the-url) for more information. **Reference:** https://developer.box.com/guides/api-calls/permissions-and-errors/versioning-errors/ --- ## Untitled *Type: guide | Category: Box AI Studio * Create agents Box AI Studio is available only for Enterprise Advanced accounts. The POST /2.0/ai_agents endpoint allows you to create a new… # Create agents Box AI Studio is available only for Enterprise Advanced accounts. The `POST /2.0/ai_agents` endpoint allows you to create a new, custom [AI agent](g://box-ai/ai-agents/index). ## Send a request To send a request, use the `POST /2.0/ai_agents` endpoint. Make sure you have generated the developer token to authorize your app. See [getting started with Box AI](g://ai-studio/getting-started-ai-studio) for details. ### Parameters To make a call, you must pass the following parameters. Mandatory parameters are in **bold**. | Parameter | Description | Example | | --- | --- | --- | | type | The type of agent used to handle queries. | ai_agent | | name | The name of the AI Agent. | My AI Agent | | access_state | The state of the AI Agent. Value is one of enabled disabled. | enabled | | icon_reference | The icon reference of the AI Agent. It should have format of the URL https://cdn01.boxcdn.net/app-assets/aistudio/avatars/<file_name> , where the possible values of file_name are: logo_boxAi.png,logo_stamp.png, logo_legal.png,logo_finance.png,logo_config.png,logo_handshake.png logo_analytics.png,logo_classification.png. | https://cdn01.boxcdn.net/app-assets/aistudio/avatars/logo_analytics.svg | | allowed_entities | List of allowed users or groups. | | | ask | The AI Agent to be used for ask. | ask | | extract | The AI Agent to be used for extraction. | | | text_gen | The AI agent used for generating text. | | **Reference:** https://developer.box.com/guides/ai-studio/ai-studio-agents/create-agents/ --- ## Untitled *Type: guide | Category: Box AI Studio * Delete AI agent The DELETE /2.0/ai_agents/{id} endpoint allows you to delete a custom AI agent based on its ID. Send a request To send a… # Delete AI agent The `DELETE /2.0/ai_agents/{id}` endpoint allows you to delete a custom AI agent based on its ID. ## Send a request To send a request, use the `DELETE /2.0/ai_agents/{id}` endpoint. ### Parameters To make a call, you must pass the following parameters. Mandatory parameters are in **bold**. | Parameter | Description | Example | | --- | --- | --- | | agent_id | The ID of the agent to delete. | 1234 | **Reference:** https://developer.box.com/guides/ai-studio/ai-studio-agents/delete-agents/ --- ## Untitled *Type: guide | Category: Box AI Studio * Get agents Box AI Studio is available only for Enterprise Advanced accounts. List all AI agents The GET /2.0/ai_agents endpoint allows you… # Get agents Box AI Studio is available only for Enterprise Advanced accounts. ## List all AI agents The `GET /2.0/ai_agents` endpoint allows you to list all AI agents based on the provided parameters. ### Send a request To send a request, use the `GET /2.0/ai_agents` endpoint. Make sure you have generated the developer token to authorize your app. See [getting started with Box AI Studio](g://ai-studio/getting-started-ai-studio) for details. #### Parameters To make a call, you must pass the following parameters. Mandatory parameters are in **bold**. | Parameter | Description | Example | | --- | --- | --- | | mode | The mode to filter the agent configuration to return. Possible values are: ask, text_gen, and extract. | ask | | fields | The fields to return in the response. | ask | | agent_state | The state of the agent to return. Value is one of enabled,disabled. | enabled | | fields | The fields to return in the response. Value is one of ask, text_gen, extract. | ask | | include_box_default | Whether to include the Box default agents in the response. | true | | limit | The maximum number of items to return per page. | 1000 | | marker | Defines the position marker at which to begin returning results. | JV9IRGZmieiBasejOG9yDCRNgd2ymoZIbjsxbJMjIs3kioVii | **Reference:** https://developer.box.com/guides/ai-studio/ai-studio-agents/get-agents/ --- ## Untitled *Type: guide | Category: Box AI Studio * Get AI agent by ID The GET /2.0/ai_agents/{id} endpoint allows you to list a specific AI agent by the agent_id parameter. Send a request To… # Get AI agent by ID The `GET /2.0/ai_agents/{id}` endpoint allows you to list a specific AI agent by the `agent_id` parameter. ## Send a request To send a request, use the `GET /2.0/ai_agents/{id}` endpoint. ### Parameters To make a call, you must pass the following parameters. Mandatory parameters are in **bold**. | Parameter | Description | Example | | --- | --- | --- | | agent_id | The agent id to get. | 1234 | | fields | The fields to return in the response. | ask | **Reference:** https://developer.box.com/guides/ai-studio/ai-studio-agents/get-agent-id/ --- ## Untitled *Type: guide | Category: Box AI Studio * AI studio agents Box AI Studio is available only for Enterprise Advanced accounts. Create, list, update, and delete custom AI agents. # AI studio agents Box AI Studio is available only for Enterprise Advanced accounts. Create, list, update, and delete custom AI agents. **Reference:** https://developer.box.com/guides/ai-studio/ai-studio-agents/ --- ## Untitled *Type: guide | Category: Box AI Studio * Update AI agent The PUT /2.0/ai_agents/{id} endpoint allows you to update a custom AI agent based on its ID. Send a request To send a… # Update AI agent The `PUT /2.0/ai_agents/{id}` endpoint allows you to update a custom AI agent based on its ID. ## Send a request To send a request, use the `PUT /2.0/ai_agents/{id}` endpoint. ### Parameters To make a call, you must pass the following parameters. Mandatory parameters are in **bold**. | Parameter | Description | Example | | --- | --- | --- | | type | The type of agent used to handle queries. | ```` | | name | The name of the AI Agent. | My AI Agent | | access_state | The state of the AI Agent. Value is one of enabled disabled. | enabled | | icon_reference | The icon reference of the AI Agent. It should have format of the URL https://cdn01.boxcdn.net/app-assets/aistudio/avatars/<file_name>, where possible values of file_name are: logo_boxAi.png,logo_stamp.png,logo_legal.png,logo_finance.png,logo_config.png,logo_handshake.png,logo_analytics.png,logo_classification.png | https://cdn01.boxcdn.net/app-assets/aistudio/avatars/logo_analytics.svg | | allowed_entities | List of allowed users or groups. | | | ask | The AI Agent to be used for ask. | ask | | extract | The AI Agent to be used for extraction. | | | text_gen | The AI agent used for generating text. | | **Reference:** https://developer.box.com/guides/ai-studio/ai-studio-agents/update-agents/ --- ## Untitled *Type: guide | Category: Applications * Integrations Box Integrations is the first place for Box users to find out about applications they can use in combination with Box. If your… # Integrations [Box Integrations](https://app.box.com/services) is the first place for Box users to find out about applications they can use in combination with Box. If your application can be used by other enterprises, listing your service in under **Integrations** can be a great way to find new users. Integrations group apps into sections so that you can quickly find featured, most popular, or recently added apps. ## Developing a platform app or becoming a Box Partner If you need more information on developing a platform app for the Box Integrations or becoming a Box Partner, visit our [Box Partner Resources](https://support.box.com/hc/en-us/sections/21356597387539-Box-Partner-Programs) guides on our community site. ## Publishing a platform app Use the following steps to publish a platform app in Box Integrations. ### Prerequisites Your application must meet the following requirements: - The platform app is in a finished state and ready for production usage. - The platform app leverages OAuth 2.0 authentication, as Integrations do not support any other authentication methods. - You are a developer with access to the platform app in the **Developer Console**. ### Steps Navigate to the Developer Console > **My Platform Apps** and select the app you want to publish. Select the **Publishing** tab from the top menu. Read through the submission checklist and check the confirmation checkbox if your app meets all the requirements. Fill in the form by providing: - the categories your app falls under - a short and a long description - screenshots and an app icon - supplementary information that will be used to support the users Use the **Preview** button in the top right corner to see how your application will look when listed. Finally, submit the application for approval by clicking the **Submit for Approval** button. Once a request for approval is received, the Box Partner team will be notified and review your request as soon as possible. For any questions, email [`integrate@box.com`](mailto:integrate@box.com). ## Unpublishing a platform app Once approved and published, a platform app can be unpublished from the same control panel: 1. Navigate to the **Developer Console** and select your platform app. 2. Select the **Publishing** tab. 3. You can now unpublish the app. **Reference:** https://developer.box.com/guides/applications/integrations/ --- ## Untitled *Type: guide | Category: Applications * Limited Access App A Limited Access App is used when leveraging Box View or previewing Box content within another application. This type of… # Limited Access App A Limited Access App is used when leveraging [Box View](g://embed/box-view) or previewing Box content within another application. This type of application can only interact with a [limited number of endpoints](g://authentication/app-token/endpoints). ## Authentication methods Limited Access Apps only support App Token authentication. Learn more about App Tokens ## When to use A Limited Access App is best used when the application: - wants to use Box View or only Box's preview services - only needs to access a [limited number of endpoints](g://authentication/app-token/endpoints) ## Use cases - Showcasing a creative professional’s portfolio on their website - Providing user manuals and product spec documents on a support site - A custom document viewer to view e-books or annotate architectural floor plans ## Approval Limited Access Apps may require approval before use. Learn how to approve Limited Access Apps **Reference:** https://developer.box.com/guides/applications/app-types/limited-access-apps/ --- ## Untitled *Type: guide | Category: Applications * Application Types The following is an overview of the different Box Application types you can create. Application Type Authentication… # Application Types The following is an overview of the different Box Application types you can create. | Application Type | Authentication Methods | | --- | --- | | Platform App | OAuth 2.0, JWT, or Client Credentials Grant | | Limited Access App | App Token | | Custom Skill | No selection needed | Learn how to select an application type **Reference:** https://developer.box.com/guides/applications/app-types/ --- ## Untitled *Type: guide | Category: Applications * Platform App Platform Apps encompass most use cases and is the most flexible application type. A platform application typically presents Box… # Platform App Platform Apps encompass most use cases and is the most flexible application type. A platform application typically presents Box functionality to a user within a custom interface. Box offers pre-built, customizable user interface components, known as [UI Elements](g://embed/ui-elements), for functionality like browsing, searching, and previewing content. ## Authentication methods Platform Apps support [OAuth 2.0](g://authentication/oauth2), [JWT](g://authentication/jwt), and [Client Credentials Grant](g://authentication/client-credentials). Learn more about authentication methods ## When to use A Platform App is best used when the application: - Wants to use [OAuth 2.0](g://authentication/oauth2), [JWT](g://authentication/jwt) or [Client Credentials Grant](g://authentication/client-credentials) for authentication. - Wants to upload and download files - Wants the freedom to access both their own files, as well as files owned by [managed or external users](g;//getting-started/user-types/#managed-users/). - Wants the option to list the application in the Box Integrations - Wants to provide integration into the Box Web App ## Use cases Example use cases for a Platform App include: A file vault in an application that allows an end user to access files that have been shared with them, while also providing access for employees to the same files through the Box Web app. An example of this is financial advisor sharing statements and investment prospectuses with investors that can be viewed and commented on within a platform application. A file upload feature in an application that allows an end user to submit and upload files from within a custom-built application to Box. These uploads then initiate a business process with the Box Web app. An example of this is a candidate submitting a PDF of a resume to a recruiting portal then can then be routed to an appropriate employee for review. ## Approval Platform Apps may require approval before use. Learn how to approve Platform Apps **Reference:** https://developer.box.com/guides/applications/app-types/platform-apps/ --- ## Untitled *Type: guide | Category: Applications * Select App Type Upon creating a new Box application in the Developer Console, you must first select one of the following types of… # Select App Type Upon creating a new Box application in the [Developer Console](https://app.box.com/developers/console), you must first select one of the following types of applications. The type of application you select is dependent on the use case for your project and only impacts the available authentication methods when configuring your application. You cannot change this selection later on. ## Platform App | | | | --- | --- | | Authentication methods | OAuth 2.0, JWT, or Client Credentials Authentication | | Notable Features | Webhooks, Integrations, and Web App Integrations | Platform App encompasses most use cases and is the most flexible option. This application type allows for interaction with our 150+ endpoints. For example, downloading/uploading, searching, applying metadata and more. Learn more about Platform Apps ## Limited Access App | | | | --- | --- | | Authentication methods | App Token | | Notable Features | Limited API access | A Limited Access App is best for leveraging [Box View](g://embed/box-view) or previewing Box content within another application. This type of application can only interact with a limited number of endpoints. Learn more about Limited Access Apps ## Custom Skills | | | | --- | --- | | Authentication methods | Access Tokens in Skills events | | Notable Features | Limited API access | A Custom Skill, or Box Skill is a type of application that performs custom processing for files uploaded to Box. Skills are designed to make it possible to use third-party Machine Learning services to automatically extract information from files uploaded to Box. Learn more about Custom Skills **Reference:** https://developer.box.com/guides/applications/app-types/select/ --- ## Untitled *Type: guide | Category: Applications * Custom Skill A Custom Skill, or Box Skill is a type of application that performs custom processing for files uploaded to Box. Skills are… # Custom Skill A Custom Skill, or Box Skill is a type of application that performs custom processing for files uploaded to Box. Skills are designed to make it straightforward to use third-party Machine Learning services to automatically extract information from files uploaded to Box and apply the resulting data as metadata on the file. Custom Skills need to be enabled on a folder by a Box Admin. After this an event is sent to the Skill's application server every time a file is uploaded to the folder. This application can then download the file, inspect it or hand it off to a machine learning service, and write powerful metadata to the file. Start building a Custom Skill ## Authentication methods Working with Custom Skills is simplified by the pre-authorized API credentials that are provided with every Skill Event. For this reason though, Custom Skills have limited API access. You do not need to select an authentication type to work with this type of application. ## When to use A Custom Skill is best used when the application: - Wants to only add metadata to files uploaded to Box - Does not want to upload new files or perform any other API calls - Wants to have way to pass files to Machine Learning services without having to handle authentication ## Use cases Example use cases for Custom Skills include: A process that automatically extracts license plate details from images and writes the details back to the file as keywords. A process that automatically detects faces in videos, and writes the timestamps at which these faces occur back to the file as a timeline. ## Approval Before a Custom Skill can be used it needs to be assigned to a folder for which the skill should trigger. Learn how to approve Custom Skills **Reference:** https://developer.box.com/guides/applications/app-types/custom-skills/ --- ## Untitled *Type: guide | Category: Authentication * Setup with Client Credentials Grant Prerequisites To set up a Platform App using server-side authentication, you will need to ensure you… # Setup with Client Credentials Grant ## Prerequisites To set up a Platform App using server-side authentication, you will need to ensure you have access to the [Developer Console](https://app.box.com/developers/console) from your Box enterprise account. Alternatively, you may sign up for a [developer account](https://account.box.com/signup/n/developer). ## App creation steps ### Navigate to the Developer Console Log into Box and go to the [Developer Console](https://app.box.com/developers/console). Select **Create Platform App**. ### Select application type Select **Platform App** from the list of application types. A modal will appear to prompt a selection for the next step. ### Provide basic application information To describe your app, provide an app name and description. Use the drop-down list to select the app's purpose. Depending on the option chosen, you might need to specify further details. | Purpose | Details | | --- | --- | | Automation, Custom Portal | Specify if the app is built by a customer or partner. | | Integration | Specify the integration category, external system name if the app is built by a customer or partner. | | Other | Specify the app purpose and if it is built by a customer or partner. | ### Select application authentication Select **Server Authentication (with Client Credentials Grant)** if you would like to verify application identity with a client ID and client secret, and confirm with **Create App**. Once you make a selection, you will not be able to change to a different authentication method without creating a new application. ## App Authorization Before the application can be used, a Box Admin needs to authorize the application within the Box Admin Console. Navigate to the **Authorization** tab for your application within the [Developer Console](https://app.box.com/developers/console). Click **Review and Submit** to send an email to your Box enterprise Admin for approval. More information on this process is available in our [authorization guide](g://authorization). Learn how to authorize a Platform Application ## Basic configuration ### Application Access An application's access level determines which users and content your app may access. By default, an application can only successfully interact with the content of its [Service Account](page://platform/user-types/#service-account) and any [App Users](page://platform/user-types). To also access existing Managed Users of an enterprise, navigate to the **Application Access** settings accessible via the **Configuration** tab of the [Developer console](https://app.box.com/developers/console) and set to **App + Enterprise Access**. ### Application Scopes An application's scopes determine which endpoints and resources an application can successfully call. See the [scopes guide](g://api-calls/permissions-and-errors/scopes) for detailed information on each option. ### CORS Domains If your application makes API calls from front-end browser code in Javascript, the domain that these calls are made from will need to be added to an allow-list due to [Cross Origin Resource Sharing](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing), also known as CORS. If all requests will be made from server-side code, you may skip this section. To add the full URI(s) to the allow-list, navigate to the **CORS Domain** section at the bottom of the **Configuration** tab in the [Developer console](https://app.box.com/developers/console). ## Using SDKs and Client Credentials Grant To learn more about Client Credentials Grant for each SDK head over to: [.Net](https://github.com/box/box-windows-sdk-v2/blob/main/docs/authentication.md#server-auth-with-ccg) [Java](https://github.com/box/box-java-sdk/blob/main/doc/authentication.md#client-credentials-grant) [Python](https://github.com/box/box-python-sdk/blob/main/docs/usage/authentication.md#client-credentials-grant) [Node](https://github.com/box/box-node-sdk/blob/main/docs/authentication.md#client-credentials-grant-authentication) [IOS](https://github.com/box/box-ios-sdk/blob/main/docs/usage/authentication.md#client-credentials-grant) **Reference:** https://developer.box.com/guides/authentication/client-credentials/client-credentials-setup/ --- ## Untitled *Type: guide | Category: Authentication * Client Credentials Grant Follow the steps below if you would like to leverage server authentication and verify your application's identity… # Client Credentials Grant Follow the steps below if you would like to leverage server authentication and verify your application's identity using a client ID and client secret. ## Prerequisites - A Platform Application using Server Authentication (with Client Credentials Grant) authentication in the Box [Developer Console](https://app.box.com/developers/console) - [2FA](https://support.box.com/hc/en-us/articles/360043697154-Two-Factor-Authentication-Set-Up-for-Your-Account) enabled on your Box account for viewing and copying the application's client secret from the configuration tab - The application is [authorized](g://authorization) in the Box Admin Console Your client secret is confidential and needs to be protected. Because this is how we securely identify an application's identity when obtaining an Access Token, you do not want to freely distribute a client secret. This includes via email, public forums and code repositories, distributed native applications, or client-side code. If you would like to add more security mechanisms, we recommend using our standard JWT application type. ## How to use When making your API call to obtain an [Access Token](e://post-oauth2-token), your request body needs to contain your client ID and client Secret. Set the `grant_type` to `client_credentials`. If you would like to authenticate as the application's [Service Account](page://platform/user-types/#service-account): - set `box_subject_type` to `enterprise` - set `box_subject_id` to the enterprise ID If you would like to authenticate as an admin or a managed user: - set `box_subject_type` to `user` - set `box_subject_id` to the user ID - enable **App + Enterprise Access** and **Generate User Access Tokens** Box [Developer Console](https://app.box.com/developers/console) If you would like to authenticate as any application user: - set `box_subject_type` to `user` - set `box_subject_id` to the user ID - enable **Generate User Access Tokens** in the Box [Developer Console](https://app.box.com/developers/console) ## Common Errors ### Grant credentials are invalid During authentication, you can encounter the following error: ``` Grant credentials are invalid [400 Bad Request] invalid_grant - Grant credentials are invalid ``` This error indicates either: the client ID and client secret passed are incorrect or are not for the same application, the `box_subject_id` cannot be used based on the selected [application access](g://authentication/client-credentials/client-credentials-setup/#application-access). A CCG app with App Access Only can send in the `box_subject_type` of `enterprise` to authenticate as its service account, but it can't authenticate as a managed user or an admin. to use a `box_subject_type` of `user`, your application should be configured to generate user access tokens in the **Advanced Features** section of the **Configuration tab**. Once you make changes to the app settings, don't forget to [reauthorize](g://authorization/custom-app-approval#re-authorization-on-changes) the application in the Admin Console. - your application has not been authorized in the Box Admin Console **Reference:** https://developer.box.com/guides/authentication/client-credentials/ --- ## Untitled *Type: guide | Category: Authentication * JWT Auth Server-side authentication using JSON Web Tokens (JWT) is the most common way to authenticate to the Box API. JWT is an open… # JWT Auth Server-side authentication using JSON Web Tokens (JWT) is the most common way to authenticate to the Box API. JWT is an [open standard](https://jwt.io/) designed to allow powerful server-to-server authentication. Server-side authentication using JWT is only available to the Platform Application [app type](g://applications/app-types/select). This authentication method does not require end-user interaction and, if granted the proper privileges, can be used to act on behalf of any user in an enterprise. There are two ways you can verify an application's permissions: - using a public and private key pair - using a client id and client secret ([Client Credentials Grant](g://authentication/client-credentials)) To learn more about these options visit our guide on using [JWT without SDKs](g://authentication/jwt/without-sdk). Upon authorizing a JWT application in the Box Admin Console, a [Service Account](page://platform/user-types/#service-account) is automatically generated and is the default Access Token used when authenticating. This is an admin-like user and why applications leveraging JWT require explicit Box Admin approval before use. ## When to use JWT Server-side authentication with JWT is the ideal authentication method for apps that: - Work with users that don't have a Box account - Want to use their own identity system - Don't want users to have to know that they are using Box - Want to store data within the application's Box account and not within the the user's Box account **Reference:** https://developer.box.com/guides/authentication/jwt/ --- ## Untitled *Type: guide | Category: Authentication * as-user Header It is possible to for a JWT application to act on behalf of another user by leveraging the as-user header. In this situation… # as-user Header It is possible to for a JWT application to act on behalf of another user by leveraging the `as-user` header. ``` curl https://api.box.com/2.0/folders/0 \ -H "as-user: [USER_ID]" -H "authorization: Bearer [ACCESS_TOKEN]" ``` In this situation the user ID is the Box identifier for a user. User IDs can found for any user via the `GET /users` endpoint, which is only available to admins, or by calling the `GET /users/me` endpoint with an authenticated user session. ## Preconditions The application must be configured to perform actions as users in the [Developer Console](https://app.box.com/developers/console). Additionally, the authenticated user needs to be a user with Admin permissions, meaning either an Admin or Co-Admin. See our guide on [User Types](page://platform/user-types) for more details. You cannot use the `user_id` of [Service Accounts](page://platform/user-types/#service-account) in the as-user header. ## as-user using SDKs All of the [official Box SDKs](g://tooling/sdks) support acting on behalf of a user using the `as-user` header. ``` var user_client = new BoxClient(config, session, asUser: '[USER_ID]'); ``` ``` client.asUser([USER_ID]"); // client.asSelf(); ``` ``` user_to_impersonate = client.user(user_id='[USER_ID]') user_client = client.as_user(user_to_impersonate) ``` ``` client.asUser('[USER_ID]'); // client.asSelf(); ``` Please note that some of our SDKs create new clients for the other user, while others modify the existing client and provide a way to return to a state where the client authenticates for the original user itself. **Reference:** https://developer.box.com/guides/authentication/jwt/as-user/ --- ## Untitled *Type: guide | Category: Authentication * Setup with JWT A Platform App can be set up to use server-side authentication with JWT. Learn how JWT authentication works Prerequisites To… # Setup with JWT A Platform App can be set up to use server-side authentication with [JWT](g://authentication/jwt). Learn how JWT authentication works ## Prerequisites To set up a Platform App using server-side authentication, you will need to ensure you have access the [Developer Console](https://app.box.com/developers/console) from your Box enterprise account. Alternatively, you may sign up for a [developer account](https://account.box.com/signup/n/developer). ## App creation steps ### Navigate to the Developer Console Log into Box and go to the [Developer Console](https://app.box.com/developers/console). Select **Create Platform App**. ### Select application type Select **Platform App** from the list of application types. A modal will appear to prompt a selection for the next step. ### Provide basic application information To describe your app, provide an app name and description. Use the drop-down list to select the app's purpose. Depending on the option chosen, you might need to specify further details. | Purpose | Details | | --- | --- | | Automation, Custom Portal | Specify if the app is built by a customer or partner. | | Integration | Specify the integration category, external system name if the app is built by a customer or partner. | | Other | Specify the app purpose and if it is built by a customer or partner. | ### Select application authentication Select **Server Authentication (with JWT)** if you would like to verify application identity [with a key pair](g://authentication/jwt/without-sdk/#public-and-private-key-pair) and confirm with **Create Platform App**. Once you make a selection, you will not be able to change to a different authentication method without creating a new application. ## Public and private key pair This section can be skipped if you selected Server Authentication (Client Credentials Grant) as your authentication method. Once a Platform App is created leveraging Server Authentication with JWT, a key pair can be generated via the configuration tab within the [Developer Console](https://app.box.com/developers/console). Alternatively, you can generate your own and supply Box with the public key. Regardless of the method you select, your Box account will need to have [2FA](https://support.box.com/hc/en-us/articles/360043697154-Two-Factor-Authentication-Set-Up-for-Your-Account) enabled for security purposes. ### Generate a keypair (Recommended) If you would like to use a Box generated keypair, navigate to the [Developer Console](https://app.box.com/developers/console) where you can generate a configuration file. This file includes a public/private keypair and a number of other application details that are necessary for authentication. To generate this file, navigate to the **Configuration** tab of the [Developer Console](https://app.box.com/developers/console) and scroll down to the **Add and Manage Public Keys** section. Click the **Generate a Public/Private Keypair** button to have Box generate a keypair you. This will trigger the download of a JSON configuration file that you can move to your application code. For security reasons, Box will not store your private key. If you lose your private key, you will need to reset the entire keypair. ### Manually add keypair Alternatively, you may generate your own keypair and upload the public key to the [Developer Console](https://app.box.com/developers/console). To create a keypair using OpenSSL, open a terminal window and run the following commands. ``` openssl genrsa -des3 -out private.pem 2048 openssl rsa -in private.pem -outform PEM -pubout -out public.pem ``` # For Windows Systems Windows users can install and use the [Cygwin](http://www.cygwin.com/) package to run OpenSSL. Then, navigate to the configuration tab for your application within the [Developer console](https://app.box.com/developers/console) and scroll down to the **Add and Manage Public Keys** section. Click the **Add a Public Key** button, enter the public key generated using the steps above and click **Verify and Save**. ## App Authorization Before the application can be used, a Box Admin needs to authorize the application within the Box Admin Console. Navigate to the **Authorization** tab for your application within the [Developer Console](https://app.box.com/developers/console). Click **Review and Submit** to send an email to your Box enterprise Admin for approval. More information on this process is available in our [support article for app authorization](https://community.box.com/t5/Managing-Developer-Sandboxes/Authorizing-Apps-in-the-Box-App-Approval-Process/ta-p/77293). Learn how to authorize a Platform Application ## Basic configuration ### Application Access An application's access level determines which users and content your app may access. By default, an application can only successfully interact with the content of its [Service Account](page://platform/user-types/#service-account) and any [App Users](page://platform/user-types). To access existing Managed Users of an enterprise and groups that were not created by the app itself, navigate to the **Application Access** settings accessible in the **Configuration** tab of the [Developer console](https://app.box.com/developers/console) and set to **App + Enterprise Access**. Otherwise access to such Managed Users and groups will be blocked. ### Application Scopes An application's scopes determine which endpoints and resources an application can successfully call. See the [scopes guide](g://api-calls/permissions-and-errors/scopes) for detailed information on each option. ### CORS Domains If your application makes API calls from front-end browser code in Javascript, the domain that these calls are made from will need to be added to an allow-list due to [Cross Origin Resource Sharing](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing), also known as CORS. If all requests will be made from server-side code, you may skip this section. To add the full URI(s) to the allow-list, navigate to the **CORS Domain** section at the bottom of the **Configuration** tab in the [Developer console](https://app.box.com/developers/console). **Reference:** https://developer.box.com/guides/authentication/jwt/jwt-setup/ --- ## Untitled *Type: guide | Category: Authentication * JWT with SDKs The official Box SDKs have built-in support for JWT authentication. This guide will take you through user authentication using… # JWT with SDKs The official Box SDKs have built-in support for JWT authentication. This guide will take you through user authentication using JWT with the use of the Box SDKs. JWT authentication is designed for working directly with the Box API without requiring a user to redirect through Box to authorize your application. ## Overview To complete a JWT authorization the following steps need to be completed. 1. Read the configuration file 2. Initialize an SDK client At the end of this flow, the application has a Box SDK client that can be used to make API calls on behalf of the application. The default method of authentication through JWT is inherently tied to the Service Account for the application. Any API call made with this token will seem to come from this application and will not have access to files and folders from other users without explicitly getting access them. ## Prerequisites Before we can get started, you will need to have completed the following steps. - Create a Box Application within the developer console - Create and download the private key configuration file for your application and save it as `config.json` - Ensure your Box Application is approved for usage within your enterprise ## 1. Read JSON configuration After creating a Box Application there should be a `config.json` file containing the application's private key and other details. The following is an example. ``` { "boxAppSettings": { "clientID": "abc...123", "clientSecret": "def...234", "appAuth": { "publicKeyID": "abcd1234", "privateKey": "-----BEGIN ENCRYPTED PRIVATE KEY-----\n....\n-----END ENCRYPTED PRIVATE KEY-----\n", "passphrase": "ghi...345" } }, "enterpriseID": "1234567" } ``` To use this object in the application it needs to be read from file. ``` var reader = new StreamReader("path/to/config.json"); var json = reader.ReadToEnd(); var config = BoxConfig.CreateFromJsonString(json); ``` ``` Reader reader = new FileReader("path/to/config.json"); BoxConfig config = BoxConfig.readFrom(reader); ``` ``` from boxsdk import JWTAuth config = JWTAuth.from_settings_file('path/to/config.json') ``` ``` var config = require("path/to/config.json"); ``` # Parsing JSON In some programming languages there is more than one way to read and parse JSON from a file. Refer to guides on your preferred programming language for more complete guides, including error handling. ## 2. Initialize SDK client The next step is to configure the Box SDK with the configuration and then initialize the client to connect as the application. ``` var sdk = new BoxJWTAuth(config); var token = sdk.AdminToken(); BoxClient client = sdk.AdminClient(token); ``` ``` BoxDeveloperEditionAPIConnection api = BoxDeveloperEditionAPIConnection.getAppEnterpriseConnection(config); ``` ``` client = Client(config) ``` ``` var sdk = BoxSDK.getPreconfiguredInstance(config); var client = sdk.getAppAuthClient("enterprise"); ``` # Service Accounts At this point the application is authenticated as an application user, not as a managed or app user. Head over to our guide on User Types to learn more about the different types of users. ## Summary By now the application should be able to authorize an application using JWT with any of our official SDKs, by using the following steps. 1. Read the configuration file 2. Initialize an SDK client To learn how to use this client head over to the guide on Making API calls. ## Using SDKs and JSON Web Tokens To learn more about JWT for each SDK head over to: [.Net](https://github.com/box/box-windows-sdk-v2/blob/main/docs/authentication.md#server-auth-with-jwt) [Java](https://github.com/box/box-java-sdk/blob/main/doc/authentication.md#server-authentication-with-jwt) [Python](https://github.com/box/box-python-sdk/blob/main/docs/usage/authentication.md#server-auth-with-jwt) [Node](https://github.com/box/box-node-sdk/blob/main/docs/authentication.md#server-auth-with-jwt) [IOS](https://github.com/box/box-ios-sdk/blob/main/docs/usage/authentication.md#server-auth-with-jwt) **Reference:** https://developer.box.com/guides/authentication/jwt/with-sdk/ --- ## Untitled *Type: guide | Category: Authentication * User Access Token It is possible for a JWT application to create an Access Token for a specific user instead of for the Service Account… # User Access Token It is possible for a JWT application to create an Access Token for a specific user instead of for the [Service Account](page://platform/user-types/#service-account). ## Preconditions The application must be configured to allow the creation of user Access Tokens. This setting can be found in the **Configuration** tab of the [Developer Console](https://app.box.com/developers/console). Additionally, the authenticated user needs to be a user with Admin permissions, meaning either an Admin, Co-Admin, or Service Account. See our guide on [User Types](page://platform/user-types) for more details. ## User Access Tokens with SDKs To create a Box SDK client that authenticates as a specific user, follow the steps described in the [JWT with SDK guide](g://authentication/jwt/with-sdk), but create a user client instead of an "Enterprise" client. ``` var userId = "12345"; var sdk = new BoxJWTAuth(config); var token = sdk.UserToken(appUserID); BoxClient client = sdk.UserClient(userToken, userId); ``` ``` String userId = "12345"; BoxDeveloperEditionAPIConnection api = new BoxDeveloperEditionAPIConnection.getAppUserConnection(userId, config) ``` ``` user = client.user(user_id='12345') auth = JWTAuth( client_id='[CLIENT_ID]', client_secret='[CLIENT_SECRET]', user=app_user, jwt_key_id='[JWT_KEY_ID]', rsa_private_key_file_sys_path='[CERT.PEM]', rsa_private_key_passphrase='[PASSPHRASE]' ) auth.authenticate_user() user_client = Client(auth) ``` ``` var sdk = BoxSDK.getPreconfiguredInstance(config); var client = sdk.getAppAuthClient('user', '12345'); ``` Learn more about using the Box SDKs with JWT ## User Access Tokens without SDKs To create a user Access Token that authenticates as a specific user, follow the steps as described in the [JWT without SDK guide](g://authentication/jwt/without-sdk) but instead of creating a claim for the enterprise, create one for a specific user ID. ``` var userId = "12345"; var claims = new List<Claim>{ new Claim("sub", userid), new Claim("box_sub_type", "user"), new Claim("jti", jti), }; ``` ``` String userId = "12345"; JwtClaims claims = new JwtClaims(); claims.setIssuer(config.boxAppSettings.clientID); claims.setAudience(authenticationUrl); claims.setSubject(userId); claims.setClaim("box_sub_type", "user"); claims.setGeneratedJwtId(64); claims.setExpirationTimeMinutesInTheFuture(0.75f); ``` ``` user_id = '12345' claims = { 'iss': config['boxAppSettings']['clientID'], 'sub': user_id, 'box_sub_type': 'user', 'aud': authentication_url, 'jti': secrets.token_hex(64), 'exp': round(time.time()) + 45 } ``` ``` let user_id = '12345'; let claims = { iss: config.boxAppSettings.clientID, sub: user_id, box_sub_type: "user", aud: authenticationUrl, jti: crypto.randomBytes(64).toString("hex"), exp: Math.floor(Date.now() / 1000) + 45 }; ``` ``` user_id = '12345' claims = { iss: config['boxAppSettings']['clientID'], sub: user_id, box_sub_type: 'user', aud: authentication_url, jti: SecureRandom.hex(64), exp: Time.now.to_i + 45 } ``` ``` $userId = '12345'; $claims = [ 'iss' => $config->boxAppSettings->clientID, 'sub' => $userId, 'box_sub_type' => 'user', 'aud' => $authenticationUrl, 'jti' => base64_encode(random_bytes(64)), 'exp' => time() + 45, 'kid' => $config->boxAppSettings->appAuth->publicKeyID ]; ``` Learn more about manually using JWT authentication **Reference:** https://developer.box.com/guides/authentication/jwt/user-access-tokens/ --- ## Untitled *Type: guide | Category: Authentication * JWT without SDKs This guide takes you through JWT authentication without using a Box SDK. JWT does not require end-user interaction and is… # JWT without SDKs This guide takes you through JWT authentication without using a Box SDK. JWT does not require end-user interaction and is designed to authenticate directly with the Box API. To learn how to use this token visit our guide on making API calls. By default, the Access Token acquired with JWT is tied to the Service Account for the application. Any API call made with this token will come from the application. This account does not have access to any existing files or folders until the application's Service Account is added as a collaborator. It is possible to [act as another user](g://authentication/oauth2/as-user) using the `as-user` header or by requesting a [User Access Token](g://authentication/jwt/user-access-tokens). ## Using a key pair Follow the steps below if you would like to verify your application's identity using a public and private key pair. ### Prerequisites - A Platform Application using JWT authentication within the [Developer Console](https://app.box.com/developers/console) - A private key configuration file named `config.json`, which can be downloaded from the configuration tab of the [Developer Console](https://app.box.com/developers/console) - Ensure your application is [authorized](g://authorization) within the Box Admin Console ### 1. Read JSON configuration Your `config.json` file contains the application's private key and other details necessary to authenticate. The following is an example of this file. ``` { "boxAppSettings": { "clientID": "abc...123", "clientSecret": "def...234", "appAuth": { "publicKeyID": "abcd1234", "privateKey": "-----BEGIN ENCRYPTED PRIVATE KEY-----\n....\n-----END ENCRYPTED PRIVATE KEY-----\n", "passphrase": "ghi...345" } }, "enterpriseID": "1234567" } ``` To use this object in the application it needs to be read from file. ``` using System; using System.IO; using Newtonsoft.Json; class Config { public class BoxAppSettings { public class AppAuth { public string privateKey { get; set; } public string passphrase { get; set; } public string publicKeyID { get; set; } } public string clientID { get; set; } public string clientSecret { get; set; } public AppAuth appAuth { get; set; } } public string enterpriseID { get; set; } public BoxAppSettings boxAppSettings { get; set; } } var reader = new StreamReader("config.json"); var json = reader.ReadToEnd(); var config = JsonConvert.DeserializeObject<Config>(json); ``` ``` import java.io.FileReader; import com.google.gson.Gson; import com.google.gson.GsonBuilder; class Config { class BoxAppSettings { class AppAuth { String privateKey; String passphrase; String publicKeyID; } String clientID; String clientSecret; AppAuth appAuth; } BoxAppSettings boxAppSettings; String enterpriseID; } FileReader reader = new FileReader("config.json"); Gson gson = new GsonBuilder().create(); Config config = (Config) gson.fromJson(reader, Config.class); ``` ``` import json import os config = json.load(open('config.json')) ``` ``` const fs = require("fs"); const config = JSON.parse(fs.readFileSync("config.json")); ``` ``` require 'json' config = JSON.parse( File.read('config.json') ) ``` ``` $json = file_get_contents('config.json'); $config = json_decode($json); ``` # Parsing JSON In some programming languages there is more than one way to read and parse JSON from a file. Refer to guides on your preferred programming language for more complete guides, including error handling. ### 2. Decrypt private key To create the JWT assertion the application needs the private key from the configuration object. This private key is encrypted and requires a passcode to unlock. Both the encrypted key and passcode are provided in the configuration object. ``` using System.Security.Cryptography; using Org.BouncyCastle.OpenSsl; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; // https://www.bouncycastle.org/csharp/index.html class PasswordFinder : IPasswordFinder { private string password; public PasswordFinder(string _password) { password = _password; } public char[] GetPassword() { return password.ToCharArray(); } } var appAuth = config.boxAppSettings.appAuth; var stringReader = new StringReader(appAuth.privateKey); var passwordFinder = new PasswordFinder(appAuth.passphrase); var pemReader = new PemReader(stringReader, passwordFinder); var keyParams = (RsaPrivateCrtKeyParameters) pemReader.ReadObject(); public RSA CreateRSAProvider(RSAParameters rp) { var rsaCsp = RSA.Create(); rsaCsp.ImportParameters(rp); return rsaCsp; } public RSAParameters ToRSAParameters(RsaPrivateCrtKeyParameters privKey) { RSAParameters rp = new RSAParameters(); rp.Modulus = privKey.Modulus.ToByteArrayUnsigned(); rp.Exponent = privKey.PublicExponent.ToByteArrayUnsigned(); rp.P = privKey.P.ToByteArrayUnsigned(); rp.Q = privKey.Q.ToByteArrayUnsigned(); rp.D = ConvertRSAParametersField(privKey.Exponent, rp.Modulus.Length); rp.DP = ConvertRSAParametersField(privKey.DP, rp.P.Length); rp.DQ = ConvertRSAParametersField(privKey.DQ, rp.Q.Length); rp.InverseQ = ConvertRSAParametersField(privKey.QInv, rp.Q.Length); return rp; } public byte[] ConvertRSAParametersField(BigInteger n, int size) { byte[] bs = n.ToByteArrayUnsigned(); if (bs.Length == size) return bs; if (bs.Length > size) throw new ArgumentException("Specified size too small", "size"); byte[] padded = new byte[size]; Array.Copy(bs, 0, padded, size - bs.Length, bs.Length); return padded; } var key = CreateRSAProvider(ToRSAParameters(keyParams)); ``` ``` import java.io.StringReader; import java.security.PrivateKey; import java.security.Security; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.openssl.PEMParser; import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder; import org.bouncycastle.operator.InputDecryptorProvider; import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo; // https://www.bouncycastle.org/java.html Security.addProvider(new BouncyCastleProvider()); PEMParser pemParser = new PEMParser( new StringReader(config.boxAppSettings.appAuth.privateKey) ); Object keyPair = pemParser.readObject(); pemParser.close(); char[] passphrase = config.boxAppSettings.appAuth.passphrase.toCharArray(); JceOpenSSLPKCS8DecryptorProviderBuilder decryptBuilder = new JceOpenSSLPKCS8DecryptorProviderBuilder().setProvider("BC"); InputDecryptorProvider decryptProvider = decryptBuilder.build(passphrase); PrivateKeyInfo keyInfo = ((PKCS8EncryptedPrivateKeyInfo) keyPair).decryptPrivateKeyInfo(decryptProvider); PrivateKey key = (new JcaPEMKeyConverter()).getPrivateKey(keyInfo); ``` ``` from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.serialization import load_pem_private_key appAuth = config["boxAppSettings"]["appAuth"] privateKey = appAuth["privateKey"] passphrase = appAuth["passphrase"] # https://cryptography.io/en/latest/ key = load_pem_private_key( data=privateKey.encode('utf8'), password=passphrase.encode('utf8'), backend=default_backend(), ) ``` ``` let key = { key: config.boxAppSettings.appAuth.privateKey, passphrase: config.boxAppSettings.appAuth.passphrase, }; ``` ``` require "openssl" appAuth = config['boxAppSettings']['appAuth'] key = OpenSSL::PKey::RSA.new( appAuth['privateKey'], appAuth['passphrase'] ) ``` ``` $private_key = $config->boxAppSettings->appAuth->privateKey; $passphrase = $config->boxAppSettings->appAuth->passphrase; $key = openssl_pkey_get_private($private_key, $passphrase); ``` # An alternative to loading private key from file the application might not want to keep both the private key and password stored on disk. An alternative option would be to pass in the password as an environment variable, separating the private key from the token used to unlock the key. ### 3. Create JWT assertion To authenticate to the Box API the application needs to create a signed JWT assertion that can be exchanged for an Access Token. A JWT assertion is an encrypted JSON object, consisting of a `header`, `claims`, and `signature`. Let's start by creating the `claims`, sometimes also referred to as the `payload`. ``` using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Collections.Generic; byte[] randomNumber = new byte[64]; RandomNumberGenerator.Create().GetBytes(randomNumber); var jti = Convert.ToBase64String(randomNumber); DateTime expirationTime = DateTime.UtcNow.AddSeconds(45); var claims = new List<Claim>{ new Claim("sub", config.enterpriseID), new Claim("box_sub_type", "enterprise"), new Claim("jti", jti), }; ``` ``` import org.jose4j.jwt.JwtClaims; String authenticationUrl = "https://api.box.com/oauth2/token"; JwtClaims claims = new JwtClaims(); claims.setIssuer(config.boxAppSettings.clientID); claims.setAudience(authenticationUrl); claims.setSubject(config.enterpriseID); claims.setClaim("box_sub_type", "enterprise"); claims.setGeneratedJwtId(64); claims.setExpirationTimeMinutesInTheFuture(0.75f); ``` ``` import time import secrets authentication_url = 'https://api.box.com/oauth2/token' claims = { 'iss': config['boxAppSettings']['clientID'], 'sub': config['enterpriseID'], 'box_sub_type': 'enterprise', 'aud': authentication_url, 'jti': secrets.token_hex(64), 'exp': round(time.time()) + 45 } ``` ``` const crypto = require("crypto"); const authenticationUrl = "https://api.box.com/oauth2/token"; let claims = { iss: config.boxAppSettings.clientID, sub: config.enterpriseID, box_sub_type: "enterprise", aud: authenticationUrl, jti: crypto.randomBytes(64).toString("hex"), exp: Math.floor(Date.now() / 1000) + 45, }; ``` ``` require 'securerandom' authentication_url = 'https://api.box.com/oauth2/token' claims = { iss: config['boxAppSettings']['clientID'], sub: config['enterpriseID'], box_sub_type: 'enterprise', aud: authentication_url, jti: SecureRandom.hex(64), exp: Time.now.to_i + 45 } ``` ``` $authenticationUrl = 'https://api.box.com/oauth2/token'; $claims = [ 'iss' => $config->boxAppSettings->clientID, 'sub' => $config->enterpriseID, 'box_sub_type' => 'enterprise', 'aud' => $authenticationUrl, 'jti' => base64_encode(random_bytes(64)), 'exp' => time() + 45, 'kid' => $config->boxAppSettings->appAuth->publicKeyID ]; ``` | Parameter | Type | Description | | --- | --- | --- | | iss, required | String | The Box Application's OAuth client ID | | sub, required | String | The Box Enterprise ID if this app is to act on behalf of the Service Account of that application, or the User ID if this app wants to act on behalf of another user. | | box_sub_type, required | String | enterprise or user depending on the type of token being requested in the sub claim | | aud, required | String | Always https://api.box.com/oauth2/token | | jti, required | String | A universally unique identifier specified by the application for this JWT. A unique string of at least 16 characters and at most 128 characters. | | exp, required | Integer | The Unix time when this JWT is to expire. Can be set to a maximum value of 60 seconds beyond the issue time. It is recommended to set this to less than the maximum allowed. | | iat, optional | Integer | Issued at time. The token cannot be used before this time. | | nbf, optional | Integer | Not before. Not Specifies when the token will start being valid. | Next, these claims need to be signed using the private key. Depending on the language and library used, the `header` of the JWT is configured by defining the encryption algorithm and the ID of the public key used to sign the claims. ``` using Microsoft.IdentityModel.Tokens; String authenticationUrl = "https://api.box.com/oauth2/token"; var payload = new JwtPayload( config.boxAppSettings.clientID, authenticationUrl, claims, null, expirationTime ); var credentials = new SigningCredentials( new RsaSecurityKey(key), SecurityAlgorithms.RsaSha512 ); var header = new JwtHeader(signingCredentials: credentials); var jst = new JwtSecurityToken(header, payload); var tokenHandler = new JwtSecurityTokenHandler(); string assertion = tokenHandler.WriteToken(jst); ``` ``` import org.jose4j.jws.AlgorithmIdentifiers; import org.jose4j.jws.JsonWebSignature; JsonWebSignature jws = new JsonWebSignature(); jws.setPayload(claims.toJson()); jws.setKey(key); jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA512); jws.setHeader("typ", "JWT"); jws.setHeader("kid", config.boxAppSettings.appAuth.publicKeyID); String assertion = jws.getCompactSerialization(); ``` ``` import jwt keyId = config['boxAppSettings']['appAuth']['publicKeyID'] assertion = jwt.encode( claims, key, algorithm='RS512', headers={ 'kid': keyId } ) ``` ``` const jwt = require("jsonwebtoken"); let keyId = config.boxAppSettings.appAuth.publicKeyID; let headers = { algorithm: "RS512", keyid: keyId, }; let assertion = jwt.sign(claims, key, headers); ``` ``` require 'jwt' keyId = appAuth['publicKeyID'] assertion = JWT.encode(claims, key, 'RS512', { kid: keyId }) ``` ``` use \Firebase\JWT\JWT; $assertion = JWT::encode($claims, $key, 'RS512'); ``` For the header the following parameters are supported. | Parameter | Type | Description | | --- | --- | --- | | algorithm, required | String | The encryption algorithm used to sign the JWT claim. This can be one of RS256, RS384, or RS512. | | keyid, required | String | The ID of the public key used to sign the JWT. Not required, though essential when multiple key pairs are defined for an application. | Using JWT libraries Signing your own JWT can be a complicated and painful process. Luckily, the hard work has already been done for you and libraries exist in pretty much every language. Head over to [JWT.io](https://jwt.io/) for an overview. ### 4. Request Access Token The final step is to exchange the short lived JWT assertion for a more long lived Access Token by calling the token endpoint with the assertion as a parameter. ``` using System.Net; using System.Net.Http; var content = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>( "grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer"), new KeyValuePair<string, string>( "assertion", assertion), new KeyValuePair<string, string>( "client_id", config.boxAppSettings.clientID), new KeyValuePair<string, string>( "client_secret", config.boxAppSettings.clientSecret) }); var client = new HttpClient(); var response = client.PostAsync(authenticationUrl, content).Result; class Token { public string access_token { get; set; } } var data = response.Content.ReadAsStringAsync().Result; var token = JsonConvert.DeserializeObject<Token>(data); var accessToken = token.access_token; ``` ``` import java.util.ArrayList; import java.util.List; import org.apache.http.HttpEntity; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; List<NameValuePair> params = new ArrayList<NameValuePair>(); params.add(new BasicNameValuePair( "grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer")); params.add(new BasicNameValuePair( "assertion", assertion)); params.add(new BasicNameValuePair( "client_id", config.boxAppSettings.clientID)); params.add(new BasicNameValuePair( "client_secret", config.boxAppSettings.clientSecret)); CloseableHttpClient httpClient = HttpClientBuilder.create().disableCookieManagement().build(); HttpPost request = new HttpPost(authenticationUrl); request.setEntity(new UrlEncodedFormEntity(params)); CloseableHttpResponse httpResponse = httpClient.execute(request); HttpEntity entity = httpResponse.getEntity(); String response = EntityUtils.toString(entity); httpClient.close(); class Token { String access_token; } Token token = (Token) gson.fromJson(response, Token.class); String accessToken = token.access_token; ``` ``` import json import requests params = { 'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer', 'assertion': assertion, 'client_id': config['boxAppSettings']['clientID'], 'client_secret': config['boxAppSettings']['clientSecret'] } response = requests.post(authentication_url, params) access_token = response.json()['access_token'] ``` ``` const axios = require("axios"); const querystring = require("querystring"); let accessToken = await axios .post( authenticationUrl, querystring.stringify({ grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer", assertion: assertion, client_id: config.boxAppSettings.clientID, client_secret: config.boxAppSettings.clientSecret, }) ) .then((response) => response.data.access_token); ``` ``` require 'json' require 'uri' require 'net/https' params = URI.encode_www_form({ grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', assertion: assertion, client_id: config['boxAppSettings']['clientID'], client_secret: config['boxAppSettings']['clientSecret'] }) uri = URI.parse(authentication_url) http = Net::HTTP.start(uri.host, uri.port, use_ssl: true) request = Net::HTTP::Post.new(uri.request_uri) request.body = params response = http.request(request) access_token = JSON.parse(response.body)['access_token'] ``` ``` use GuzzleHttp\Client; $params = [ 'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer', 'assertion' => $assertion, 'client_id' => $config->boxAppSettings->clientID, 'client_secret' => $config->boxAppSettings->clientSecret ]; $client = new Client(); $response = $client->request('POST', $authenticationUrl, [ 'form_params' => $params ]); $data = $response->getBody()->getContents(); $access_token = json_decode($data)->access_token; ``` ## Code Samples The code in this guide is available on [GitHub](https://github.com/box-community/samples-docs-authenticate-with-jwt-api). **Reference:** https://developer.box.com/guides/authentication/jwt/without-sdk/ --- ## Untitled *Type: guide | Category: Authentication * Annotator Tokens Annotations is one of the key features supported by new Box View, that allows developers to provide collaboration… # Annotator Tokens Annotations is one of the key features supported by new Box View, that allows developers to provide collaboration capabilities right from within the embedded Box preview in their application. Box View supports three annotation types: highlight only, highlight annotation, and point annotation. Annotations are only supported on document and image previews. ## What is an Annotator Token An Annotator Token is an Access Token that allows an application to create a Preview Embed Link for a file that a user can make annotations on. As an application might not create a new App User for every one of the application's users, the Annotator token allows the application to track which of their own users made the annotations. The Annotator Token is used instead of a regular Access Token, App Token, or File Token to generate a preview session (an expiring embed link) that is linked to a unique user ID and display name. Since a preview session generated using an annotator token is tied to a specific external user, it is strongly recommended that an application generates different preview sessions using different annotator tokens for different end users of an application. ## External user info The external display name associated with an annotation is essentially a stateless "label" appended to the annotation. This means that once an annotation has been added, the display name is permanently associated with the annotation and cannot be updated unless the annotation is deleted and added again with the updated display name. ## Create without SDKs To create an annotator token, follow the instructions for manually authenticating through JWT but replace the JWT claim with the following data. ``` var claims = new List<Claim>{ new Claim("sub", '[EXTERNAL_USER_ID]'), new Claim("name", '[EXTERNAL_USER_DISPLAY_NAME]'), new Claim("box_sub_type", "external"), new Claim("jti", jti), }; ``` ``` JwtClaims claims = new JwtClaims(); claims.setIssuer(config.boxAppSettings.clientID); claims.setAudience(authenticationUrl); claims.setSubject("[EXTERNAL_USER_ID]"); claims.setName("[EXTERNAL_USER_DISPLAY_NAME]"); claims.setClaim("box_sub_type", "external"); claims.setGeneratedJwtId(64); claims.setExpirationTimeMinutesInTheFuture(0.75f); ``` ``` claims = { 'iss': config['boxAppSettings']['clientID'], 'sub': '[EXTERNAL_USER_ID]', 'name': '[EXTERNAL_USER_DISPLAY_NAME]', 'box_sub_type': 'external', 'aud': authentication_url, 'jti': secrets.token_hex(64), 'exp': round(time.time()) + 45 } ``` ``` let claims = { iss: config.boxAppSettings.clientID, sub: "[EXTERNAL_USER_ID]", name: "[EXTERNAL_USER_DISPLAY_NAME]", box_sub_type: "external", aud: authenticationUrl, jti: crypto.randomBytes(64).toString("hex"), exp: Math.floor(Date.now() / 1000) + 45 }; ``` ``` claims = { iss: config['boxAppSettings']['clientID'], sub: "[EXTERNAL_USER_ID]", name: "[EXTERNAL_USER_DISPLAY_NAME]", box_sub_type: 'external', aud: authentication_url, jti: SecureRandom.hex(64), exp: Time.now.to_i + 45 } ``` ``` $claims = [ 'iss' => $config->boxAppSettings->clientID, 'sub' => '[EXTERNAL_USER_ID]', 'name' => '[EXTERNAL_USER_DISPLAY_NAME]', 'box_sub_type' => 'external', 'aud' => $authenticationUrl, 'jti' => base64_encode(random_bytes(64)), 'exp' => time() + 45, 'kid' => $config->boxAppSettings->appAuth->publicKeyID ]; ``` | Parameter | Type | Description | | --- | --- | --- | | sub | String | The external user ID to tie this annotation to. This can be any arbitrary ID tracked by the application | | box_sub_type | String | external to signify an external user ID | | box_sub_type | String | The external user name to tie this annotation to. This will be displayed in the Box UI | Then, convert this claim to an assertion according to the guide and pass this assertion to the [`POST /oauth2/token`](e://post-oauth2-token) endpoint together with an existing valid Access Token, App Token, or File Token, as well as a set of scopes, and the resource for which to create the token. ``` var content = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>( "grant_type", "urn:ietf:params:oauth:grant-type:token-exchange"), new KeyValuePair<string, string>( "resource", "https://api.box.com/2.0/files/123456"), new KeyValuePair<string, string>( "subject_token", "[ACCESS_TOKEN]"), new KeyValuePair<string, string>( "subject_token_type", "urn:ietf:params:oauth:token-type:access_token"), new KeyValuePair<string, string>( "scope", "item_preview"), new KeyValuePair<string, string>( "actor_token", "[JWT_ASSERTION_FOR_ANNOTATOR_TOKEN]"), new KeyValuePair<string, string>( "actor_token_type", "urn:ietf:params:oauth:token-type:id_token"), }); ``` ``` List<NameValuePair> params = new ArrayList<NameValuePair>(); params.add(new BasicNameValuePair( "grant_type", "urn:ietf:params:oauth:grant-type:token-exchange")); params.add(new BasicNameValuePair( "resource", "https://api.box.com/2.0/files/123456")); params.add(new BasicNameValuePair( "subject_token", "[ACCESS_TOKEN]")); params.add(new BasicNameValuePair( "subject_token_type", "urn:ietf:params:oauth:token-type:access_token")); params.add(new BasicNameValuePair( "scope", "item_preview")); params.add(new BasicNameValuePair( "actor_token", "[JWT_ASSERTION_FOR_ANNOTATOR_TOKEN]")); params.add(new BasicNameValuePair( "actor_token_type", "urn:ietf:params:oauth:token-type:id_token")); ``` ``` params = urlencode({ 'grant_type': 'urn:ietf:params:oauth:grant-type:token-exchange', 'resource': 'https://api.box.com/2.0/files/123456', 'subject_token': '[ACCESS_TOKEN]', 'subject_token_type': 'urn:ietf:params:oauth:token-type:access_token', 'scope': 'item_preview', 'actor_token': '[JWT_ASSERTION_FOR_ANNOTATOR_TOKEN]', 'actor_token_type': 'urn:ietf:params:oauth:token-type:id_token' }).encode() ``` ``` let accessToken = await axios .post( authenticationUrl, querystring.stringify({ grant_type: "urn:ietf:params:oauth:grant-type:token-exchange", resource: "https://api.box.com/2.0/files/123456", subject_token: "[ACCESS_TOKEN]", subject_token_type: "urn:ietf:params:oauth:token-type:access_token", scope: "item_preview", actor_token: "[JWT_ASSERTION_FOR_ANNOTATOR_TOKEN]", actor_token_type: "urn:ietf:params:oauth:token-type:id_token" }) ) .then(response => response.data.access_token); ``` ``` params = URI.encode_www_form({ grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange', resource: 'https://api.box.com/2.0/files/123456', subject_token: '[ACCESS_TOKEN]', subject_token_type: 'urn:ietf:params:oauth:token-type:access_token', scope: 'item_preview', actor_token: '[JWT_ASSERTION_FOR_ANNOTATOR_TOKEN]', actor_token_type: 'urn:ietf:params:oauth:token-type:id_token' }) ``` ``` $params = [ 'grant_type' => 'urn:ietf:params:oauth:grant-type:token-exchange', 'resource' => 'https://api.box.com/2.0/files/123456', 'subject_token' => '[ACCESS_TOKEN]', 'subject_token_type' => 'urn:ietf:params:oauth:token-type:access_token', 'scope' => 'item_preview', 'actor_token' => '[JWT_ASSERTION_FOR_ANNOTATOR_TOKEN]', 'actor_token_type' => 'urn:ietf:params:oauth:token-type:id_token' ]; ``` | Parameter | Description | | --- | --- | | resource | An optional full URL path to the file the token should be restricted to. | | actor_token | The JWT assertion created earlier | | actor_token_type | Always set to urn:ietf:params:oauth:token-type:id_token | ## Create with SDKs To create a JWT annotator token with an SDK an application can exchange any active token for another token. ``` var options = { actor: { id: "[EXTERNAL_USER_ID]", name: "[EXTERNAL_USER_DISPLAY_NAME" } }; client .exchangeToken( "item_preview", "https://api.box.com/2.0/files/123456", options ) .then(tokenInfo => { //=> tokenInfo.accessToken }); ``` **Reference:** https://developer.box.com/guides/authentication/tokens/annotator-tokens/ --- ## Untitled *Type: guide | Category: Authentication * Access Tokens Instead of a user name and password, Access Tokens are the credentials used to represent the authenticated user to the Box… # Access Tokens Instead of a user name and password, Access Tokens are the credentials used to represent the authenticated user to the Box servers. ## Token Object ### OAuth 2.0 authentication When an Access Token is requested using OAuth 2.0, an Access Token and Refresh Token pair are returned. ``` curl -X POST https://api.box.com/oauth2/token \ -H "content-type: application/x-www-form-urlencoded" \ -d '...' ``` ``` { "access_token": "c3FIOG9vSGV4VHo4QzAyg5T1JvNnJoZ3ExaVNyQWw6WjRsanRKZG5lQk9qUE1BVQ", "expires_in": 3600, "token_type": "bearer", "refresh_token": "c3FIOG9vSGV4VHo4QzAyg5T1JvNnJoZ3ExaVNyQWw6WjRsanRKZG5lQk9qUE1BVQ", "issued_token_type": "urn:ietf:params:oauth:token-type:access_token" } ``` Within this object we can see the token string (`access_token`), as well as the Refresh Token (`refresh_token`) that can be used to request a new Access Token when the current one expires (`expires_in`). ### Server authentication When an Access Token is requested using JWT or Client Credentials Grant, only an Access Token is returned: ``` curl --location --request POST 'https://api.box.com/oauth2/token' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode '...' ``` ``` { "access_token": "DkXZmsjUKizvL2z0WiaLvMBeQ756XCGGf", "expires_in": 4123, "restricted_to": [], "issued_token_type": "bearer" } ``` Within this object we can see the token string (`access_token`). Because a Refresh Token is not returned, you must request a new token when the Access Token expires (`expires_in`) using the [token endpoint](e://post-oauth2-token). **Reference:** https://developer.box.com/guides/authentication/tokens/access-tokens/ --- ## Untitled *Type: guide | Category: Authentication * Tokens At the core of every Box API call is an Access Token. Similar to using the Box Web App, you will only be able to successfully… # Tokens At the core of every Box API call is an Access Token. Similar to using the Box Web App, you will only be able to successfully interact with content the user associated with the Access Token either a collaborator on or owns. This can be further restricted by [downscoping](g://authentication/tokens/downscope) a token. Required access scopes, application access, enabled advanced settings, user permissions, and endpoint-specific restrictions all work together to determine which API calls will be successful. For example, even if a user has collaborator access to a folder, a call to get information about the folder will not be successful if the read scope is not granted to the application. ## Types of tokens | Type | Duration | | --- | --- | | Access Token | 60 minutes | | Refresh Token | 60 days or one use | | Developer Token | 60 minutes | ## Application Types & Access Tokens The following shows how each application type is expected to create an Access Token. | Box Application Type | How to get Access Token | | --- | --- | | Platform App + OAuth 2.0 | Explicit user grant | | Platform App + JWT | Exchange a JWT assertion | | Platform App + Client Credentials Grant | Use client ID and client secret | | Limited Access App + App Token | Configure token in Developer Console | | Custom Skill | Access Token in event payload | **Reference:** https://developer.box.com/guides/authentication/tokens/ --- ## Untitled *Type: guide | Category: Authentication * Revoke a Token An Access Token can be revoked at any time by either sending the Access Token or Refresh Token the POST /oauth2/revoke… # Revoke a Token An Access Token can be revoked at any time by either sending the Access Token or Refresh Token the [`POST /oauth2/revoke`](endpoint://post-oauth2-revoke) endpoint. # Usage in SDKs All of the Box SDKs support manually revoking the current Access Token associated with the client. To revoke a specific token, first initialize a new SDK with that token and then call the relevant revoke method. **Reference:** https://developer.box.com/guides/authentication/tokens/revoke/ --- ## Untitled *Type: guide | Category: Authentication * Using in SDKs The SDKs have direct support for JWT and OAuth 2.0 authentication, yet in some cases your application might need to provide an… # Using in SDKs The SDKs have direct support for JWT and OAuth 2.0 authentication, yet in some cases your application might need to provide an Access Token to the SDK directly. ## Developer Token Similarly, developer tokens can be used directly in SDKs without needing any further SDK configuration. ## App Token When using App Token authentication it is possible to directly pass the App Token to the SDKs. **Reference:** https://developer.box.com/guides/authentication/tokens/sdks/ --- ## Untitled *Type: guide | Category: Authentication * Refresh a Token An Access Token can be refreshed by using the Refresh Token that came with the Access Token. This can be done before or… # Refresh a Token An Access Token can be refreshed by using the Refresh Token that came with the Access Token. This can be done before or after the Access Token expires. To do this, the application passes the `refresh_token` to the [`POST /oauth2/token`](endpoint://post-oauth2-token) endpoint as follows. # Usage in SDKs All of the Box SDKs support automatic Access Token renewal for JWT and OAuth 2.0 applications. # Refresh token expiration A Refresh Token is valid for 60 days and can be used to obtain a new Access Token and Refresh Token only once. If the Access Token and Refresh Token are not refreshed within 60 days, the user will need to be re-authorized. Every time an application uses the Refresh Token to get a new Access Token the Refresh Token is invalidated and a new Refresh Token is returned with the new Access Token. This new Refresh Token is then again only valid for 1 use within 60 days. **Reference:** https://developer.box.com/guides/authentication/tokens/refresh/ --- ## Untitled *Type: guide | Category: Authentication * As-User Header It is possible to for an OAuth 2.0 application to act on behalf of another user through the as-user header. In this situation… # As-User Header It is possible to for an OAuth 2.0 application to act on behalf of another user through the `as-user` header. ``` curl https://api.box.com/2.0/folders/0 \ -H "as-user: [USER_ID]" -H "authorization: Bearer [ACCESS_TOKEN]" ``` In this situation the user ID is the Box identifier for a user. User IDs can be found for any user via the `GET /users` endpoint, which is only available to admins, or by calling the `GET /users/me` endpoint with an authenticated user session. ## Preconditions Using the `as-user` header has a few requirements. Firstly, the application needs to be configured to perform actions as users in the Developer Console. Additionally, the authenticated user needs to be a user with admin permissions, meaning either an admin, co-admin, or service account. Co-admin users will also need the 'Manage Users' permission scope. See our guide on User Types for more details. ## as-user using SDKs ``` var user_client = new BoxClient(config, session, asUser: '[USER_ID]'); ``` ``` client.asUser([USER_ID]"); // client.asSelf(); ``` ``` user_to_impersonate = client.user(user_id='[USER_ID]') user_client = client.as_user(user_to_impersonate) ``` ``` client.asUser('[USER_ID]'); // client.asSelf(); ``` Please note that some of our SDKs create new clients for the other user, while others modify the existing client and provide a way to return to a state where the client authenticates for the original user itself. **Reference:** https://developer.box.com/guides/authentication/oauth2/as-user/ --- ## Untitled *Type: guide | Category: Authentication * OAuth 2.0 Auth Client-side OAuth 2.0 is one of the easiest ways to authenticate a user for the Box API. It is an open standard designed to… # OAuth 2.0 Auth Client-side OAuth 2.0 is one of the easiest ways to authenticate a user for the Box API. It is an [open standard](https://oauth.net/2/) designed to allow users to provide applications access to their data in other applications. If you've ever logged in to a website with Twitter, Facebook, or Google you've most likely used OAuth 2.0. Client-side authentication on Box has a similar flow where a user is redirected from an application to the Box web app, required to log in, and grant the application access to the user's data. ## When to use OAuth 2.0 Client-side authentication is the ideal authentication method for apps that: - Work with users that already have existing Box accounts - Want or require users to know that they are using Box - Want to store data within the user's Box account and not within the the application's Box account **Reference:** https://developer.box.com/guides/authentication/oauth2/ --- ## Untitled *Type: guide | Category: Authentication * Setup with OAuth 2.0 A Platform App can be set up to use client-side OAuth 2.0 authentication. Learn how OAuth 2.0 authentication works… # Setup with OAuth 2.0 A Platform App can be set up to use client-side [OAuth 2.0](g://authentication/oauth2) authentication. Learn how OAuth 2.0 authentication works ## Prerequisites To set up a Platform App using OAuth 2.0 authentication, you will need to ensure you have access the [Developer Console](https://app.box.com/developers/console) from your Box enterprise account. Alternatively, you may sign up for a [developer account](https://account.box.com/signup/n/developer). ## App creation steps ### Navigate to the Developer Console Log into Box and go to the [Developer Console](https://app.box.com/developers/console). Select **Create Platform App**. ### Select application type Select **Platform App** from the list of application types. A modal will appear to prompt a selection for the next step. ### Provide basic application information To describe your app, provide an app name and description. Use the drop-down list to select the app's purpose. Depending on the option chosen, you might need to specify further details. | Purpose | Details | | --- | --- | | Automation, Custom Portal | Specify if the app is built by a customer or partner. | | Integration | Specify the integration category, external system name if the app is built by a customer or partner. | | Other | Specify the app purpose and if it is built by a customer or partner. | ### Select application authentication Select **User Authentication (OAuth 2.0)** and confirm with **Create App**. Once you make a selection, you will not be able to change to a different authentication method without creating a new application. ## Basic configuration Before the application can be used, some additional configuration is required. ### Redirect URI During the OAuth 2.0 flow, users are redirected to their browser to authenticate and then authorize the application to take actions on their behalf. Prior to redirecting the user, Box verifies that the `redirect_uri` parameter passed into the [authorization URL](e://get-authorize/#param-redirect_uri) matches one of the redirect URIs configured for the application. This will be an exact match check, meaning the URIs must be exactly the same. Localhost and loopback address redirect URIs will be permitted redirect to any port, but the scheme, domain, path and query parameters must match one of the configured URIs. You can configure these under the OAuth 2.0 Redirect URI section on the Configuration page in the developer console. These must be valid URIs that are HTTPS, or a less secure HTTP for localhost or loopback address. We do not permit duplicate URIs to be saved. Starting November 29, 2021, new applications using OAuth 2.0 will require the URIs set in the configuration tab of the Developer Console to strictly match the one used during redirect. In addition, both new and existing applications, will gain the ability to add multiple redirect URIs. If you configured multiple redirect URIs for the application, the authorization URL must include the `redirect_uri` parameter matching one of the URIs configured in the developer console. If the parameter is not specified, the user will see a `redirect_uri_missing` error and will not be redirected back to the app after granting application access. For existing applications, the deadline to make changes to this URL to avoid service disruption is May 13, 2022. ### Application Scopes Scopes define what permissions your application has in order to access data. See the [scopes guide](g://api-calls/permissions-and-errors/scopes) for detailed information on each option. ### CORS Domains If your application makes API calls from front-end browser code in Javascript, the domain that these calls are made from will need to be added to an allow-list due to [Cross Origin Resource Sharing](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing), also known as CORS. If all requests will be made from server-side code, you may skip this section. To add the full URI(s) to the allow-list, navigate to the **CORS Domain** section at the bottom of the **Configuration** tab in the [Developer Console](https://app.box.com/developers/console). **Reference:** https://developer.box.com/guides/authentication/oauth2/oauth2-setup/ --- ## Untitled *Type: guide | Category: Authentication * OAuth 2.0 with SDKs The Box SDKs have built-in support for client-side OAuth 2.0. In the process a user is redirected to the Box web app in… # OAuth 2.0 with SDKs The Box SDKs have built-in support for client-side OAuth 2.0. In the process a user is redirected to the Box web app in a browser where they log in and authorize the application access to their data before they are redirected back to the applications `redirect_url`. This last step requires the application to be running on a web server somewhere accessible to the user. ## Overview To complete an OAuth 2.0 flow the following steps need to be completed. 1. Configure the Box SDK 2. Redirect the user to the Box website 3. The user grants the application access 4. Exchange the authorization code for an access token At the end of this flow, the application has an Access Token that can be used to make API calls on behalf of this user. The access token acquired through OAuth 2.0 is inherently tied to the user who authorized the application. Any API call made with this token will seem to come from this application, and the user needs to have access to any file or folder the application tries to access with this token. ## Parameters | Parameter | Description | | --- | --- | | CLIENT_ID | The client ID or API key for the application | | CLIENT_SECRET | The client secret or API secret for the application | | REDIRECT_URI | The redirect URL for your application that a user will be sent to after they have authorized the application. This can be configured in the developer console | ## 1. Configure SDK The first step is to make sure your environment has been prepared with the SDK of your choice. ``` var redirectUrl = "[REDIRECT_URI]"; var config = new BoxConfig("[CLIENT_ID]", "[CLIENT_SECRET]", new Uri(redirectUrl)); var sdk = new BoxClient(config); ``` ``` import com.box.sdk.BoxAPIConnection; String authorizationUrl = "https://account.box.com/api/oauth2/authorize?client_id=[CLIENT_ID]&response_type=code"; ``` ``` from boxsdk import OAuth2, Client auth = OAuth2( client_id='[CLIENT_ID]', client_secret='[CLIENT_SECRET]' ) ``` ``` var BoxSDK = require("box-node-sdk"); var sdk = new BoxSDK({ clientID: "[CLIENT_ID]", clientSecret: "[CLIENT_SECRET]", }); ``` Learn more about installing an SDK for your environment ## 2. Redirect user Next, redirect the user to the authorization URL. Most of the SDKs support a way to get the authorization URL for an SDK client. If you configured multiple redirect URIs for the application, the authorization URL must include the `redirect_uri` parameter matching one of the URIs configured in the developer console. If the parameter is not specified, the user will see a `redirect_uri_missing` error and will not be redirected back to the app after granting application access. ``` var authorizationUrl = "https://account.box.com/api/oauth2/authorize?client_id=[CLIENT_ID]&response_type=code"; // redirectTo(authorizationUrl); ``` ``` String authorizationUrl = "https://account.box.com/api/oauth2/authorize?client_id=[CLIENT_ID]&response_type=code"; // response.redirect(authorizationUrl); ``` ``` auth_url, csrf_token = auth.get_authorization_url('[REDIRECT_URL]') // redirect(auth_url, code=302) ``` ``` var authorize_url = sdk.getAuthorizeURL({ response_type: "code", }); // res.redirect(authorize_url) ``` The way in which a user is redirected to a URL depends on the application framework used. Most framework documentation provides extensive guidance on this topic. The [authorization URL](endpoint://get-authorize) can also be created manually as follows. ``` https://account.box.com/api/oauth2/authorize?client_id=[CLIENT_ID]&redirect_uri=[REDIRECT_URI]&response_type=code ``` Additional query parameters can be passed along when redirecting the user to limit down the scope, or pass along some extra state. See the reference documentation for more information. If you have [Box Verified Enterprise](https://support.box.com/hc/en-us/articles/360043693554-Box-Verified-Enterprise-Supported-Apps) for your Box instance turned on, you may encounter an issue using the standard `account.box.com` base URL. Instead, use `ent.box.com` in place of `account.box.com`. ## 3. User grants application access Once the user is redirected to the Box web app they will have to log in. After they logged in they are presented with a screen to approve your application. When the user accepts this requests and clicks the button, the browser will redirect to your application's redirect URL as configured in the developer console. ## 4. Exchange code The user is redirected to your application's redirect URL with a query parameter containing a short-lived authorization code. ``` https://your.domain.com/path?code=1234567 ``` This code is not an [Access Token](g://authentication/tokens/access-tokens) and is only valid for a few seconds. The SDKs can be used to exchange the code for an actual Access Token. ``` var session = await sdk.Auth.AuthenticateAsync("[CODE]"); var client = new BoxClient(config, session); ``` ``` BoxAPIConnection client = new BoxAPIConnection( "[CLIENT_ID]", "[CLIENT_SECRET]", "[CODE]" ); ``` ``` auth.authenticate('[CODE]') client = Client(auth) ``` ``` var code = "..."; sdk.getTokensAuthorizationCodeGrant("[CODE]", null, function (err, tokenInfo) { var client = sdk.getPersistentClient(tokenInfo); }); ``` At the end of this flow, the application has an Access Token that can be used to make API calls on behalf of this user. ## Using SDKs and OAuth 2.0 To learn more about OAuth 2.0 authentication for each SDK head over to: [.Net](https://github.com/box/box-windows-sdk-v2/blob/main/docs/authentication.md#traditional-3-legged-oauth2) [Java](https://github.com/box/box-java-sdk/blob/main/doc/authentication.md#standard-3-legged-oauth-20) [Python](https://github.com/box/box-python-sdk/blob/main/docs/usage/authentication.md#traditional-3-legged-oauth2) [Node](https://github.com/box/box-node-sdk/blob/main/docs/authentication.md#traditional-3-legged-oauth2) [IOS](https://github.com/box/box-ios-sdk/blob/main/docs/usage/authentication.md#traditional-3-legged-oauth2) **Reference:** https://developer.box.com/guides/authentication/oauth2/with-sdk/ --- ## Untitled *Type: guide | Category: Authentication * OAuth 2.0 without SDKs Overview While leveraging an official Box SDK removes common hurdles of authentication, it is possible to use the Box… # OAuth 2.0 without SDKs ## Overview While leveraging an official Box SDK removes common hurdles of authentication, it is possible to use the Box APIs without one. This guide reviews the steps to manually complete the OAuth 2.0 flow. 1. Build the authorization URL 2. Redirect the user to the authorization URL 3. The user grants the application access to take actions on their behalf, which, if successful, provides an authorization code 4. Redirect the user back to the application 5. Exchange the authorization code for an Access Token At the end of this flow, the application has an [Access Token](g://authentication/tokens/access-tokens), which can be used to make API calls on behalf of the user. The Access Token acquired through the OAuth 2.0 flow is inherently tied to the user who authorized the application. It is possible to [act as another user](g://authentication/oauth2/as-user) using the `as-user` header. ## Prerequisites Before continuing you will need to complete the following steps: - Create a Platform App within the Box Developer Console, which leverages the OAuth 2.0 authentication method. - Navigate to the configuration tab for the application to copy the `client_id` and `client_secret` values. - Ensure at least one redirect URI is configured in the configuration tab for the application. ## 1. Build authorization URL An [authorization URL](e://get-authorize) is comprised of the following parameters: | Parameter | Status | Description | | --- | --- | --- | | CLIENT_ID | Required | Obtained from the configuration tab of the Developer Console | | REDIRECT_URI | Optional | Configured in the Developer Console and where the user is sent once granting access to the application | | RESPONSE_TYPE | Required | Always set to code | | STATE | Recommended | Protects against cross-site request forgery | If you configured multiple redirect URIs for the application, the authorization URL must include the `redirect_uri` parameter matching one of the URIs configured in the developer console. If the parameter is not specified, the user will see a `redirect_uri_missing` error and will not be redirected back to the app. At the minimum this URL will always use the format: `https://account.box.com/api/oauth2/authorize`?`client_id=CLIENTIDHERE`&`response_type=code` ``` var baseUrl = "https://account.box.com/api/oauth2/authorize"; var clientId = "[CLIENT_ID]"; var authorizationUrl = $"{baseUrl}?client_id={clientId}&response_type=code"; ``` ``` String baseUrl = "https://account.box.com/api/oauth2/authorize"; String clientId = "[CLIENT_ID]"; String authorizationUrl = String.format("%s?client_id=%s&response_type=code", baseUrl, clientId); ``` ``` base_url = 'https://account.box.com/api/oauth2/authorize' client_id = '[CLIENT_ID]' authorizationUrl = f'{base_url}?client_id=${client_id}&response_type=code' ``` ``` var baseUrl = "https://account.box.com/api/oauth2/authorize"; var clientId = "[CLIENT_ID]"; var authorizationUrl = `${baseUrl}?client_id=${clientId}&response_type=code`; ``` Learn more about the authorization URL If you have [Box Verified Enterprise](https://support.box.com/hc/en-us/articles/360043693554-Box-Verified-Enterprise-Supported-Apps) for your Box instance turned on, you may encounter an issue using the standard `account.box.com` base URL. Instead, use `ent.box.com` in place of `account.box.com`. ## 2. Redirect the user Next, redirect the user to the authorization URL. The way this is done depends on the application framework. Most framework documentation provides extensive guidance on this topic. If the authorization URL is not valid for the app specified, the user will see an error page rather than a grant access screen. For example, if the `redirect_uri` parameter in the authorization URL does not match one of the URIs configured for your app, the user will see a `redirect_uri_mismatch` error. ``` var authorizationUrl = $"{baseUrl}?client_id={clientId}&response_type=code"; // redirectTo(authorizationUrl); ``` ``` String authorizationUrl = String.format("%s?client_id=%s&response_type=code", baseUrl, clientId); // response.redirect(authorizationUrl); ``` ``` auth_url = f'{base_url}?client_id=${client_id}&response_type=code' // redirect(auth_url, code=302) ``` ``` var authorizationUrl = `${baseUrl}?client_id=${clientId}&response_type=code`; // res.redirect(authorize_url) ``` Additional query parameters can be passed along when redirecting the user to limit down the scope, or pass along some extra state. See the authorization reference documentation for more information. ## 3. User grants application access The user is redirected to their browser to log in to their account using the Box UI. They are then presented with the list of requested scopes and the option to approve the application to take actions on their behalf. When the user accepts this request by clicking **Grant access to Box**, the browser will redirect to the configured redirect URL with a query parameter containing a short-lived authorization code. If you configured multiple redirect URIs for the application, the authorization URL must include the `redirect_uri` parameter matching one of the URIs configured in the developer console. If the parameter is not specified, the user will see a `redirect_uri_missing` error and will not be redirected back to the app. ``` https://your.domain.com/path?code=1234567 ``` ## 4. Exchange code The provided authorization code is [valid for 30 seconds](g://api-calls/permissions-and-errors/expiration) and must be exchanged for an [Access Token](e://post-oauth2-token) before expiration. ``` using System.Net; using System.Net.Http; using Newtonsoft.Json; String authenticationUrl = "https://api.box.com/oauth2/token"; var client = new HttpClient(); var content = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>("grant_type", "authorization_code"), new KeyValuePair<string, string>("code", "[CODE]"), new KeyValuePair<string, string>("client_id", "[CLIENT_ID]"), new KeyValuePair<string, string>("client_secret", "[CLIENT_SECRET]") }); var response = client.PostAsync(authenticationUrl, content).Result; class Token { public string access_token { get; set; } } var data = response.Content.ReadAsStringAsync().Result; var token = JsonConvert.DeserializeObject<Token>(data); var accessToken = token.access_token; ``` ``` String authenticationUrl = "https://api.box.com/oauth2/token"; List<NameValuePair> params = new ArrayList<NameValuePair>(); params.add(new BasicNameValuePair("grant_type", "authorization_code")); params.add(new BasicNameValuePair("code", "[CODE]")); params.add(new BasicNameValuePair("client_id", "[CLIENT_ID]")); params.add(new BasicNameValuePair("client_secret", "[CLIENT_SECRET]")); CloseableHttpClient httpClient = HttpClientBuilder.create().disableCookieManagement().build(); HttpPost request = new HttpPost(authenticationUrl); request.setEntity(new UrlEncodedFormEntity(params)); CloseableHttpResponse httpResponse = httpClient.execute(request); HttpEntity entity = httpResponse.getEntity(); String response = EntityUtils.toString(entity); httpClient.close(); class Token { String access_token; } Token token = (Token) gson.fromJson(response, Token.class); String accessToken = token.access_token; ``` ``` authentication_url = "https://api.box.com/oauth2/token"; params = urlencode({ 'grant_type': 'authorization_code', 'code': '[CODE]', 'client_id': '[CLIENT_ID]', 'client_secret': '[CLIENT_SECRET]' }).encode() request = Request(authentication_url, params) response = urlopen(request).read() access_token = json.loads(response)['access_token'] ``` ``` const authenticationUrl = "https://api.box.com/oauth2/token"; let accessToken = await axios .post( authenticationUrl, querystring.stringify({ grant_type: "authorization_code", code: "[CODE]", client_id: "[CLIENT_ID]", client_secret: "[CLIENT_SECRET]", }) ) .then((response) => response.data.access_token); ``` To learn how to use an Access Token visit our guide on [Making API calls](g://api-calls). **Reference:** https://developer.box.com/guides/authentication/oauth2/without-sdk/ --- ## Untitled *Type: guide | Category: Authentication * Setup with App Token Auth A Limited Access App can be set up to use server-side App Tokens for authentication. Learn how App Token… # Setup with App Token Auth A Limited Access App can be set up to use server-side [App Tokens](g://authentication/app-token) for authentication. Learn how App Token authentication works ## Prerequisites To set up a Platform App using server-side authentication, you will need to ensure you have access the [Developer Console](https://app.box.com/developers/console) from your Box enterprise account. Alternatively, you may sign up for a [developer account](https://account.box.com/signup/n/developer). ## App creation steps ### 1. Log in to the Developer Console Log into Box and navigate to the [Developer Console](https://app.box.com/developers/console). Select **Create Platform App**. ### 2. Create a Platform App Select **Limited Access App** from the list of application types. A modal will appear to prompt the next step. ### 3. Select an app name Finally, select a unique name for your application and click **Create App**. ## App Authorization Once a keypair is successfully added to your application your Box enterprise Admin needs to authorize the application within the Box Admin Console. Navigate to the **General Settings** tab for your application within the [Developer Console](https://app.box.com/developers/console) and scroll down to the **App Authorization** section. Click **Submit and Review** to send an email to your Box enterprise Admin for approval. More information on this process is available in our [authorization guide](g://authorization). ## Basic configuration Before the application can be used, some basic additional configuration might be required. ### Primary and Secondary App Tokens Authentication with Limited Access Apps is done through preconfigured App Tokens. To configure an app token, navigate to the **Configuration** tab for your application within the [Developer Console](https://app.box.com/developers/console). Scroll down to the **Primary Access Token** section and click the **Generate Key** button. App tokens can be configured to automatically expire or be valid indefinitely. After creation, the key can be used to make [API calls](g://api-calls). # App authorization App Tokens can not be generated until the application is successfully authorized within the Box Admin Console. ### CORS Domains If your application makes API calls from front-end browser code in Javascript, the domain that these calls are made from will need to be added to an allow-list due to [Cross Origin Resource Sharing](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing), also known as CORS. If all requests will be made from server-side code, you may skip this section. To add the full URI(s) to the allow-list, navigate to the **CORS Domain** section at the bottom of the **Configuration** tab in the [Developer Console](https://app.box.com/developers/console). **Reference:** https://developer.box.com/guides/authentication/app-token/app-token-setup/ --- ## Untitled *Type: guide | Category: Authentication * Supported Endpoints App Token authentication currently only supports a handful of the BOX API. Endpoint Path Create a folder POST /folders… # Supported Endpoints App Token authentication currently only supports a handful of the BOX API. | Endpoint | Path | | --- | --- | | Create a folder | POST /folders | | Upload a file | POST /files/content | | Download a file | GET /files/:id/content | | Delete a file | DELETE /files/:id | | Get embed link | GET /files/:id | **Reference:** https://developer.box.com/guides/authentication/app-token/endpoints/ --- ## Untitled *Type: guide | Category: Authentication * App Token Auth Server-side authentication using App Tokens is an alternative way to authenticate to the Box API with fixed, long-lived… # App Token Auth Server-side authentication using App Tokens is an alternative way to authenticate to the Box API with fixed, long-lived Access Tokens that are restricted to the application's [Service Account](page://platform/user-types/#service-account). App Token Auth is intended to be used by applications leveraging [Box View](g://embed/box-view). ## App Token Restrictions A server-side App Token is an authentication method where the application only has access to read and write data to its own account. By using this authentication method there is no need to authorize a user as the application is automatically authenticated as the Service Account that belongs to that application. ## When to use App Tokens Server-side authentication with App Tokens is the ideal authentication method for apps that: - Want to leverage Box's preview services via [Box View](g://embed/box-view) - Work in an environment that either has no user model, or has users that don't have Box accounts - Want to use their own identity system - Don't want users to have to know that they are using Box - Want to store data in the application's Service Account and not a user's account **Reference:** https://developer.box.com/guides/authentication/app-token/ --- ## Untitled *Type: guide | Category: Authentication * App Tokens with SDKs The official Box SDKs have built-in support for App Token authentication. App Token authentication is designed for… # App Tokens with SDKs The official Box SDKs have built-in support for App Token authentication. App Token authentication is designed for working directly with the Box API without requiring a user to redirect through Box to authorize your application, yet is restricted to the application's own data. The method of authentication through JWT is inherently tied to the Service Account for the application. Any API call made with this token will seem to come from this application and will not have access to files and folders from other users without explicitly getting access them. ## Prerequisites Before we can get started, you will need to have completed the following steps. - Create a Box Application within the developer console - Ensure the application is configured to use App Token authentication - Generate a primary and secondary App Token for the application and store the tokens somewhere in your code. ## Initializing an SDK client To initialize an SDK client for app token auth, ensure the SDK is installed and then configure the SDK as follows. ``` var config = new BoxConfig("[CLIENT_ID]", "", new Uri("http://localhost")); var session = new OAuthSession("[APP_TOKEN]", "N/A", 3600, "bearer"); var client = new BoxClient(config, session); ``` ``` BoxTransactionalAPIConnection api = new BoxTransactionalAPIConnection("[APP_TOKEN]"); ``` ``` from boxsdk import Client, OAuth2 auth = OAuth2(access_token='[APP_TOKEN]') client = Client(auth) ``` ``` var BoxSDK = require('box-node-sdk'); var sdk = new BoxSDK({ clientID: '[CLIENT_ID]', clientSecret: '' }); var client = sdk.getBasicClient('[APP_TOKEN]'); ``` With this the application should be able to make API calls to any of the [endpoints](g://authentication/app-token/endpoints) enabled for App Token auth. ## Using SDKs and Application Tokens To learn more about Application Tokens for each SDK head over to: [.Net](https://github.com/box/box-windows-sdk-v2/blob/main/docs/authentication.md#box-view-authentication-with-app-tokens) [Java](https://github.com/box/box-java-sdk/blob/main/doc/authentication.md#box-view-authentication-with-app-token) [Python](https://github.com/box/box-python-sdk/blob/main/docs/usage/authentication.md#box-view-authentication-with-app-tokens) [Node](https://github.com/box/box-node-sdk/blob/main/docs/authentication.md#box-view-authentication-with-app-tokens) **Reference:** https://developer.box.com/guides/authentication/app-token/with-sdk/ --- ## Untitled *Type: guide | Category: Authentication * Rotating App Tokens By updating one of the application's App Tokens at a time the application can rotate the tokens without running into any… # Rotating App Tokens By updating one of the application's App Tokens at a time the application can rotate the tokens without running into any conflicts. ## Why rotate tokens There are a few reasons to rotate App Tokens on a fixed interval. 1. To replace tokens that have been configured to auto-expire 2. To limit the effects of any compromised tokens In either case, Box supports having two active App Tokens at any time, allowing for a seamless rotation from the old to the new tokens. ## Rotation steps These instructions assume that you have already created a primary and secondary App Token before and are ready to replace either of them. By following these steps you can configure your application with two new tokens without any issues. 1. Assuming your application is using the Primary App Token, go to [developer console](https://app.box.com/developers/console) application. Head to the "Configuration" section of your application "Generate Key" button for the Secondary App Token. 2. Update your application with the Secondary Token. Ensure your application is configured with this new token completely before moving on to the next step. 3. Once you are confident that the Primary App token is no longer in use, head over to the [developer console](https://app.box.com/developers/console) and click the "Revoke" button for the Primary App Token. Repeat the same process with the tokens switched to roll back from the Secondary App Token to the Primary App Token. **Reference:** https://developer.box.com/guides/authentication/app-token/rollover/ --- ## Untitled *Type: guide | Category: Authentication * App Tokens without SDKs If you are not ready to use any of the official Box SDKs, or an SDK is not available in your language of choice, it… # App Tokens without SDKs If you are not ready to use any of the official Box SDKs, or an SDK is not available in your language of choice, it is totally possible to use the Box APIs without them. App Token authentication is designed for working directly with the Box API without requiring a user to redirect through Box to authorize your application, yet is restricted to the application's own data. The method of authentication through JWT is inherently tied to the Service Account for the application. Any API call made with this token will seem to come from this application and will not have access to files and folders from other users without explicitly getting access them. ## Prerequisites Before we can get started, you will need to have completed the following steps. - Create a Box Application within the developer console - Ensure the application is configured to use App Token authentication - Generate a primary and secondary App Token for the application and store the tokens somewhere in your code. ## Making API calls To use an App Token directly the application can use the App Token the same way it would use any Access Token. ``` curl https://api.box.com/2.0/users/me \ -H "authorization: Bearer EGmDmRVfhfHsqesn5yVYHAqUkD0dyDfk" ``` **Reference:** https://developer.box.com/guides/authentication/app-token/without-sdk/ --- ## Untitled *Type: guide | Category: Applications * Create Web App Integration This guide explains how to set up a Web App Integration with a Platform App. Server-side integration is no longer… # Create Web App Integration This guide explains how to set up a Web App Integration with a Platform App. Server-side integration is no longer supported. This means the applications using server-side actions will still be working, but you won't be able to modify the server-side configuration options such as Preliminary Callback URL or Basic Authentication. You will be able to deactivate them and change the implementation to a new one. ## Create an OAuth 2.0 Application Navigate to the [Developer Console](https://app.box.com/developers/console) and create a [Platform App](g://applications/app-types/platform-apps) that leverages [OAuth 2.0 authentication](g://authentication/oauth2/oauth2-setup). ## Create a New Integration Then, navigate to the **Integrations** tab and click **Create a Web App Integration**. ## Configure Integration To configure the integration, follow the guidance below for each value. ### App Info | Field | Description | | --- | --- | | Integration name | The name of your integration, which users see in the Box Web App when they select the More Options > Integrations menu on a file or folder. | | Description | The description of the integration displayed in the Box Integrations. | | Supported file extensions | The integration will only appear as an option in the More Options > Integrations menu for the selected file extensions. | | Permissions requirement | Determines what permissions level users need to see the integration. Download permissions are required allows users to download the file - they will not be able to update it. Full permissions are required allows users to download and update the file. | | Integration scopes | Specifies the scope of your integration - either the file/folder from which integration is invoked, or its parent folder. | | Display on shared pages toggle | Determines if an integration can be shown to external users on a shared page. If enabled, users who are not collaborating on the content will see the integration in the context-menu when accessing the items through a shared link. | | Lock to only allow the current user to overwrite the file using your integration toggle | Determines if different web app integrations can edit the file at the same time. | | Integration type | Select desired integration type. Available options are: Files, Folders, Both. | ### Callback Configuration | Field | Description | | --- | --- | | Client Callback URL | Handles additional callback requests from Box after the primary request with Popup Integrations. If the application specifies a file parameter in the REST method, the preliminary callback URL cannot originate from the client. As a result, a second request must be made from the client to your server so the server can send the necessary interface to the user. | | Prompt Message | Specifies the message that users see when they initiate the integration. Use this field to provide context about what happens next. The message is limited to 500 characters. | | User Experience | Informs that the integration will open in a new window. | | New Window Settings | Determines if the application opens in a new tab. | ### Callback Parameters The **Callback Parameters** section configures the parameters that Box sends to the callback URL when a user accepts a confirmation prompt. If this setting is not configured, Box does not send any parameters to the callback URL. To add a parameter, select the **Method** (GET or POST), specify the **Parameter name** and add a **Parameter value**. The **File** method is no longer supported. If you already used this method, you cannot edit its values. You can change the **File** method to **GET** or **POST**, but you can't undo this action. For example: **GET - `userid` - `#user_id#`**. The following parameter values are available. | Parameter | Method | Description | | --- | --- | --- | | user_id | GET, POST | The Box user ID. This information is used in Popup Integrations in which user authentication is required to complete an action. You can store the Box ID in your application to enable subsequent authentication requests from the integration. | | user_name | POST | The full name or email address of the Box user. Not all Box users specify their names at all times. | | file_id | GET, POST | The Box file ID. You can use this ID to make Box API calls that affect the file. | | file_name | POST | The name of the file. | | file_extension | GET, POST | The extension of the file. | | auth_code | GET, POST | The OAuth 2.0 authorization code, which is generated by Box upon successful authentication. Your application must then supply this authorization code to Box in exchange for an OAuth 2.0 Access Token. An authorization header containing a valid Access Token must be included in every Box API request. | | redirect_to_box_url | GET, POST | In Popup Integrations, the URL to which requests are sent by the confirmation prompt. Use this URL to redirect users to the All Files page. This parameter closes the popup panel and refreshes the All Files page to reflect any changes performed by the integration. If you do not want to add this parameter to your application, you can specify the entire URL. Success: #redirect_to_box_url#&status=success&message=Your%20action%20was%20successful%2E. Failure: #redirect_to_box_url#&status=failure&message=Your%20action%20was%20unsuccessful%2E | ### Integration Status - **Development**: The integration is visible and available only to application collaborators listed under the **General Settings** tab. This option is best used when the application is still in development and undergoing testing. - **Online**: The integration is visible and available to all Box users. This option is best used when development is complete and the application is ready to publish in the Integrations. - **Maintenance**: The integration is visible and available only to application collaborators listed under the **General Settings** tab. This option is best used after the integration is published in the Integrations, but needs to perform maintenance updates or troubleshoot issues. Use this option to temporarily take the integration offline for everyone except the application's collaborators. ## Example Use Cases of Box Integrations When a user chooses a Popup Integration, Box sends a callback request to the primary callback URL. It sends the callback parameters have been configured to the server. In some cases, Box may make a second request if the client cannot get all the data it needs from the first request. The following example does not require a client callback URL: - The Popup Integration performs a REST call using a `download_file_url` callback parameter. - The user clicks **OK** in the confirmation prompt to accept the popup. - Box sends a request to the following URL (the primary callback URL plus the callback parameter): `http://www.doceditor.com/service?apikey=abc&file=&redirect=`. - The response from the callback URL displays a user interface to the user who made the request. The popup has all the information needed to continue the action and an additional client callback is not needed. The following example requires a client callback URL: - The Popup Integration performs a REST call using a file-callback parameter. - The user clicks **OK** in the confirmation prompt to accept the popup. - The popup displays a page where Box sends a POST request with the contents of a file, along with the callback parameters to the remote server. - Box receives the response from the remote server and directs the client to POST the response to the client callback URL. The server identified by the URL interprets the response and redirects the user with the correct session ID. ## Client-callback URL Request Format The POST request that Box sends to the client callback URL takes the response from the primary callback URL and forwards it to the same URL along with the same data as the original callback. | Client Callback URL | Example | | --- | --- | | Two GET parameters and one POST parameter: http://your-client-callback-url.com/?get_param1=value1&get_param2=value2 | POST data: post_param1=value1initial_callback_response | The response to the client-callback request is an HTTP status 302, redirecting the user to the correct URL or to the HTML for a UI. Most often the URL points to a separate API or custom script developed for Web App Integrations, which parses the result of the primary callback URL. Also, note that the URL must be publicly accessible on the internet. ## Making Integration Publicly Available To make a Box integration publicly available it needs to be listed in the App Center. Follow the [Integrations](g://applications/integrations) guide for more details. **Reference:** https://developer.box.com/guides/applications/web-app-integrations/configure/ --- ## Untitled *Type: guide | Category: Applications * Web App Integration Web App Integrations allow third-party applications to become part of the Box user experience by allowing users to use… # Web App Integration Web App Integrations allow third-party applications to become part of the Box user experience by allowing users to use such third-party applications when editing or sharing files. ## Features - **File interaction**. Users can modify, share, or edit content stored in Box using a third-party application. - **Recommended Apps support**. Integrations can appear in the Box Preview interface under **Recommended Apps**. For details, see [Recommended Web Integrations](https://support.box.com/hc/en-us/articles/360044195533-Installing-Recommended-Apps-in-your-Enterprise). - **Scoped availability**. Integrations can be restricted to certain content types and file extensions. ## Visibility in Recommended Apps Your web application integration appears in **Recommended Apps** only if it is published in Integrations. Learn how to create a Web App Integration **Reference:** https://developer.box.com/guides/applications/web-app-integrations/ --- ## Untitled *Type: guide | Category: Applications * Integrations Types Currently, Box provides the Popup integration type. Popup Integrations In a popup integration, Box opens a panel and… # Integrations Types Currently, Box provides the Popup integration type. ## Popup Integrations In a popup integration, Box opens a panel and loads the application's callback URL configured for the integration. The application can display its own user interface for the integration in the popup. The integration receives a short-lived authorization code with this request, which can be used to connect to the Box APIs, exchange the code for an Access Token, and then use that to make API calls to Box. Popup panels use HTML `<iframe>` tags to display the embedded content. To protect the security of Box's content, callback URLs should use SSL, and the response from the callback URL should include an `X-Frame-Options` header set to a random string value. **Reference:** https://developer.box.com/guides/applications/web-app-integrations/types/ --- ## Untitled *Type: guide | Category: Applications * User Experience The following explains what a Web App Integration looks like from a user's point of view. Box users can discover… # User Experience The following explains what a Web App Integration looks like from a user's point of view. 1. Box users can discover applications in the Box Integrations. They can visit the Integrations directly, or select the **Integrations** menu from the Box Web App UI. 2. In the Integrations they select the application they would like to add. Every published Box application has a public profile page, which provides details including the integrations it provides. 3. They user adds the application to their account by clicking the **Add** button in the Integrations listing. Once an application is added to an account, all its integrations become available to the user. 4. Select an integration from the **More Actions** menu on a file or folder. 5. Box will ask the user for permission to share the file or folder with the application. The confirmation prompt must accepted before the integration can be successfully used. 6. Upon granting access, Box passes the data to the application. Depending on the type of integration, the application will then display a popup panel or run a server-based process. **Reference:** https://developer.box.com/guides/applications/web-app-integrations/user-experience/ --- ## Untitled *Type: guide | Category: Box AI * Override AI model configuration The ai_agent configuration allows you to override the default AI model configuration. It is available for… # Override AI model configuration The `ai_agent` configuration allows you to override the default AI model configuration. It is available for the following endpoints: - [`POST ai/ask`](e://post_ai_ask#param_ai_agent) - [`POST ai/text_gen`](e://post_ai_text_gen#param_ai_agent) - [`POST ai/extract`](e://post_ai_extract#param_ai_agent) - [`POST ai/extract_structured`](e://post_ai_extract_structured#param_ai_agent) Use the [`GET ai_agent_default`](e://get_ai_agent_default) endpoint to fetch the default configuration. The override examples include: - Replacing the default AI model with a custom one based on your organization's needs. - Tweaking the base `prompt` to allow a more customized user experience. - Changing a parameter, such as `temperature`, to make the results more or less creative. ## Sample configuration A complete configuration for `ai/ask` is as follows: ``` { "type": "ai_agent_ask", "basic_text": { "llm_endpoint_params": { "type": "openai_params", "frequency_penalty": 1.5, "presence_penalty": 1.5, "stop": "<|im_end|>", "temperature": 0, "top_p": 1 }, "model": "azure__openai__gpt_4o_mini", "num_tokens_for_completion": 8400, "prompt_template": "It is `{current_date}`, consider these travel options `{content}` and answer the `{user_question}`.", "system_message": "You are a helpful travel assistant specialized in budget travel" }, "basic_text_multi": { "llm_endpoint_params": { "type": "openai_params", "frequency_penalty": 1.5, "presence_penalty": 1.5, "stop": "<|im_end|>", "temperature": 0, "top_p": 1 }, "model": "azure__openai__gpt_4o_mini", "num_tokens_for_completion": 8400, "prompt_template": "It is `{current_date}`, consider these travel options `{content}` and answer the `{user_question}`.", "system_message": "You are a helpful travel assistant specialized in budget travel" }, "long_text": { "embeddings": { "model": "openai__text_embedding_ada_002", "strategy": { "id": "basic", "num_tokens_per_chunk": 64 } }, "llm_endpoint_params": { "type": "openai_params", "frequency_penalty": 1.5, "presence_penalty": 1.5, "stop": "<|im_end|>", "temperature": 0, "top_p": 1 }, "model": "azure__openai__gpt_4o_mini", "num_tokens_for_completion": 8400, "prompt_template": "It is `{current_date}`, consider these travel options `{content}` and answer the `{user_question}`.", "system_message": "You are a helpful travel assistant specialized in budget travel" }, "long_text_multi": { "embeddings": { "model": "openai__text_embedding_ada_002", "strategy": { "id": "basic", "num_tokens_per_chunk": 64 } }, "llm_endpoint_params": { "type": "openai_params", "frequency_penalty": 1.5, "presence_penalty": 1.5, "stop": "<|im_end|>", "temperature": 0, "top_p": 1 }, "model": "azure__openai__gpt_4o_mini", "num_tokens_for_completion": 8400, "prompt_template": "It is `{current_date}`, consider these travel options `{content}` and answer the `{user_question}`.", "system_message": "You are a helpful travel assistant specialized in budget travel" } } ``` ### Differences in parameter sets The set of parameters available for `ask`, `text_gen`, `extract`, `extract_structured` differs slightly, depending on the API call. The agent configuration for the `ask` endpoint includes `basic_text`, `basic_text_multi`, `long_text` and `long_text_multi` parameters. This is because of the `mode` parameter you use to specify if the request is for a single item or multiple items. If you selected `multiple_item_qa` as the `mode`, you can also use `multi` parameters for overrides. The agent configuration for `text_gen` includes the `basic_gen` parameter that is used to generate text. ### LLM endpoint params The `llm_endpoint_params` configuration options differ depending on the overall AI model being [Google](r://ai-llm-endpoint-params-google), [OpenAI](r://ai-llm-endpoint-params-openai) or [AWS](r://ai-llm-endpoint-params-aws) based. For example, both `llm_endpoint_params` objects accept a `temperature` parameter, but the outcome differs depending on the model. For Google and AWS models, the [`temperature`](https://ai.google.dev/gemini-api/docs/models/generative-models#model-parameters) is used for sampling during response generation, which occurs when `top-P` and `top-K` are applied. Temperature controls the degree of randomness in the token selection. For OpenAI models, [`temperature`](https://community.openai.com/t/temperature-top-p-and-top-k-for-chatbot-responses/295542) is the sampling temperature with values between 0 and 2. Higher values like 0.8 make the output more random, while lower values like 0.2 make it more focused and deterministic. When introducing your own configuration, use `temperature` or or `top_p` but not both. ### System message The `system_message` parameter's aim is to help the LLM understand its role and what it’s supposed to do. For example, if your solution is processing travel itineraries, you can add a system message saying: ``` You are a travel agent aid. You are going to help support staff process large amounts of schedules, tickets, etc. ``` This message is separate from the content you send in, but it can improve the results. ### Number of tokens for completion The `num_tokens_for_completion` parameter represents the number of [tokens](https://help.openai.com/en/articles/4936856-what-are-tokens-and-how-to-count-them) Box AI can return. This number can vary based on the model used. **Reference:** https://developer.box.com/guides/box-ai/ai-agents/ai-agent-overrides/ --- ## Untitled *Type: guide | Category: Box AI * AI agent configuration versioning Box updates the default models across the endpoints on a regular basis in order to stay up to date with… # AI agent configuration versioning Box updates the default models across the endpoints on a regular basis in order to stay up to date with the most advanced options. If a default model is updated, it will be posted in the developer changelog. AI agent configuration versioning gives the developers more control over AI agent versioning and ensures consistent responses. AI agent configuration versioning adopts the following principles: - Each AI agent snapshot is supported for at least 12 months, unless there are factors outside of Box's control. For example, a Large Language Model (LLM) may get deprecated. - An AI agent snapshot is available unless a new, stable agent version is released - A 6-month window is provided to test and transition to the new snapshot. ## Historical AI agent configuration The values in the [default agent configuration](g://box-ai/ai-agents/get-agent-default-config) used by the LLM gateway often change to achieve the best possible answer quality. To make sure your configurations are not affected in a negative way, you can use the historical AI agent configuration provided below to [override the default one](g://box-ai/ai-agents/ai-agent-overrides). ``` { "ask": { "type": "ai_agent_ask", "longText": { "model": "azure__openai__gpt_4o_mini", "systemMessage": "", "promptTemplate": "Reply as if it's {current_date}.\nI will ask you for help and provide subsections of one document delimited by five backticks (`````) at the beginning and at the end.\nIf I make a reference to \"this\", I am referring to the document I provided between the five backticks. I may ask you a question where the answer is contained within the document. In that case, do your best to answer using only the document, but if you cannot, feel free to mention that you couldn't find an answer in the document, but you have some answer from your general knowledge.\nI may ask you to perform some kind of computation or symbol manipulation such as filtering a list, counting something, summing, averaging, and other aggregation/grouping functions or some combination of them. In these cases, first list the plan of how you plan to perform such a computation, then follow that plan step by step, keeping track of intermediate results, and at the end tell me the final answer.\nI may ask you to enumerate or somehow list people, places, characters, or other important things from the document, if I do so, please only use the document provided to list them.\nTEXT FROM DOCUMENT STARTS\n`````\n{content}\n `````\nTEXT FROM DOCUMENT ENDS\nNever mention five backticks in your response. Unless you are told otherwise, a one paragraph response is sufficient for any requested summarization tasks.\nHere is how I need help from you: {user_question}", "numTokensForCompletion": 6000, "llmEndpointParams": { "type": "openai_params", "temperature": 0.0, "topP": 1.0, "frequencyPenalty": 0.0, "presencePenalty": 1.5, "stop": "<|im_end|> " }, "embeddings": { "model": "azure__openai__text_embedding_ada_002", "strategy": { "id": "basic", "numTokensPerChunk": 64 } } }, "basicText": { "model": "azure__openai__gpt_4o_mini", "systemMessage": "", "promptTemplate": "Reply as if it's {current_date}.\nI will ask you for help and provide the entire text of one document delimited by five backticks (`````) at the beginning and at the end.\nIf I make a reference to \"this\", I am referring to the document I provided between the five backticks. I may ask you a question where the answer is contained within the document. In that case, do your best to answer using only the document, but if you cannot, feel free to mention that you couldn't find an answer in the document, but you have some answer from your general knowledge.\nI may ask you to perform some kind of computation or symbol manipulation such as filtering a list, counting something, summing, averaging, and other aggregation/grouping functions or some combination of them. In these cases, first list the plan of how you plan to perform such a computation, then follow that plan step by step, keeping track of intermediate results, and at the end tell me the final answer.\nI may ask you to enumerate or somehow list people, places, characters, or other important things from the document, if I do so, please only use the document provided to list them.\nTEXT FROM DOCUMENT STARTS\n `````\n{content}\n`````\nTEXT FROM DOCUMENT ENDS\nNever mention five backticks in your response. Unless you are told otherwise, a one paragraph response is sufficient for any requested summarization tasks.\nHere is how I need help from you: {user_question}", "numTokensForCompletion": 6000, "llmEndpointParams": { "type": "openai_params", "temperature": 0.0, "topP": 1.0, "frequencyPenalty": 0.0, "presencePenalty": 1.5, "stop": "<|im_end|>" } }, "longTextMulti": { "model": "azure__openai__gpt_4o_mini", "systemMessage": "Role and Goal: You are an assistant designed to analyze and answer a question based on provided snippets from multiple documents, which can include business-oriented documents like docs, presentations, PDFs, etc. The assistant will respond concisely, using only the information from the provided documents.\n\nConstraints: The assistant should avoid engaging in chatty or extensive conversational interactions and focus on providing direct answers. It should also avoid making assumptions or inferences not supported by the provided document snippets.\n\nGuidelines: When answering, the assistant should consider the file's name and path to assess relevance to the question. In cases of conflicting information from multiple documents, it should list the different answers with citations. For summarization or comparison tasks, it should concisely answer with the key points. It should also consider the current date to be the date given.\n\nPersonalization: The assistant's tone should be formal and to-the-point, suitable for handling business-related documents and queries.\n", "promptTemplate": "Current date: {current_date}\n\nTEXT FROM DOCUMENTS STARTS\n{content}\nTEXT FROM DOCUMENTS ENDS\n\nHere is how I need help from you: {user_question}\n.", "numTokensForCompletion": 6000, "llmEndpointParams": { "type": "openai_params", "temperature": 0.0, "topP": 1.0, "frequencyPenalty": 0.0, "presencePenalty": 1.5, "stop": "<|im_end|>" }, "embeddings": { "model": "azure__openai__text_embedding_ada_002", "strategy": { "id": "basic", "numTokensPerChunk": 64 } } }, "basicTextMulti": { "model": "azure__openai__gpt_4o_mini", "systemMessage": "", "promptTemplate": "Current date: {current_date}\n\nTEXT FROM DOCUMENTS STARTS\n{content}\nTEXT FROM DOCUMENTS ENDS\n\nHere is how I need help from you: {user_question}\n.", "numTokensForCompletion": 6000, "llmEndpointParams": { "type": "openai_params", "temperature": 0.0, "topP": 1.0, "frequencyPenalty": 0.0, "presencePenalty": 1.5, "stop": "<|im_end|>" } }, }, "extract": { "type": "ai_agent_extract", "longText": { "model": "google__gemini_1_5_flash_001", "systemMessage": "Respond only in valid json. You are extracting metadata that is name, value pairs from a document. Only output the metadata in valid json form, as {\"name1\":\"value1\",\"name2\":\"value2\"} and nothing else. You will be given the document data and the schema for the metadata, that defines the name, description and type of each of the fields you will be extracting. Schema is of the form {\"fields\": [{\"key\": \"key_name\", \"displayName\": \"key display name\", \"type\": \"string\", \"description\": \"key description\"}]}. Leverage key description and key display name to identify where the key and value pairs are in the document. In certain cases, key description can also indicate the instructions to perform on the document to obtain the value. Prompt will be in the form of Schema is ``schema`` \n document is````document````", "promptTemplate": "If you need to know today's date to respond, it is {current_date}. Schema is ``{user_question}`` \n document is````{content}````", "numTokensForCompletion": 4096, "llmEndpointParams": { "type": "google_params", "temperature": 0.0, "topP": 1.0, "frequencyPenalty": 0.0, "presencePenalty": 0.0 }, "embeddings": { "model": "azure__openai__text_embedding_ada_002", "strategy": { "id": "basic", "numTokensPerChunk": 64 } } }, "basicText": { "model": "google__gemini_1_5_flash_001", "systemMessage": "Respond only in valid json. You are extracting metadata that is name, value pairs from a document. Only output the metadata in valid json form, as {\"name1\":\"value1\",\"name2\":\"value2\"} and nothing else. You will be given the document data and the schema for the metadata, that defines the name, description and type of each of the fields you will be extracting. Schema is of the form {\"fields\": [{\"key\": \"key_name\", \"displayName\": \"key display name\", \"type\": \"string\", \"description\": \"key description\"}]}. Leverage key description and key display name to identify where the key and value pairs are in the document. In certain cases, key description can also indicate the instructions to perform on the document to obtain the value. Prompt will be in the form of Schema is ``schema`` \n document is````document````", "promptTemplate": "If you need to know today's date to respond, it is {current_date}. Schema is ``{user_question}`` \n document is````{content}````", "numTokensForCompletion": 4096, "llmEndpointParams": { "type": "google_params", "temperature": 0.0, "topP": 1.0, "frequencyPenalty": 0.0, "presencePenalty": 0.0 } } }, "textGen": { "type": "ai_agent_text_gen", "basicGen": { "model": "azure__openai__gpt_4o_mini", "systemMessage": "\nIf you need to know today's date to respond, it is {current_date}.\nThe user is working in a collaborative document creation editor called Box Notes.\nAssume that you are helping a business user create documents or to help the user revise existing text.\nYou can help the user in creating templates to be reused or update existing documents, you can respond with text that the user can use to place in the document that the user is editing.\nIf the user simply asks to \"improve\" the text, then simplify the language and remove jargon, unless the user specifies otherwise.\nDo not open with a preamble to the response, just respond.\n", "promptTemplate": "{user_question}", "numTokensForCompletion": 12000, "llmEndpointParams": { "type": "openai_params", "temperature": 0.1, "topP": 1.0, "frequencyPenalty": 0.75, "presencePenalty": 0.75, "stop": "<|im_end|>" }, "embeddings": { "model": "azure__openai__text_embedding_ada_002", "strategy": { "id": "basic", "numTokensPerChunk": 64 } }, "contentTemplate": "`````{content}`````" } }, "extractStructured": { "type": "ai_agent_extract_structured", "longText": { "model": "google__gemini_1_5_flash_001", "systemMessage": "Respond only in valid json. You are extracting metadata that is name, value pairs from a document. Only output the metadata in valid json form, as {\"name1\":\"value1\",\"name2\":\"value2\"} and nothing else. You will be given the document data and the schema for the metadata, that defines the name, description and type of each of the fields you will be extracting. Schema is of the form {\"fields\": [{\"key\": \"key_name\", \"prompt\": \"prompt to extract the value\", \"type\": \"date\"}]}. Leverage prompt for each key to identify where the key and value pairs are in the document. In certain cases, prompt can also indicate the instructions to perform on the document to obtain the value. Prompt will be in the form of Schema is ``schema`` \n document is````document````", "promptTemplate": "If you need to know today's date to respond, it is {current_date}. Schema is ``{user_question}`` \n document is````{content}````", "numTokensForCompletion": 4096, "llmEndpointParams": { "type": "google_params", "temperature": 0.0, "topP": 1.0, "frequencyPenalty": 0.0, "presencePenalty": 0.0 }, "embeddings": { "model": "google__textembedding_gecko_003", "strategy": { "id": "basic", "numTokensPerChunk": 64 } } }, "basicText": { "model": "google__gemini_1_5_flash_001", "systemMessage": "Respond only in valid json. You are extracting metadata that is name, value pairs from a document. Only output the metadata in valid json form, as {\"name1\":\"value1\",\"name2\":\"value2\"} and nothing else. You will be given the document data and the schema for the metadata, that defines the name, description and type of each of the fields you will be extracting. Schema is of the form {\"fields\": [{\"key\": \"key_name\", \"prompt\": \"prompt to extract the value\", \"type\": \"date\"}]}. Leverage prompt for each key to identify where the key and value pairs are in the document. In certain cases, prompt can also indicate the instructions to perform on the document to obtain the value. Prompt will be in the form of Schema is ``schema`` \n document is````document````", "promptTemplate": "If you need to know today's date to respond, it is {current_date}. Schema is ``{user_question}`` \n document is````{content}````", "numTokensForCompletion": 4096, "llmEndpointParams": { "type": "google_params", "temperature": 0.0, "topP": 1.0, "frequencyPenalty": 0.0, "presencePenalty": 0.0 } } } } ``` **Reference:** https://developer.box.com/guides/box-ai/ai-agents/ai-agent-versioning/ --- ## Untitled *Type: guide | Category: Box AI * AI model overrides Box updates the default models across the endpoints on a regular basis to stay up to date with the most advanced options… # AI model overrides Box updates the default models across the endpoints on a regular basis to stay up to date with the most advanced options. If your implementation is based on Box AI, a new default model might alter the results in a way that could break or change a downstream process. Switching to a specific version may prevent encountering any issues. Selecting a specific model may also bring better results to your use case. This is why, you can switch to any model included in the [supported models](g://box-ai/ai-models) list. Apart from switching models, you can pass options to further customize the agents used in Box AI implementation and get the responses that suit your use case. To see specific use cases, check the [overrides tutorial](g://box-ai/ai-agents/ai-agent-overrides). **Reference:** https://developer.box.com/guides/box-ai/ai-agents/ --- ## Untitled *Type: guide | Category: Box AI * Get default AI agent configuration The GET /2.0/ai_agent_default endpoint allows you to fetch the default configuration for AI services… # Get default AI agent configuration The `GET /2.0/ai_agent_default` endpoint allows you to fetch the default configuration for AI services. Once you get the configuration details you can override them using the [`ai_agent`](g://box-ai/ai-agents/ai-agent-overrides) parameter. ## Send a request To send a request, use the `GET /2.0/ai_agent_default` endpoint. Make sure you have generated the developer token to authorize your app. See [getting started with Box AI](g://box-ai/ai-tutorials/prerequisites) for details. ### Parameters To make a call, you must pass the following parameters. Mandatory parameters are in **bold**. | Parameter | Description | Example | | --- | --- | --- | | language | The language code the agent configuration is returned for. If the language is not supported, the default configuration is returned. | ja-JP | | mode | The mode used to filter the agent configuration. The value can be ask, text_gen, extract, or extract_structured depending on the result you want to achieve. | ask | | model | The model you want to get the configuration for. To make sure your chosen model is supported, see the list of models. | azure__openai__gpt_4o_mini | ## Responses The responses to the call may vary depending on the `mode` parameter value you choose. When you set the `mode` parameter to `ask` the response will be as follows: ``` { "type": "ai_agent_ask", "basic_text": { "model": "azure__openai__gpt_4o_mini", "system_message": "", "prompt_template": "prompt_template": "{user_question}Write it in an informal way.{content}" }, "num_tokens_for_completion": 6000, "llm_endpoint_params": { "temperature": 0, "top_p": 1, "frequency_penalty": 0, "presence_penalty": 1.5, "stop": "<|im_end|>", "type": "openai_params" } }, "long_text": { "model": "azure__openai__gpt_4o_mini", "system_message": "", "prompt_template": "prompt_template": "{user_question}Write it in an informal way.{content}" }, "num_tokens_for_completion": 6000, "llm_endpoint_params": { "temperature": 0, "top_p": 1, "frequency_penalty": 0, "presence_penalty": 1.5, "stop": "<|im_end|>", "type": "openai_params" }, "embeddings": { "model": "azure__openai__text_embedding_ada_002", "strategy": { "id": "basic", "num_tokens_per_chunk": 64 } } }, "basic_text_multi": { "model": "azure__openai__gpt_4o_mini", "system_message": "", "prompt_template": "Current date: {current_date}\n\nTEXT FROM DOCUMENTS STARTS\n{content}\nTEXT FROM DOCUMENTS ENDS\n\nHere is how I need help from you: {user_question}\n.", "num_tokens_for_completion": 6000, "llm_endpoint_params": { "temperature": 0, "top_p": 1, "frequency_penalty": 0, "presence_penalty": 1.5, "stop": "<|im_end|>", "type": "openai_params" } }, "long_text_multi": { "model": "azure__openai__gpt_4o_mini", "system_message": "Role and Goal: You are an assistant designed to analyze and answer a question based on provided snippets from multiple documents, which can include business-oriented documents like docs, presentations, PDFs, etc. The assistant will respond concisely, using only the information from the provided documents.\n\nConstraints: The assistant should avoid engaging in chatty or extensive conversational interactions and focus on providing direct answers. It should also avoid making assumptions or inferences not supported by the provided document snippets.\n\nGuidelines: When answering, the assistant should consider the file's name and path to assess relevance to the question. In cases of conflicting information from multiple documents, it should list the different answers with citations. For summarization or comparison tasks, it should concisely answer with the key points. It should also consider the current date to be the date given.\n\nPersonalization: The assistant's tone should be formal and to-the-point, suitable for handling business-related documents and queries.\n", "prompt_template": "Current date: {current_date}\n\nTEXT FROM DOCUMENTS STARTS\n{content}\nTEXT FROM DOCUMENTS ENDS\n\nHere is how I need help from you: {user_question}\n.", "num_tokens_for_completion": 6000, "llm_endpoint_params": { "temperature": 0, "top_p": 1, "frequency_penalty": 0, "presence_penalty": 1.5, "stop": "<|im_end|>", "type": "openai_params" }, "embeddings": { "model": "azure__openai__text_embedding_ada_002", "strategy": { "id": "basic", "num_tokens_per_chunk": 64 } } } } ``` When you set the `mode` parameter to `text_gen` the response will be as follows: ``` { "type": "ai_agent_text_gen", "basic_gen": { "model": "azure__openai__gpt_4o_mini", "system_message": "\nIf you need to know today's date to respond, it is {current_date}.\nThe user is working in a collaborative document creation editor called Box Notes.\nAssume that you are helping a business user create documents or to help the user revise existing text.\nYou can help the user in creating templates to be reused or update existing documents, you can respond with text that the user can use to place in the document that the user is editing.\nIf the user simply asks to \"improve\" the text, then simplify the language and remove jargon, unless the user specifies otherwise.\nDo not open with a preamble to the response, just respond.\n", "prompt_template": "{user_question}", "num_tokens_for_completion": 12000, "llm_endpoint_params": { "temperature": 0.1, "top_p": 1, "frequency_penalty": 0.75, "presence_penalty": 0.75, "stop": "<|im_end|>", "type": "openai_params" }, "embeddings": { "model": "azure__openai__text_embedding_ada_002", "strategy": { "id": "basic", "num_tokens_per_chunk": 64 } }, "content_template": "`````{content}`````" } } ``` When you set the `mode` parameter to `extract` the response will be as follows: ``` { "type": "ai_agent_extract", "basic_text": { "model": "google__gemini_1_5_flash_001", "system_message": "Respond only in valid json. You are extracting metadata that is name, value pairs from a document. Only output the metadata in valid json form, as {\"name1\":\"value1\",\"name2\":\"value2\"} and nothing else. You will be given the document data and the schema for the metadata, that defines the name, description and type of each of the fields you will be extracting. Schema is of the form {\"fields\": [{\"key\": \"key_name\", \"displayName\": \"key display name\", \"type\": \"string\", \"description\": \"key description\"}]}. Leverage key description and key display name to identify where the key and value pairs are in the document. In certain cases, key description can also indicate the instructions to perform on the document to obtain the value. Prompt will be in the form of Schema is ``schema`` \n document is````document````", "prompt_template": "If you need to know today's date to respond, it is {current_date}. Schema is ``{user_question}`` \n document is````{content}````", "num_tokens_for_completion": 4096, "llm_endpoint_params": { "temperature": 0, "top_p": 1, "top_k": null, "type": "google_params" } }, "long_text": { "model": "google__gemini_1_5_flash_001", "system_message": "Respond only in valid json. You are extracting metadata that is name, value pairs from a document. Only output the metadata in valid json form, as {\"name1\":\"value1\",\"name2\":\"value2\"} and nothing else. You will be given the document data and the schema for the metadata, that defines the name, description and type of each of the fields you will be extracting. Schema is of the form {\"fields\": [{\"key\": \"key_name\", \"displayName\": \"key display name\", \"type\": \"string\", \"description\": \"key description\"}]}. Leverage key description and key display name to identify where the key and value pairs are in the document. In certain cases, key description can also indicate the instructions to perform on the document to obtain the value. Prompt will be in the form of Schema is ``schema`` \n document is````document````", "prompt_template": "If you need to know today's date to respond, it is {current_date}. Schema is ``{user_question}`` \n document is````{content}````", "num_tokens_for_completion": 4096, "llm_endpoint_params": { "temperature": 0, "top_p": 1, "top_k": null, "type": "google_params" }, "embeddings": { "model": "azure__openai__text_embedding_ada_002", "strategy": { "id": "basic", "num_tokens_per_chunk": 64 } } } } ``` When you set the `mode` parameter to `extract_structured` the response will be as follows: ``` { "type": "ai_agent_extract_structured", "basic_text": { "model": "google__gemini_1_5_flash_001", "system_message": "Respond only in valid json. You are extracting metadata that is name, value pairs from a document. Only output the metadata in valid json form, as {\"name1\":\"value1\",\"name2\":\"value2\"} and nothing else. You will be given the document data and the schema for the metadata, that defines the name, description and type of each of the fields you will be extracting. Schema is of the form {\"fields\": [{\"key\": \"key_name\", \"prompt\": \"prompt to extract the value\", \"type\": \"date\"}]}. Leverage prompt for each key to identify where the key and value pairs are in the document. In certain cases, prompt can also indicate the instructions to perform on the document to obtain the value. Prompt will be in the form of Schema is ``schema`` \n document is````document````", "prompt_template": "If you need to know today's date to respond, it is {current_date}. Schema is ``{user_question}`` \n document is````{content}````", "num_tokens_for_completion": 4096, "llm_endpoint_params": { "temperature": 0, "top_p": 1, "top_k": null, "type": "google_params" } }, "long_text": { "model": "google__gemini_1_5_flash_001", "system_message": "Respond only in valid json. You are extracting metadata that is name, value pairs from a document. Only output the metadata in valid json form, as {\"name1\":\"value1\",\"name2\":\"value2\"} and nothing else. You will be given the document data and the schema for the metadata, that defines the name, description and type of each of the fields you will be extracting. Schema is of the form {\"fields\": [{\"key\": \"key_name\", \"prompt\": \"prompt to extract the value\", \"type\": \"date\"}]}. Leverage prompt for each key to identify where the key and value pairs are in the document. In certain cases, prompt can also indicate the instructions to perform on the document to obtain the value. Prompt will be in the form of Schema is ``schema`` \n document is````document````", "prompt_template": "If you need to know today's date to respond, it is {current_date}. Schema is ``{user_question}`` \n document is````{content}````", "num_tokens_for_completion": 4096, "llm_endpoint_params": { "temperature": 0, "top_p": 1, "top_k": null, "type": "google_params" }, "embeddings": { "model": "google__textembedding_gecko_003", "strategy": { "id": "basic", "num_tokens_per_chunk": 64 } } } } ``` **Reference:** https://developer.box.com/guides/box-ai/ai-agents/get-agent-default-config/ --- ## Untitled *Type: guide | Category: Authentication * Use a Token Every authenticated API call requires an Access Token to be passed in the Authorization header as a Bearer Token or it will… # Use a Token Every authenticated API call requires an Access Token to be passed in the `Authorization` header as a `Bearer Token` or it will return a `401 Unauthorized` HTTP status. ``` curl https://api.box.com/2.0/users/me \ -H "authorization: Bearer EGmDmRVfhfHsqesn5yVYHAqUkD0dyDfk" ``` Use the [`GET /users/me`](endpoint://get-users-id) endpoint to inspect what user an Access Token is authenticated for. **Reference:** https://developer.box.com/guides/authentication/tokens/api-calls/ --- ## Untitled *Type: guide | Category: Box AI * AWS Claude 3.7 Sonnet Overview AWS Claude 3.7 Sonnet model is designed to enhance language understanding and generation tasks. Model details… # AWS Claude 3.7 Sonnet ## Overview **AWS Claude 3.7 Sonnet** model is designed to enhance language understanding and generation tasks. ## Model details | Item | Value | Description | | --- | --- | --- | | Model name | AWS Claude 3.7 Sonnet | The name of the model. | | Model category | Premium | The category of the model - standard or premium. | | API model name | aws__claude_3_7_sonnet | The name of the model that is used in the Box AI API for model overrides. The user must provide this exact name for the API to work. | | Hosting layer | Amazon Web Services (AWS) | The trusted organization that securely hosts LLM. | | Model provider | Anthropic | The organization that provides this model. | | Release date | February 24, 2025 | The release date for the model. | | Knowledge cutoff date | October 2024 | The date after which the model does not get any information updates. | | Input context window | 200k tokens | The number of tokens supported by the input context window. | | Maximum output tokens | 8k tokens | The number of tokens that can be generated by the model in a single request. | | Empirical throughput | 42 | The number of tokens the model can generate per second. | | Open source | No | Specifies if the model's code is available for public use. | ## Additional documentation For additional information, see [official AWS Claude documentation](https://aws.amazon.com/bedrock/claude/). **Reference:** https://developer.box.com/guides/box-ai/ai-models/aws-claude-3-7-sonnet-model-card/ --- ## Untitled *Type: guide | Category: Box AI * AWS Claude 3.5 Sonnet Overview AWS Claude 3.5 Sonnet model is designed to enhance language understanding and generation tasks. Model details… # AWS Claude 3.5 Sonnet ## Overview **AWS Claude 3.5 Sonnet** model is designed to enhance language understanding and generation tasks. ## Model details | Item | Value | Description | | --- | --- | --- | | Model name | AWS Claude 3.5 Sonnet | The name of the model. | | Model category | Premium | The category of the model - standard or premium. | | API model name | aws__claude_3_5_sonnet | The name of the model that is used in the Box AI API for model overrides. The user must provide this exact name for the API to work. | | Hosting layer | Amazon Web Services (AWS) | The trusted organization that securely hosts LLM. | | Model provider | Anthropic | The organization that provides this model. | | Release date | June 20th, 2024 | The release date for the model. | | Knowledge cutoff date | April 2024 | The date after which the model does not get any information updates. | | Input context window | 200k tokens | The number of tokens supported by the input context window. | | Maximum output tokens | 8k tokens | The number of tokens that can be generated by the model in a single request. | | Empirical throughput | Not specified | The number of tokens the model can generate per second. | | Open source | No | Specifies if the model's code is available for public use. | ## Additional documentation For additional information, see [official AWS Claude 3.5 Sonnet documentation](https://aws.amazon.com/bedrock/claude/). **Reference:** https://developer.box.com/guides/box-ai/ai-models/aws-claude-3-5-sonnet-model-card/ --- ## Untitled *Type: guide | Category: Box AI * AWS Claude 3 Sonnet AWS Claude 3 Sonnet model is designed for advanced language tasks, focusing on comprehension and context handling. Model… # AWS Claude 3 Sonnet **AWS Claude 3 Sonnet** model is designed for advanced language tasks, focusing on comprehension and context handling. ## Model details | Item | Value | Description | | --- | --- | --- | | Model name | AWS Claude 3 Sonnet | The name of the model. | | Model category | Premium | The category of the model - standard or premium. | | API model name | aws__claude_3_sonnet | The name of the model that is used in the Box AI API for model overrides. The user must provide this exact name for the API to work. | | Hosting layer | Amazon Web Services (AWS) | The trusted organization that securely hosts LLM. | | Model provider | Anthropic | The organization that provides this model. | | Release date | March 4th 2024 | The release date for the model. | | Knowledge cutoff date | August 2023 | The date after which the model does not get any information updates. | | Input context window | 200k tokens | The number of tokens supported by the input context window. | | Maximum output tokens | 4k tokens | The number of tokens that can be generated by the model in a single request. | | Empirical throughput | 49.8 | The number of tokens the model can generate per second. | | Open source | No | Specifies if the model's code is available for public use. | ## Additional documentation For additional information, see [official AWS Claude 3 Sonnet documentation](https://aws.amazon.com/bedrock/claude/). **Reference:** https://developer.box.com/guides/box-ai/ai-models/aws-claude-3-sonnet-model-card/ --- ## Untitled *Type: guide | Category: Box AI * AWS Claude 3 Haiku Overview AWS Claude 3 Haiku model is tailored for various language tasks, including creative writing and conversational… # AWS Claude 3 Haiku ## Overview **AWS Claude 3 Haiku** model is tailored for various language tasks, including creative writing and conversational AI. ## Model details | Item | Value | Description | | --- | --- | --- | | Model name | AWS Claude 3 Haiku | The name of the model. | | Model category | Standard | The category of the model - standard or premium. | | API model name | aws__claude_3_haiku | The name of the model that is used in the Box AI API for model overrides. The user must provide this exact name for the API to work. | | Hosting layer | Amazon Web Services (AWS) | The trusted organization that securely hosts LLM. | | Model provider | Anthropic | The organization that provides this model. | | Release date | March 13th, 2024 | The release date for the model. | | Knowledge cutoff date | August 2023 | The date after which the model does not get any information updates. | | Input context window | 200k tokens | The number of tokens supported by the input context window. | | Maximum output tokens | 4k tokens | The number of tokens that can be generated by the model in a single request. | | Empirical throughput | 117 | The number of tokens the model can generate per second. | | Open source | No | Specifies if the model's code is available for public use. | ## Additional documentation For additional information, see [official AWS Claude 3 Haiku documentation](https://aws.amazon.com/bedrock/claude/). **Reference:** https://developer.box.com/guides/box-ai/ai-models/aws-claude-3-haiku-model-card/ --- ## Untitled *Type: guide | Category: Box AI * AWS Claude 4 Opus AWS Claude 4 Opus model excels at coding and complex problem-solving, powering frontier agent products. It's possible to… # AWS Claude 4 Opus **AWS Claude 4 Opus** model excels at coding and complex problem-solving, powering frontier agent products. It's possible to run Claude Code in the background, enabling developers to assign long-running coding tasks for Opus to handle independently. ## Model details | Item | Value | Description | | --- | --- | --- | | Model name | AWS Claude 4 Opus | The name of the model. | | Model category | Premium | The category of the model - standard or premium. | | API model name | aws__claude_4_opus | The name of the model that is used in the Box AI API for model overrides. The user must provide this exact name for the API to work. | | Hosting layer | Amazon Web Services (AWS) | The trusted organization that securely hosts LLM. | | Model provider | Anthropic | The organization that provides this model. | | Release date | March 4th 2024 | The release date for the model. | | Knowledge cutoff date | August 2023 | The date after which the model does not get any information updates. | | Input context window | 200k tokens | The number of tokens supported by the input context window. | | Maximum output tokens | 32k tokens | The number of tokens that can be generated by the model in a single request. | | Empirical throughput | Not specified | The number of tokens the model can generate per second. | | Open source | No | Specifies if the model's code is available for public use. | ## Additional documentation For additional information, see [official AWS Claude 4 Opus documentation](https://aws.amazon.com/bedrock/claude/). **Reference:** https://developer.box.com/guides/box-ai/ai-models/aws-claude-4-opus-model-card/ --- ## Untitled *Type: guide | Category: Box AI * AWS Claude 4 Sonnet AWS Claude 4 Sonnet model delivers superior coding and reasoning while responding more precisely to your instructions… # AWS Claude 4 Sonnet **AWS Claude 4 Sonnet** model delivers superior coding and reasoning while responding more precisely to your instructions, and brings frontier performance to everyday use cases. ## Model details | Item | Value | Description | | --- | --- | --- | | Model name | AWS Claude 4 Sonnet | The name of the model. | | Model name | AWS Titan Text Lite | The name of the model - standard or premium. | | API model name | aws__claude_4_sonnet | The name of the model that is used in the Box AI API for model overrides. The user must provide this exact name for the API to work. | | Hosting layer | Amazon Web Services (AWS) | The trusted organization that securely hosts LLM. | | Model provider | Anthropic | The organization that provides this model. | | Release date | May 22nd 2025 | The release date for the model. | | Knowledge cutoff date | November 2024 | The date after which the model does not get any information updates. | | Input context window | 200k tokens | The number of tokens supported by the input context window. | | Maximum output tokens | 64k tokens | The number of tokens that can be generated by the model in a single request. | | Empirical throughput | Not specified | The number of tokens the model can generate per second. | | Open source | No | Specifies if the model's code is available for public use. | ## Additional documentation For additional information, see [official AWS Claude 4 Sonnet documentation](https://aws.amazon.com/bedrock/claude/). **Reference:** https://developer.box.com/guides/box-ai/ai-models/aws-claude-4-sonnet-model-card/ --- ## Untitled *Type: guide | Category: Box AI * AWS Titan Text Lite AWS Titan Text Lite model is designed for advanced language processing, capable of handling extensive contexts, making… # AWS Titan Text Lite **AWS Titan Text Lite** model is designed for advanced language processing, capable of handling extensive contexts, making it suitable for complex tasks, although the model itself is lightweight. ## Model details | Item | Value | Description | | --- | --- | --- | | Model name | AWS Titan Text Lite | The name of the model. | | Model category | Standard | The category of the model - standard or premium. | | API model name | aws__titan_text_lite | The name of the model that is used in the Box AI API for model overrides. The user must provide this exact name for the API to work. | | Hosting layer | Amazon Web Services (AWS) | The trusted organization that securely hosts LLM. | | Model provider | Anthropic | The organization that provides this model. | | Release date | September 2024 | The release date for the model. | | Knowledge cutoff date | Not provided | The date after which the model does not get any information updates. | | Input context window | 128k tokens | The number of tokens supported by the input context window. | | Maximum output tokens | 4k tokens | The number of tokens that can be generated by the model in a single request. | | Empirical throughput | Not specified | The number of tokens the model can generate per second. | | Open source | No | Specifies if the model's code is available for public use. | ## Additional documentation For additional information, see [official AWS Titan documentation](https://aws.amazon.com/bedrock/titan/). **Reference:** https://developer.box.com/guides/box-ai/ai-models/aws-titan-text-lite-model-card/ --- ## Untitled *Type: guide | Category: Box AI * Azure OpenAI GPT-4.1 Mini Azure OpenAI GPT-4.1 Mini is a multimodal model designed to handle lightweight tasks. Model details Item Value… # Azure OpenAI GPT-4.1 Mini **Azure OpenAI GPT-4.1 Mini** is a multimodal model designed to handle lightweight tasks. ## Model details | Item | Value | Description | | --- | --- | --- | | Model name | GPT-4.1 mini | The name of the model. | | Model category | Standard | The category of the model - standard or premium. | | API model name | azure__openai__gpt_4.1_mini | The name of the model that is used in the Box AI API for model overrides. The user must provide this exact name for the API to work. | | Hosting layer | Microsoft Azure | The trusted organization that securely hosts LLM. | | Model provider | Microsoft Azure | The organization that provides this model. | | Release date | April 14th, 2025 | The release date for the model. | | Knowledge cutoff date | May 2024 | The date after which the model does not get any information updates. | | Input context window | 1m tokens | The number of tokens supported by the input context window. | | Maximum output tokens | 32k tokens | The number of tokens that can be generated by the model in a single request. | | Empirical throughput | 87.5 | The number of tokens the model can generate per second. | | Open source | No | Specifies if the model's code is available for public use. | ## Additional documentation For additional information, see [official Azure OpenAI GPT-4o documentation](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models?tabs=python-secure#gpt-4o-and-gpt-4-turbo). **Reference:** https://developer.box.com/guides/box-ai/ai-models/azure-openai-gpt-4-1-mini-model-card/ --- ## Untitled *Type: guide | Category: Box AI * Azure OpenAI GPT-4.1 Azure OpenAI GPT-4.1 is a multimodal model, highly efficient in handling complex, multi-step tasks. Model details Item… # Azure OpenAI GPT-4.1 **Azure OpenAI GPT-4.1** is a multimodal model, highly efficient in handling complex, multi-step tasks. ## Model details | Item | Value | Description | | --- | --- | --- | | Model name | GPT-4.1 | The name of the model. | | Model category | Premium | The category of the model - standard or premium. | | API model name | azure__openai__gpt_4_1 | The name of the model that is used in the Box AI API for model overrides. The user must provide this exact name for the API to work. | | Hosting layer | Microsoft Azure | The trusted organization that securely hosts LLM. | | Model provider | Microsoft Azure | The organization that provides this model. | | Release date | April 14th, 2025 | The release date for the model. | | Knowledge cutoff date | May 2024 | The date after which the model does not get any information updates. | | Input context window | 1m tokens | The number of tokens supported by the input context window. | | Maximum output tokens | 32k tokens | The number of tokens that can be generated by the model in a single request. | | Empirical throughput | 87.5 | The number of tokens the model can generate per second. | | Open source | No | Specifies if the model's code is available for public use. | ## Additional documentation For additional information, see [official Azure OpenAI GPT-4o documentation](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models?tabs=python-secure#gpt-4o-and-gpt-4-turbo). **Reference:** https://developer.box.com/guides/box-ai/ai-models/azure-openai-gpt-4-1-model-card/ --- ## Untitled *Type: guide | Category: Box AI * Azure OpenAI GPT-4o Azure OpenAI GPT-4o is a multimodal model designed to handle lightweight tasks. Model details Item Value Description… # Azure OpenAI GPT-4o **Azure OpenAI GPT-4o** is a multimodal model designed to handle lightweight tasks. ## Model details | Item | Value | Description | | --- | --- | --- | | Model name | GPT-4o | The name of the model. | | Model category | Premium | The category of the model - standard or premium. | | API model name | azure__openai__gpt_4o | The name of the model that is used in the Box AI API for model overrides. The user must provide this exact name for the API to work. | | Hosting layer | Microsoft Azure | The trusted organization that securely hosts LLM. | | Model provider | Microsoft Azure | The organization that provides this model. | | Release date | May 13th, 2024 | The release date for the model. | | Knowledge cutoff date | September 2023 | The date after which the model does not get any information updates. | | Input context window | 128k tokens | The number of tokens supported by the input context window. | | Maximum output tokens | 2k tokens | The number of tokens that can be generated by the model in a single request. | | Empirical throughput | 87.5 | The number of tokens the model can generate per second. | | Open source | No | Specifies if the model's code is available for public use. | ## Additional documentation For additional information, see [official Azure OpenAI GPT-4o documentation](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models?tabs=python-secure#gpt-4o-and-gpt-4-turbo). **Reference:** https://developer.box.com/guides/box-ai/ai-models/azure-openai-gpt-4o-model-card/ --- ## Untitled *Type: guide | Category: Box AI * Azure OpenAI GPT-4o Mini Azure OpenAI GPT-4o Mini is a multimodal model designed to handle lightweight tasks. Model details Item Value… # Azure OpenAI GPT-4o Mini **Azure OpenAI GPT-4o Mini** is a multimodal model designed to handle lightweight tasks. ## Model details | Item | Value | Description | | --- | --- | --- | | Model name | GPT-4o Mini | The name of the model. | | Model category | Standard | The category of the model - standard or premium. | | API model name | azure__openai__gpt_4o_mini | The name of the model that is used in the Box AI API for model overrides. The user must provide this exact name for the API to work. | | Hosting layer | Microsoft Azure | The trusted organization that securely hosts LLM. | | Model provider | Microsoft Azure | The organization that provides this model. | | Release date | July 18th, 2024 | The release date for the model. | | Knowledge cutoff date | October 2023 | The date after which the model does not get any information updates. | | Input context window | 128k tokens | The number of tokens supported by the input context window. | | Maximum output tokens | 16k tokens | The number of tokens that can be generated by the model in a single request. | | Empirical throughput | 85.4 | The number of tokens the model can generate per second. | | Open source | No | Specifies if the model's code is available for public use. | ## Usage Box Webapp uses this model to cover the following use cases: - Creating content - Editing content - Creating summaries - Single doc Q&A on text Box AI API uses this model to cover the following use cases: - Creating content - Editing content - Creating summaries - Single doc Q&A on text - Extracting metadata ## Additional documentation For additional information, see [official Azure OpenAI GPT-4o Mini documentation](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models?tabs=python-secure#gpt-4o-and-gpt-4-turbo). **Reference:** https://developer.box.com/guides/box-ai/ai-models/azure-openai-gpt-4o-mini-model-card/ --- ## Untitled *Type: guide | Category: Box AI * Azure text-embedding-ada-002 Azure text-embedding-ada-002 is a multimodal model designed to handle lightweight tasks. Model details Item… # Azure text-embedding-ada-002 **Azure text-embedding-ada-002** is a multimodal model designed to handle lightweight tasks. ## Model details | Item | Value | Description | | --- | --- | --- | | Model name | text-embedding-ada-002 | The name of the model. | | Model category | Standard | The category of the model - standard or premium. | | API model name | azure__openai__text_embedding_ada_002 | The name of the model that is used in the Box AI API for model overrides. The user must provide this exact name for the API to work. | | Hosting layer | Microsoft Azure | The trusted organization that securely hosts LLM. | | Model provider | Microsoft Azure | The organization that provides this model. | | Release date | December 2022 | The release date for the model. | | Knowledge cutoff date | September 2021 | The date after which the model does not get any information updates. | | Input context window | 8k tokens | The number of tokens supported by the input context window. | | Maximum output tokens | Not applicable | The number of tokens that can be generated by the model in a single request. | | Empirical throughput | 1000 | The number of tokens the model can generate per second. | | Open source | No | Specifies if the model's code is available for public use. | ## Additional documentation For additional information, see [official Azure Embeddings models documentation](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models#embeddings). **Reference:** https://developer.box.com/guides/box-ai/ai-models/azure-text-embedding-ada-002-model-card/ --- ## Untitled *Type: guide | Category: Box AI * Google Gemini 1.5 Flash Google Gemini 1.5 Flash is a multimodal model designed to handle lightweight tasks. It is designed for high-volume… # Google Gemini 1.5 Flash **Google Gemini 1.5 Flash** is a multimodal model designed to handle lightweight tasks. It is designed for high-volume, low-latency tasks, making it highly efficient for large-scale use cases like summarization, multimodal processing, and categorization ## Model details | Item | Value | Description | | --- | --- | --- | | Model name | Google Gemini 1.5 Flash | The name of the model. | | API model name | google__gemini_1_5_flash_001 | The name of the model that is used in the Box AI API for model overrides. The user must provide this exact name for the API to work. | | Hosting layer | Google | The trusted organization that securely hosts LLM. | | Model provider | Google | The organization that provides this model. | | Release date | May 14th 2024 | The release date for the model. | | Knowledge cutoff date | November 2023 | The date after which the model does not get any information updates. | | Input context window | 1m tokens | The number of tokens supported by the input context window. | | Maximum output tokens | 8k tokens | The number of tokens that can be generated by the model in a single request. | | Empirical throughput | 176 | The number of tokens the model can generate per second. | | Open source | No | Specifies if the model's code is available for public use. | ## Additional documentation For additional information, see [official Google Gemini 1.5 Flash documentation](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models#gemini-models). **Reference:** https://developer.box.com/guides/box-ai/ai-models/google-gemini-1-5-flash-001-model-card/ --- ## Untitled *Type: guide | Category: Box AI * Google Gemini 2.0 Flash Lite Google Gemini 2.0 Flash Lite is a multimodal model designed to handle lightweight tasks. It is designed for… # Google Gemini 2.0 Flash Lite **Google Gemini 2.0 Flash Lite** is a multimodal model designed to handle lightweight tasks. It is designed for high-volume, low-latency tasks, making it highly efficient for large-scale use cases like summarization, multimodal processing, and categorization but with higher quality than Gemini 1.5 Flash. ## Model details | Item | Value | Description | | --- | --- | --- | | Model name | Google Gemini 2.0 Flash Lite | The name of the model. | | Model category | Standard | The category of the model - standard or premium. | | API model name | google__gemini_2_0_flash_lite_preview | The name of the model that is used in the Box AI API for model overrides. The user must provide this exact name for the API to work. | | Hosting layer | Google | The trusted organization that securely hosts LLM. | | Model provider | Google | The organization that provides this model. | | Release date | February 5th 2025 | The release date for the model. | | Knowledge cutoff date | June 2024 | The date after which the model does not get any information updates. | | Input context window | 1m tokens | The number of tokens supported by the input context window. | | Maximum output tokens | 8k tokens | The number of tokens that can be generated by the model in a single request. | | Empirical throughput | 168 | The number of tokens the model can generate per second. | | Open source | No | Specifies if the model's code is available for public use. | ## Additional documentation For additional information, see [official Google Gemini 2.0 Flash Lite documentation](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models#gemini-models). **Reference:** https://developer.box.com/guides/box-ai/ai-models/google-gemini-2-0-flash-lite-preview-02-05/ --- ## Untitled *Type: guide | Category: Box AI * Google Gemini 1.5 Pro 001 Google Gemini 1.5 Pro 001 is a multimodal model designed to handle lightweight tasks. This model mid-size… # Google Gemini 1.5 Pro 001 **Google Gemini 1.5 Pro 001** is a multimodal model designed to handle lightweight tasks. This model mid-size multimodal model capable of handling extensive inputs like long videos, hours of audio, large data sets, and complex reasoning tasks ## Model details | Item | Value | Description | | --- | --- | --- | | Model name | Google Gemini 1.5 Pro 001 | The name of the model. | | API model name | google__gemini_1_5_pro_001 | The name of the model that is used in the Box AI API for model overrides. The user must provide this exact name for the API to work. | | Hosting layer | Google | The trusted organization that securely hosts LLM. | | Model provider | Google | The organization that provides this model. | | Release date | February 15th 2024 | The release date for the model. | | Knowledge cutoff date | November 2023 | The date after which the model does not get any information updates. | | Input context window | 1m tokens | The number of tokens supported by the input context window. | | Maximum output tokens | 8k tokens | The number of tokens that can be generated by the model in a single request. | | Empirical throughput | 45.5 | The number of tokens the model can generate per second. | | Open source | No | Specifies if the model's code is available for public use. | ## Additional documentation For additional information, see [official Google Gemini 1.5 Pro documentation](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models#gemini-models). **Reference:** https://developer.box.com/guides/box-ai/ai-models/google-gemini-1-5-pro-001-model-card/ --- ## Untitled *Type: guide | Category: Authentication * Developer Tokens A Developer Token is an Access Token available to developers during development and testing. These tokens are short lived… # Developer Tokens A Developer Token is an Access Token available to developers during development and testing. These tokens are short lived, as they expire after 60 minutes, and cannot be refreshed programmatically. ## Create Developer Token To create a Developer Token for an application: - Navigate to the Box [Developer Console](https://app.box.com/developers/console) and select the application to create a Developer Token for. - Select the **Configuration** tab. - Under Developer Token, select **Generate Developer Token**. You can also generate a Developer Token directly from [My Platform Apps](g://applications) view, using the menu available for each app. ## Using Developer Token A Developer Token can be used like any Access Token in the `Authorization` header of an API call. ``` curl https://api.box.com/2.0/users/me \ -H "authorization: Bearer [DEVELOPER_TOKEN]" ``` A Developer Token is associated with the the user that is logged in to the Developer Console when the token is generated. Our SDKs can be initialized with a Developer Token to create a basic API client. # Developer tokens should not be used in production environments Developer Tokens should only be used for development or testing purposes. When you explicitly revoke a developer token for a given app via the Developer console, all webhooks created by that application get deleted. ## Using SDKs and Developer Tokens To learn more about Developer Tokens for each SDK head over to: [.Net](https://github.com/box/box-windows-sdk-v2/blob/main/docs/authentication.md#developer-token) [Java](https://github.com/box/box-java-sdk/blob/main/doc/authentication.md#developer-token) [Python](https://github.com/box/box-python-sdk/blob/main/docs/usage/authentication.md#developer-token) [Node](https://github.com/box/box-node-sdk/blob/main/docs/authentication.md#developer-token) [IOS](https://github.com/box/box-ios-sdk/blob/main/docs/usage/authentication.md#developer-token) **Reference:** https://developer.box.com/guides/authentication/tokens/developer-tokens/ --- ## Untitled *Type: guide | Category: Box AI * Google Gemini 2.0 Flash Google Gemini 2.0 Flash is a multimodal model designed for optimal for high-volume, high-frequency tasks at scale… # Google Gemini 2.0 Flash **Google Gemini 2.0 Flash** is a multimodal model designed for optimal for high-volume, high-frequency tasks at scale. It capable of multimodal reasoning and has a context window of 1 million tokens. ## Model details | Item | Value | Description | | --- | --- | --- | | Model name | Google Gemini 2.0 Flash | The name of the model. | | Model name | Google Gemini 2.0 Flash | The name of the model. | | Model category | Standard | The category of the model - standard or premium. | | API model name | google__gemini_2_0_flash_001 | The name of the model that is used in the Box AI API for model overrides. The user must provide this exact name for the API to work. | | Hosting layer | Google | The trusted organization that securely hosts LLM. | | Model provider | Google | The organization that provides this model. | | Release date | February 5th 2025 | The release date for the model. | | Knowledge cutoff date | June 2024 | The date after which the model does not get any information updates. | | Input context window | 1m tokens | The number of tokens supported by the input context window. | | Maximum output tokens | 8k tokens | The number of tokens that can be generated by the model in a single request. | | Empirical throughput | 168 | The number of tokens the model can generate per second. | | Open source | No | Specifies if the model's code is available for public use. | ## Additional documentation For additional information, see [official Google Gemini 2.0 Flash documentation](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models#gemini-models). **Reference:** https://developer.box.com/guides/box-ai/ai-models/google-gemini-2-0-flash-001-model-card/ --- ## Untitled *Type: guide | Category: Authentication * Downscope a Token Downscoping is a way to exchange an existing Access Token for a new one that is more restricted. Reasons to downscope An… # Downscope a Token Downscoping is a way to exchange an existing Access Token for a new one that is more restricted. ## Reasons to downscope An application might need to share the Access Token with an environment that it does not fully control. A common example of this would be when using Box UI Elements in a web browser. When an application needs to pass an Access Token to the browser there is a potential security risk that needs to be resolved. In order to limit this risk the Access Token can be exchanged for a new token with much stricter permissions. ## High-level overview A downscoped token is a token that has fewer permissions (scopes) than the original token, as well as the optional additional restriction to only allow access to a specific file. The new token takes the permissions of the original token and restricts them to the tokens passed in, as well as the resource provided. ## Downscoping in practice To downscope a token, pass the `POST /oauth2/token` endpoint an existing Access Token, a list of scopes, as well as an optional file URL to restrict the token to. | Parameter | Description | | --- | --- | | subject_token | The original token to downscope. This can be a token that was acquired through OAuth 2.0, JWT token exchange, or as an App Token. | | scope | A space-delimited list of scopes to limit the new token to. Any valid scope for the application can be used, though a special set of scopes for Box UI elements is available | | resource | An optional full URL path to the file the token should be restricted to. | | box_shared_link | An optional shared link URL for a file or folder on Box. Password protected links are not supported. This option cannot be used in addition to the resource option nor can it be a shared link created on a weblink. | | subject_token_type | Always set to urn:ietf:params:oauth:token-type:access_token | | grant_type | Always set to urn:ietf:params:oauth:grant-type:token-exchange | ## Downscoped Access Token Object A downscoped Access Token returned by the `POST /oauth2/token` endpoint contains extra information on the specific restrictions. ``` { "access_token": "1!DgsZ6V9kMWZu2StrxwQDF5BudQNen-xUmU2cfcVKArE....", "expires_in": 4175, "token_type": "bearer", "restricted_to": [ { "scope": "item_preview", "object": { "type": "folder", "id": "1234567890", "sequence_id": "0", "etag": "0", "name": "Test" } } ], "issued_token_type": "urn:ietf:params:oauth:token-type:access_token" } ``` Most importantly here is the list of `restricted_to` entries that will contain each combination of `object` and `scope` that the new token has the permissions for. A downscoped token does not include a refresh token. To get a new downscoped token, refresh the original refresh token and use that new token to get a downscoped token. **Reference:** https://developer.box.com/guides/authentication/tokens/downscope/ --- ## Untitled *Type: guide | Category: Box AI * Google Gemini 2.5 Pro Google Gemini 2.5 Pro is a multimodal model capable of solving complex problems. It can comprehend vast datasets and… # Google Gemini 2.5 Pro **Google Gemini 2.5 Pro** is a multimodal model capable of solving complex problems. It can comprehend vast datasets and challenging problems from different information sources, including text, audio, images, video, and even entire code repositories. ## Model details | Item | Value | Description | | --- | --- | --- | | Model name | Google Gemini 2.5 Pro | The name of the model. | | Model category | Premium | The category of the model: Standard or Premium. | | API model name | google__gemini_2_5_pro | The name of the model that is used in the Box AI API for model overrides. The user must provide this exact name for the API to work. | | Hosting layer | Google | The trusted organization that securely hosts LLM. | | Model provider | Google | The organization that provides this model. | | Release date | June 17th 2025 | The release date for the model. | | Knowledge cutoff date | January 2025 | The date after which the model does not get any information updates. | | Input context window | 1m tokens | The number of tokens supported by the input context window. | | Maximum output tokens | 65k tokens | The number of tokens that can be generated by the model in a single request. | | Empirical throughput | Not specified | The number of tokens the model can generate per second. | | Open source | No | Specifies if the model's code is available for public use. | ## Additional documentation For additional information, see [official Google Gemini 2.5 Pro documentation](https://cloud.google.com/vertex-ai/generative-ai/docs/models/gemini/2-5-pro). **Reference:** https://developer.box.com/guides/box-ai/ai-models/google-gemini-2-5-pro-model-card/ --- ## Untitled *Type: guide | Category: Box AI * IBM Llama 3.2 Vision Instruct IBM Llama 3.2 Vision Instruct is a model built for image-in, text-out use cases such as document-level… # IBM Llama 3.2 Vision Instruct **IBM Llama 3.2 Vision Instruct** is a model built for image-in, text-out use cases such as document-level understanding, interpretation of charts and graphs, and captioning of images. ## Model details | Item | Value | Description | | --- | --- | --- | | Model name | IBM Llama 3.2 Vision Instruct | The name of the model. | | Model category | Standard | The category of the model - standard or premium. | | API model name | ibm__llama__3_2_90b_vision_instruct | The name of the model that is used in the Box AI API for model overrides. The user must provide this exact name for the API to work. | | Hosting layer | IBM | The trusted organization that securely hosts LLM. | | Model provider | Meta | The organization that provides this model. | | Release date | September 25th 2024 | The release date for the model. | | Knowledge cutoff date | December 2023 | The date after which the model does not get any information updates. | | Input context window | 128k | The number of tokens supported by the input context window. | | Maximum output tokens | Not specified | The number of tokens that can be generated by the model in a single request. | | Empirical throughput | Not specified | The number of tokens the model can generate per second. | | Open source | Yes | Specifies if the model's code is available for public use. | | IP infringement protection | No | Use of this model does not come with any intellectual property rights assurances or protections from Box. Please consider any potential IP issues that might arise from using the model’s outputs. | ## Additional documentation For additional information, see [official IBM Llama 3.2 Vision Instruct documentation](https://www.ibm.com/docs/en/watsonx/w-and-w/2.1.0?topic=models-third-party-foundation#llama-3-2-vision). **Reference:** https://developer.box.com/guides/box-ai/ai-models/ibm-llama-3-2-90b-vision-instruct-model-card/ --- ## Untitled *Type: guide | Category: Box AI * OpenAI GPT o3 OpenAI GPT o3 is specifically designed to tackle reasoning and problem-solving tasks with increased focus and capability. It… # OpenAI GPT o3 **OpenAI GPT o3** is specifically designed to tackle reasoning and problem-solving tasks with increased focus and capability. It spends more time processing and understanding the user's request, making it exceptionally strong in areas like science, coding, and math compared to previous iterations. ## Model details | Item | Value | Description | | --- | --- | --- | | Model name | GPT o3 | The name of the model. | | Model category | Premium | The category of the model - standard or premium. | | API model name | openai__gpt_o3 | The name of the model that is used in the Box AI API for model overrides. The user must provide this exact name for the API to work. | | Hosting layer | OpenAI | The trusted organization that securely hosts LLM. | | Model provider | OpenAI | The organization that provides this model. | | Release date | April 16th, 2025 | The release date for the model. | | Knowledge cutoff date | May 2024 | The date after which the model does not get any information updates. | | Input context window | 200k tokens | The number of tokens supported by the input context window. | | Maximum output tokens | 100k tokens | The number of tokens that can be generated by the model in a single request. | | Empirical throughput | Not specified | The number of tokens the model can generate per second. | | Open source | No | Specifies if the model's code is available for public use. | ## Additional documentation For additional information, see [official OpenAI GPT o3 documentation](https://openai.com/index/introducing-o3-and-o4-mini/). **Reference:** https://developer.box.com/guides/box-ai/ai-models/openai-gpt-o3-model-card/ --- ## Untitled *Type: guide | Category: Box AI * IBM Llama 4 Scout IBM Llama 4 Scout is an auto-regressive language model that uses a mixture-of-experts (MoE) architecture and incorporates… # IBM Llama 4 Scout **IBM Llama 4 Scout** is an auto-regressive language model that uses a mixture-of-experts (MoE) architecture and incorporates early fusion for native multimodality. ## Model details | Item | Value | Description | | --- | --- | --- | | Model name | IBM Llama 4 Scout | The name of the model. | | Model category | Standard | The category of the model - standard or premium. | | API model name | ibm__llama_4_scout | The name of the model that is used in the Box AI API for model overrides. The user must provide this exact name for the API to work. | | Hosting layer | IBM | The trusted organization that securely hosts LLM. | | Model provider | Meta | The organization that provides this model. | | Release date | April 5th 2025 | The release date for the model. | | Knowledge cutoff date | August 2024 | The date after which the model does not get any information updates. | | Input context window | 10m | The number of tokens supported by the input context window. | | Maximum output tokens | Not specified | The number of tokens that can be generated by the model in a single request. | | Empirical throughput | Not specified | The number of tokens the model can generate per second. | | Open source | Yes | Specifies if the model's code is available for public use. | | IP infringement protection | No | Use of this model does not come with any intellectual property rights assurances or protections from Box. Please consider any potential IP issues that might arise from using the model’s outputs. | ## Additional documentation For additional information, see [official IBM Llama 4 Scout documentation](https://www.ibm.com/docs/en/watsonx/w-and-w/2.1.0?topic=models-third-party-foundation). **Reference:** https://developer.box.com/guides/box-ai/ai-models/ibm-llama-4-scout-model-card/ --- ## Untitled *Type: guide | Category: Box AI * xAI Grok 3 Beta xAI Grok 3 Beta is a model that excels at enterprise use cases like data extraction, coding, and text summarization… # xAI Grok 3 Beta **xAI Grok 3 Beta** is a model that excels at enterprise use cases like data extraction, coding, and text summarization. Possesses deep domain knowledge in finance, healthcare, law, and science. ## Model details | Item | Value | Description | | --- | --- | --- | | Model name | xAI Grok 3 Beta | The name of the model. | | Model category | Premium | The category of the model - standard or premium. | | API model name | xai__grok_3_beta | The name of the model that is used in the Box AI API for model overrides. The user must provide this exact name for the API to work. | | Hosting layer | xAI | The trusted organization that securely hosts LLM. | | Model provider | xAI | The organization that provides this model. | | Release date | April 17th 2025 | The release date for the model. | | Knowledge cutoff date | January 2025 | The date after which the model does not get any information updates. | | Input context window | 1m tokens | The number of tokens supported by the input context window. | | Maximum output tokens | 131k tokens | The number of tokens that can be generated by the model in a single request. | | Empirical throughput | Not specified | The number of tokens the model can generate per second. | | Open source | No | Specifies if the model's code is available for public use. | | IP infringement protection | No | Use of this model does not come with any intellectual property rights assurances or protections from Box. Please consider any potential IP issues that might arise from using the model’s outputs. | ## Additional documentation For additional information, see [official xAI Grok 3 Mini Beta documentation](https://docs.x.ai/docs/models). **Reference:** https://developer.box.com/guides/box-ai/ai-models/xai-grok-3-beta-model-card/ --- ## Untitled *Type: guide | Category: Box AI * xAI Grok 3 Mini Reasoning Beta xAI Grok 3 Mini Reasoning Beta is a lightweight model that thinks before responding. Fast, smart, and great… # xAI Grok 3 Mini Reasoning Beta **xAI Grok 3 Mini Reasoning Beta** is a lightweight model that thinks before responding. Fast, smart, and great for logic-based tasks that do not require deep domain knowledge. The raw thinking traces are accessible. ## Model details | Item | Value | Description | | --- | --- | --- | | Model name | xAI Grok 3 Mini Reasoning Beta | The name of the model. | | Model category | Standard | The category of the model - standard or premium. | | API model name | xai__grok_3_mini_reasoning_beta | The name of the model that is used in the Box AI API for model overrides. The user must provide this exact name for the API to work. | | Hosting layer | xAI | The trusted organization that securely hosts LLM. | | Model provider | xAI | The organization that provides this model. | | Release date | April 9th 2025 | The release date for the model. | | Knowledge cutoff date | unknown | The date after which the model does not get any information updates. | | Input context window | 131k tokens | The number of tokens supported by the input context window. | | Maximum output tokens | 131k tokens | The number of tokens that can be generated by the model in a single request. | | Empirical throughput | Not specified | The number of tokens the model can generate per second. | | Open source | No | Specifies if the model's code is available for public use. | | IP infringement protection | No | Use of this model does not come with any intellectual property rights assurances or protections from Box. Please consider any potential IP issues that might arise from using the model’s outputs. | ## Additional documentation For additional information, see [official xAI Grok 3 Mini Beta documentation](https://docs.x.ai/docs/models). **Reference:** https://developer.box.com/guides/box-ai/ai-models/xai-grok-3-mini-beta-model-card/ --- ## Untitled *Type: guide | Category: Box AI * Ask questions to Box AI Box AI API is available to all Enterprise Plus and Enterprise Advanced customers. Box AI API allows you to ask a… # Ask questions to Box AI Box AI API is available to all Enterprise Plus and Enterprise Advanced customers. Box AI API allows you to ask a question about a supplied file or a set of files, and get a response based on the content. For example, while viewing a document in Box, you can ask Box AI to summarize the content. ## Before you start Make sure you followed the steps listed in [getting started with Box AI](g://box-ai/ai-tutorials/prerequisites) to create a platform app and authenticate. ## Send a request To send a request containing your question, use the `POST /2.0/ai/ask` endpoint and provide the mandatory parameters. ### Parameters To make a call, you need to pass the following parameters. Mandatory parameters are in **bold**. | Parameter | Description | Available values | Example | | --- | --- | --- | --- | | mode | The type of request. It can be a question about a single file or a set of files. For a single text files, Box AI API supports up to 1MB of text representation. If the file size exceeds 1MB, the first 1MB of text representation will be processed. If you want to list multiple files, the limit is 25 files. Box AI handles image documents with a resolution of 1024 x 1024 pixels, with a maximum of 5 images or 5 pages for multi-page images. If the number of image or image pages exceeds 5, the first 5 images or pages will be processed. If you set mode to single_item_qa, the items array can list only one element. Currently Box AI does not support multi-modal requests. If both images and text are sent Box AI will only process the text. | single_item_qa, multiple_item_qa | single_item_qa | | prompt | The question about your document or content. The prompt's length cannot exceed 10000 characters. | | What is this document about? | | dialogue_history.prompt | The prompt previously provided by the client and answered by the Large Language Model (LLM). | Make my email about public APIs sound more professional | | | dialogue_history.answer | The answer previously provided by the LLM. | Here is a draft of your professional email about public APIs. | | | dialogue_history.created_at | The ISO date formatted timestamp of when the previous answer to the prompt was created. | 2012-12-12T10:53:43-08:00 | | | include_citations | Specifies if the citations should be returned in the answer. | true, false | true | | items.id | The Box file ID you want to provide as input. | | 112233445566 | | items.type | The type of the provided input. Currently, it can be a single file or multiple files. | file | file | | items.content | The content of the item. Usually it is the text representation. | | An application programming interface (API) is a way for two or more computer programs or components to communicate with each other. It is a type of software interface... | | ai_agent | The AI agent used to override the default agent configuration. You can use this parameter replace the default LLM with a custom one using the model parameter for shorter and longer texts, tweak the base prompt to allow for a more customized user experience, or change an LLM parameter, such as temperature, to make the results more or less creative. Before you use the ai_agent parameter, you can get the default configuration using the GET 2.0/ai_agent_default request. For specific use cases, see the AI model overrides tutorial. | | | ## Use cases ## Ask questions about an item This example shows how to ask a question about one or more items using the `POST ask/ai` API. When using this endpoint, remember to specify the `mode` parameter depending on the number of items you want to supply. ``` curl -i -L POST "https://api.box.com/2.0/ai/ask" \ -H "content-type: application/json" \ -H "authorization: Bearer <ACCESS_TOKEN>" \ -d '{ "mode": "single_item_qa", "items": [ { "id": "12345678", "type": "file" } ], "prompt": "List the guidelines on creating questions in Box AI for Documents" }' ``` The response will be as follows: ``` { "answer": "The guidelines for working with questions in Box AI for Documents are as follows:\n\n1. Box AI pulls information only from the document loaded in preview.\n2. If questions fall outside the scope of the document, Box AI will inform you that it cannot answer.\n3. Be specific when asking questions; use parameters like numbered lists, brevity, tables, and central themes or key points.\n4. Aim to stay within the scope of the document.\n5. Focus on text-based responses only.", "created_at": "2024-11-04T02:30:09.557-08:00", "completion_reason": "done" } ``` ## Ask questions with content parameter If you use the `content` parameter as the source of input for Box AI, it will use it as the primary source. ``` curl -i -L POST "https://api.box.com/2.0/ai/ask" \ -H "content-type: application/json" \ -H "authorization: Bearer <ACCESS_TOKEN>" \ -d '{ "mode": "single_item_qa", "items": [ { "id": "12345678", "type": "file", "content": "This is a document about Box AI For documents. It consists of the functionality summary and guidelines on how to work with Box AI. Additionally, it provides a set of best practices for creating questions." } ], "prompt": "List the guidelines on creating questions in Box AI for Documents" }' ``` The response to this request is based on the `content` parameter instead of the file's content: ``` { "answer": "The document does not provide specific guidelines on working with questions in Box AI for Documents. It only mentions that it includes a set of best practices for creating questions, but the details of those guidelines are not included in the text provided. If you have more information or another document, I can help further!", "created_at": "2024-11-04T02:31:51.125-08:00", "completion_reason": "done" } ``` ## Ask questions with citations parameter Setting the `citations` parameter to `true` causes the response to include excerpts from source file or files Box AI used to compile the answer. ``` curl -i -L POST "https://api.box.com/2.0/ai/ask" \ -H "content-type: application/json" \ -H "authorization: Bearer <ACCESS_TOKEN>" \ -d '{ "mode": "multiple_item_qa", "include_citations": true, "items": [ { "id": "12345678", "type": "file" } ], "prompt": "List the guidelines on working with responses in Box AI for Documents" }' ``` The resulting answer includes the source file and direct content citations. ``` { "answer": "The guidelines for working with questions in Box AI for Documents are as follows:\n\n1. Box AI pulls information only from the document loaded in preview, and cannot answer questions outside its scope.\n2. Be specific when asking questions; use parameters like numbered lists, brevity, tables, and central themes or key points.\n3. Examples of better phrasing include asking for a numbered list of key points instead of just \"list key points,\" and requesting a succinct outline of important points rather than a general inquiry about the document's purpose.\n4. Stay within the scope of the document and focus on text-based responses only.", "created_at": "2024-11-04T02:35:00.578-08:00", "completion_reason": "done", "citations": [ { "type": "file", "id": "12345678", "name": "Box AI for Documents.docx", "content": "Guidelines for Box AI questions\nBox AI pulls information only from the document you loaded in preview." }, { "type": "file", "id": "12345678", "name": "Box AI for Documents.docx", "content": "If you ask any questions outside of the scope of the document, Box AI informs you that it cannot answer the question with the information provided." }, { "type": "file", "id": "12345678", "name": "Box AI for Documents.docx", "content": "As you ask Box AI to analyze your document, consider these suggestions:\n· Be as specific as possible." }, { "type": "file", "id": "12345678", "name": "Box AI for Documents.docx", "content": "Box AI for Documents\n\nWhen viewing a document in Box, you can ask Box AI to summarize document content, search key points, and write outline drafts based on your document files." } ] } ``` **Reference:** https://developer.box.com/guides/box-ai/ai-tutorials/ask-questions/ --- ## Untitled *Type: guide | Category: Box AI * Override AI model configuration Before you start Make sure you followed the steps listed in getting started with Box AI to create a platform… # Override AI model configuration ## Before you start Make sure you followed the steps listed in [getting started with Box AI](g://box-ai/ai-tutorials/prerequisites) to create a platform app and authenticate. To get more context, read about [agent overrides](g://box-ai/ai-agents/ai-agent-overrides). ## Override prompt This example shows how to use the `prompt_template` parameter to change the query result. The first step is to ask Box AI to summarize a document about Box AI for Documents. The `mode` parameter is set to `single_item_qa` because only one document is supplied. ``` curl -i -L POST "https://api.box.com/2.0/ai/ask" \ -H "content-type: application/json" \ -H "authorization: <Bearer TOKEN>" \ -d '{ "mode": "single_item_qa", "prompt": "Summarize this article about Box AI for Documents", "items": [ { "type": "file", "id": "123467890" } ] }' ``` You will get a response similar to the following: ``` { "answer": "Box AI for Documents is a tool that enhances document analysis by allowing users to summarize content, identify key points, and draft outlines directly from files in Box. It supports various file types, including text documents, spreadsheets, and presentation slides. Users can initiate interactions with Box AI through the web app, where they can select suggestions or type specific questions. Responses are generated in real time, and users have options to save or clear chat history. The document also provides guidelines for effective inquiries and troubleshooting tips for potential issues with using Box AI.", "created_at": "2024-10-08T00:29:07.283-07:00", "completion_reason": "done" } ``` To further improve the result, you can use the `prompt_template` parameter to add some more instructions for Box AI. In this example, let's change the tone of the response. ``` { "prompt": "Summarize this article about Box AI for Documents", "mode": "single_item_qa", "items": [ { "id": "123467890", "type": "file" } ], "ai_agent": { "type": "ai_agent_ask", "basic_text": { "prompt_template": "prompt_template": "{user_question} Write the summary in an informal way.{content}" }, } } } ``` The response would be slightly less formal: ``` { "answer": "Box AI for Documents is a tool that helps you analyze and gain insights from your documents in Box. You can use it to summarize content, identify key points, and draft outlines, making it easier to handle meeting notes, reports, and marketing materials. To get started, just open a file in the Box web app and click the Box AI button. It offers quick suggestions like summarizing the document or checking for next steps. Responses are generated in real time, and you can save them or clear chat history as needed. Just remember, Box AI only pulls info from the document you're viewing, so be specific with your questions!", "created_at": "2024-10-08T00:38:01.767-07:00", "completion_reason": "done" } ``` ## Override AI model (text generation) This example shows you how changing the AI model in the `ai_agent` options can influence the way the text is generated. First let's generate some text using the `POST ai/text_gen` endpoint. This endpoint is using the OpenAI 3.5 turbo model by default. ``` curl -i -L POST "https://api.box.com/2.0/ai/text_gen" \ -H "content-type: application/json" \ -H "authorization: Bearer TOKEN" \ -d '{ "prompt": "Write a short post about Box AI for documents.Make it highlight the benefits of the solution. You can add some emoticons.", "items": [ { "id": "123467890", "type": "file" } ] } ``` The response is as follows: ``` { "answer": "🌟 Exciting News! 🌟\n\nIntroducing Box AI for documents - your new best friend in creating smarter, more efficient content! 🤖💡\n\n🔹 Say goodbye to manual searching and organizing - Box AI does it all for you!\n🔹 Enjoy lightning-fast document analysis and categorization.\n🔹 Boost productivity with automated suggestions and smart recommendations.\n🔹 Collaborate seamlessly with real-time insights and intelligent tagging.\n\nExperience the future of document creation with Box AI - making work easier, faster, and more fun! 🚀💻 #BoxAI #SmartDocuments", "created_at": "2024-10-08T01:19:06.22-07:00", "completion_reason": "done" } ``` Let's change the model using the `ai_agent` configuration: ``` curl -i -L POST "https://api.box.com/2.0/ai/text_gen" \ -H "content-type: application/json" \ -H "authorization: Bearer TOKEN" \ -d '{ "prompt": "Write a short post about Box AI for documents.Make it highlight the benefits of the solution. You can add some emoticons.", "items": [ { "id": "123467890", "type": "file" } ], "ai_agent": { "type": "ai_agent_text_gen", "basic_gen": { "model": "openai__gpt_4o_2024_05_13" } } } ``` After the model switch, the response is slightly different: ``` { "answer": "🚀 **Boost Your Productivity with Box AI for Documents!** 📄✨\n\nSay goodbye to tedious document creation and editing! With Box AI, you can streamline your workflow and focus on what truly matters. Here’s why you’ll love it:\n\n1. **Smart Suggestions** 🤖: Get real-time recommendations to enhance your content.\n2. **Automated Formatting** 📝: Ensure consistency across all your documents effortlessly.\n3. **Collaboration Made Easy** 👥: Work seamlessly with your team, no matter where they are.\n4. **Time-Saving Templates** ⏳: Use pre-built templates to speed up document creation.\n5. **Enhanced Accuracy** ✅: Reduce errors with intelligent proofreading.\n\nTransform the way you work with documents and experience a new level of efficiency with Box AI! 🌟", "created_at": "2024-10-08T01:28:36.777-07:00", "completion_reason": "done" } ``` As you can see the responses differ to some extent. Thanks to the model switch, you can optimize your interaction with Box AI and choose the most suitable model for your needs. ## Override AI model (metadata extraction) Switching models can also give us different results for metadata extraction. Let's use a sample contract to extract the metadata. In this example, the model used is Google Gemini. ``` curl -i -L 'https://api.box.com/2.0/ai/extract' \ -H 'content-type: application/json' \ -H 'authorization: Bearer TOKEN' \ -d '{ "prompt": "Extract any data that would be good metadata to save for future contracts.", "items": [ { "type": "file", "id": "123456789" } ] }' ``` The response is a set of metadata: ``` { "answer": "{\"Buyer Legal Entity Name\": \"Acme Retail Corp.\", \"Supplier Legal Entity Name\": \"Acme Manufacturing Inc.\", \"Buyer Contact Person\": \"Jane Doe\", \"Supplier Contact Person\": \"Eva Smith\", \"Payment Term\": \"payment in full before pickup of goods\", \"Invoice Currency\": \"Euro\", \"Incoterm\": \"FCA Amsterdam\", \"Governing Law\": \"laws state jurisdiction in which supplier is located\", \"Effective Date\": \"March 27, 2024\", \"Buyer Signature Date\": \"March 28th, 2024\", \"Supplier Signature Date\": \"March 28th, 2024\"}", "created_at": "2024-10-08T01:53:14.993-07:00", "completion_reason": "done" } ``` Let's change the model to the most recent OpenAI option: ``` curl -i -L 'https://api.box.com/2.0/ai/extract' \ -H 'content-type: application/json' \ -H 'authorization: Bearer TOKEN' \ -d '{ "prompt": "Extract any data that would be good metadata to save for future contracts.", "items": [ { "type": "file", "id": "123456789" } ], "ai_agent": { "type": "ai_agent_extract", "basic_text": { "model": "openai__gpt_4o_2024_05_13" } } }' ``` Using this model results in a response listing more metadata entries: ``` { "answer": "{\"Effective Date\": \"March 27, 2024\", \"Supplier Legal Entity Name\": \"Acme Manufacturing Inc.\", \"Supplier Registered Office Address\": \"123 Main Street\", \"Supplier Contact Person(s)\": \"Eva Smith\", \"Buyer Legal Entity Name\": \"Acme Retail Corp.\", \"Buyer Registered Office Address\": \"456 Market Avenue\", \"Buyer Contact Person(s)\": \"Jane Doe\", \"Incoterm\": \"FCA Amsterdam\", \"Payment Term\": \"payment in full before pickup of goods\", \"Invoice Currency\": \"Euro\", \"Buyer Printed Name\": \"Jane Doe\", \"Buyer Date\": \"March 28th, 2024\", \"Buyer Title / Position\": \"CEO\", \"Seller Printed Name\": \"Eve Smith\", \"Seller Date\": \"March 28th, 2024\", \"Seller Title / Position\": \"Sales Manager\"}", "created_at": "2024-10-08T01:54:28.099-07:00", "completion_reason": "done" } ``` **Reference:** https://developer.box.com/guides/box-ai/ai-tutorials/default-agent-overrides/ --- ## Untitled *Type: guide | Category: Box AI * Extract metadata from file (structured) With Box AI API, you can extract metadata from the provided file and get the result in the form of… # Extract metadata from file (structured) With Box AI API, you can extract metadata from the provided file and get the result in the form of key-value pairs. As input, you can either create a structure using the `fields` parameter, or use an already defined metadata template. To learn more about creating templates, see [Creating metadata templates in the Admin Console](https://support.box.com/hc/en-us/articles/360044194033-Customizing-Metadata-Templates) or use the [metadata template API](g://metadata/templates/create). ## Before you start Make sure you followed the steps listed in [getting started with Box AI](g://box-ai/ai-tutorials/prerequisites) to create a platform app and authenticate. ## Send a request To send a request, use the `POST /2.0/ai/extract_structured` endpoint. ### Parameters To make a call, you must pass the following parameters. Mandatory parameters are in **bold**. The `items` array can have exactly one element. | Parameter | Description | Example | | --- | --- | --- | | metadata_template | The metadata template containing the fields to extract. For your request to work, you must provide either metadata_template or fields, but not both. | | | metadata_template.type | The type of metadata template. | metadata_template | | metadata_template.scope | The scope of the metadata template that can either be global or enterprise. Global templates are those available to any Box enterprise, whereas enterprise templates are bound to a specific enterprise. | metadata_template | | metadata_template.template_key | The name of your metadata template. | invoice | | items.id | Box file ID of the document. The ID must reference an actual file with an extension. | 1233039227512 | | items.type | The type of the supplied input. | file | | items.content | The content of the item, often the text representation. | This article is about Box AI. | | fields.type | The type of the field. It include but is not limited to string, float, date, enum, and multiSelect. | string | | fields.description | A description of the field. | The person's name. | | fields.displayName | The display name of the field. | Name | | fields.key | A unique identifier for the field. | name | | fields.options | A list of options for this field. This is most often used in combination with the enum and multiSelect field types. | [{"key":"First Name"},{"key":"Last Name"}] | | fields.options.key | A unique identifier for the field. | First Name | | fields.prompt | Additional context about the key (identifier) that may include how to find and format it. | Name is the first and last name from the email address | | ai_agent | The AI agent used to override the default agent configuration. This parameter allows you to, for example, replace the default LLM with a custom one using the model parameter, tweak the base prompt to allow for a more customized user experience, or change an LLM parameter, such as temperature, to make the results more or less creative. Before you use the ai_agent parameter, you can get the default configuration using the GET 2.0/ai_agent_default request. For specific use cases, see the AI model overrides tutorial. | | ## Use cases This example shows you how to extract metadata from a sample invoice in a structured way. Let's assume you want to extract the vendor name, invoice number, and a few more details. ### Create the request To get the response from Box AI, call `POST /2.0/ai/extract_structured` endpoint with the following parameters: - `items.type` and `items.id` to specify the file to extract the data from. - `fields` to specify the data that you want to extract from the given file. - `metadata_template` to supply an already existing metadata template. You can use either `fields` or `metadata_template` to specify your structure, but not both. ### Use fields parameter The `fields` parameter allows you to specify the data you want to extract. Each `fields` object has a subset of parameters you can use to add more information about the searched data. For example, you can add the field type, description, or even a prompt with some additional context. ``` curl --location 'https://api.box.com/2.0/ai/extract_structured' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer <ACCESS_TOKEN>'' \ --data '{ "items": [ { "id": "1517628697289", "type": "file" } ], "fields": [ { "key": "document_type", "type": "enum", "prompt": "what type of document is this?", "options": [ { "key": "Invoice" }, { "key": "Purchase Order" }, { "key": "Unknown" } ] }, { "key": "document_date", "type": "date" }, { "key": "vendor", "description": "The name of the entity.", "prompt": "Which vendor is sending this document.", "type": "string" }, { "key": "document_total", "type": "float" } ] }' ``` The response lists the specified fields and their values: ``` { "document_date": "2024-02-13", "vendor": "Quasar Innovations", "document_total": $1050, "document_type": "Purchase Order" } ``` ### Use metadata template If you prefer to use a metadata template, you can provide its `template_key`, `type`, and `scope`. ``` curl --location 'https://api.box.com/2.0/ai/extract_structured' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer <ACCESS_TOKEN>' \ --data '{ "items": [ { "id": "1517628697289", "type": "file" } ], "metadata_template": { "template_key": "rbInvoicePO", "type": "metadata_template", "scope": "enterprise_1134207681" } }' ``` The response lists the fields included in the metadata template and their values: ``` { "documentDate": "February 13, 2024", "total": "$1050", "documentType": "Purchase Order", "vendor": "Quasar Innovations", "purchaseOrderNumber": "003" } ``` **Reference:** https://developer.box.com/guides/box-ai/ai-tutorials/extract-metadata-structured/ --- ## Untitled *Type: guide | Category: Box AI * Supported AI models Box supports a variety of AI models, categorized along two dimensions: access level and capability tier. Access Levels… # Supported AI models Box supports a variety of AI models, categorized along two dimensions: access level and capability tier. ## Access Levels ### Core Models These models are built into Box AI and available by default for all customers. No configuration is required. ### Customer-Enabled Models These models require activation by Box admins in the Admin Console or a request to Box to enable them. Some models may be subject to additional terms or pricing. ## Capability Tiers ### Standard Models Designed for high-speed, cost-efficient tasks like basic summarization, Q&A, and structured data extraction from shorter or simpler documents. Ideal for high-volume, low-complexity use cases. ### Premium Models Offer more advanced reasoning, larger context windows, and better performance on long-form, complex, or domain-specific content. Suitable for sophisticated tasks like multi-step reasoning, understanding large taxonomies, and analyzing lengthy or unstructured documents. A model can be both customer-enabled and premium, or core and standard. In other words, access level and capability tiers are independent categorizations (for example, models can be either capability tier regardless of access level). The two categorizations are complementary. ## Using models How to use the supported AI models: - get the [default AI agent configuration](e://get_ai_agent_default), - override the AI agent configuration used in [`POST 2.0/ai/ask`](e://post_ai_ask), [`POST 2.0/ai/text_gen`](e://post_ai_text_gen), [`POST 2.0/ai/extract`](e://post_ai_extract), [`POST 2.0/ai/extract_structured`](e://post_ai_extract_structured) endpoints. When using the `model` parameter your API calls, use the **API Name** visible on each tile and model card. For example, to get the AI agent configuration for a specific model, use the [model](e://get-ai-agent-default#param-model) parameter and provide the `azure__openai__gpt_4o_mini` API name. Make sure you use **two underscores** after the provider name. The list may change depending on the model availability. Models offered in **Beta** mode have not been fully performance-tested at scale and are made available on an as-is basis. You may experience variability in model/output quality, availability, and accuracy. ## Core Box AI Models Box AI is powered by the following AI models. These models are integrated with Box AI to facilitate various use cases while adhering to enterprise grade standards. Below, you’ll find information about each model, including its capabilities, intended applications, and applicable usage guidelines. A multimodal model designed to handle lightweight tasks. Default for Box AI for Box Hubs Default for Box AI for Docs Default for Box AI for Box Notes Q&A Chat Available Standard A multimodal model, highly efficient in handling complex, multi-step tasks. Chat Available Premium A multimodal model designed to handle lightweight tasks. Chat Available Standard A multimodal model, highly efficient in handling complex, multi-step tasks. Chat Preview Available Premium A most capable 2nd generation text embedding model. Skilled in text search, code search, and sentence similarity. Embeddings Available Standard Gemini multimodal model with a 1 million token context window and advanced reasoning capabilities. Chat Available Premium Gemini multimodal model designed for optimal for high-volume, high-frequency tasks at scale. Chat Available Standard Gemini multimodal model designed to handle lightweight tasks. Default for Box AI Extract Chat Available Standard A model tailored for various language tasks, including creative writing and conversational AI. Chat Available Standard A model designed for advanced language tasks, focusing on comprehension and context handling. Chat Available Premium A model designed to enhance language understanding and generation tasks. Chat Available Premium A model designed to enhance language understanding and generation tasks Chat Available Premium A model that brings frontier performance to everyday use cases. Chat Available Premium A model that excels at coding and complex problem-solving, powering frontier agent products. Chat Available Premium A model capable of advanced language processing, handling extensive contexts, making it suitable for complex tasks. Chat Available Standard A model built for document-level understanding, interpretation of charts and graphs, and captioning of images. Chat Available Standard A natively multimodal AI model that enables text and multimodal experiences. Chat Available Standard ## Customer-enabled models Certain Box AI customers may enable additional AI models upon their request and/or otherwise made available to them through their admin console. Use of these models may be subject to additional terms. By selecting a customer-enabled model, customer acknowledges that their data may be processed by additional [subprocessors](https://www.box.com/legal/subprocessors) of their choice. A model that excels at enterprise use cases like data extraction, coding, and text summarization. Chat Beta Premium A lightweight model that is great for logic-based tasks that do not require deep domain knowledge. Chat Beta Premium A multimodal model, highly efficient in handling complex, multi-step tasks. Beta Premium **Reference:** https://developer.box.com/guides/box-ai/ai-models/ --- ## Untitled *Type: guide | Category: Box AI * Extract metadata from file (freeform) Box AI API allows you to query a document or image and extract metadata based on a provided prompt… # Extract metadata from file (freeform) Box AI API allows you to query a document or image and extract metadata based on a provided prompt. **Freeform** means that the prompt can include a stringified version of formats such as JSON or XML, or even plain text. ## Before you start Make sure you followed the steps listed in [getting started with Box AI](g://box-ai/ai-tutorials/prerequisites) to create a platform app and authenticate. ## Send a request To send a request, use the `POST /2.0/ai/extract` endpoint. ### Parameters To make a call, you must pass the following parameters. Mandatory parameters are in **bold**. The `items` array can have exactly one element. | Parameter | Description | Example | | --- | --- | --- | | prompt | The request for Box AI to generate or refine the text. The prompt's length cannot exceed 10000 characters. | Create a meeting agenda for a weekly sales meeting. | | items.id | Box file ID of the document. The ID must reference an actual file with an extension. | 1233039227512 | | items.type | The type of the supplied input. | file | | items.content | The content of the item, often the text representation. | This article is about Box AI. | | ai_agent | The AI agent used to override the default agent configuration. This parameter allows you to, for example, replace the default LLM with a custom one using the model parameter, tweak the base prompt to allow for a more customized user experience, or change an LLM parameter, such as temperature, to make the results more or less creative. Before you use the ai_agent parameter, you can get the default configuration using the GET 2.0/ai_agent_default request. For specific use cases, see the AI model overrides tutorial. | | ## Use cases This example shows you how to extract metadata from a sample invoice. ### Create the request To get the response from Box AI, call `POST /2.0/ai/extract` endpoint with the following parameters: - `prompt` that can be a query, or a structured or unstructured list of fields to extract. - `type` and `id` of the file to extract the data from. ### Create the prompt Depending on the use case and the level of detail, you can construct various prompts. #### Use plain text Because this endpoint allows freeform prompts, you can use plain text to get the information. ``` curl --location 'https://api.box.com/2.0/ai/extract' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer <ACCESS_TOKEN>' \ --data '{ "prompt": "find the document type (invoice or po), vendor, total, and po number", "items": [ { "type": "file", "id": "1443721424754" } ] }' ``` In such a case, the response will be based on the keywords included in the text: ``` { "answer": "{\"Document Type\": \"Invoice\", \"Vendor\": \"Quasar Innovations\", \"Total\": \"$1,050\", \"PO Number\": \"003\"}", "created_at": "2024-05-31T10:30:51.223-07:00", "completion_reason": "done" } ``` #### Use specific terms If you don't want to write the entire sentence, the prompt can consist of terms that you expect to find in an invoice: ``` curl --location 'https://api.box.com/2.0/ai/extract' \ --header 'Content-Type: application/json' \ --header 'Authorization: <ACCESS_TOKEN>' \ --data '{ "prompt": "{\"vendor\",\"total\",\"doctype\",\"date\",\"PO\"}", "items": [ { "type": "file", "id": "1443721424754" } ] }' ``` Using this approach results in a list of terms provided in the request and their values: ``` { "answer": "{\"vendor\": \"Quasar Innovations\", \"total\": \"$1,050\", \"doctype\": \"Invoice\", \"PO\": \"003\"}", "created_at": "2024-05-31T10:28:51.906-07:00", "completion_reason": "done" } ``` #### Use key-value pairs The prompt can also be a list of key-value pairs that helps Box AI to come up with the metadata structure. This approach requires listing the key-value pairs within a `fields` array. ``` curl --location 'https://api.box.com/2.0/ai/extract' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer <ACCESS_TOKEN>' \ --data '{ "prompt": "{\"fields\": [{\"key\":\"vendor\",\"displayName\":\"Vendor\",\"type\":\"string\",\"description\":\ "Vendorname\"},{\"key\":\"documentType\",\"displayName\":\"Type\",\"type\":\"string\",\"description\":\"\"}]}", "items": [ { "type": "file", "id": "1443721424754" } ] }' ``` The response includes the `fields` present in the file, along with their values: ``` { "answer": "{\"vendor\": \"Quasar Innovations\", \"documentType\": \"Invoice\"}", "created_at": "2024-05-31T10:15:38.17-07:00", "completion_reason": "done" } ``` **Reference:** https://developer.box.com/guides/box-ai/ai-tutorials/extract-metadata/ --- ## Untitled *Type: guide | Category: Box AI * Generate text with Box AI Box AI API is available to all Enterprise Plus and Enterprise Advanced customers. You can use Box AI to generate… # Generate text with Box AI Box AI API is available to all Enterprise Plus and Enterprise Advanced customers. You can use Box AI to generate text based on provided content. For example, you can ask Box AI to generate a template based on the content you read or create in Box Notes. Then you can embed the generated text directly into your document. ## Before you start Make sure you followed the steps listed in [getting started with Box AI](g://box-ai/ai-tutorials/prerequisites) to create a platform app and authenticate. ## Send a request To send a request, use the `POST /2.0/ai/text_gen` endpoint. ### Parameters To make a call, you must pass the following parameters. Mandatory parameters are in **bold**. **Note**: The `items` array can have exactly one element. | Parameter | Description | Example | | --- | --- | --- | | prompt | The request for Box AI to generate or refine the text. The prompt's length cannot exceed 10000 characters. | Create a meeting agenda for a weekly sales meeting. | | items.id | Box file ID of the document. | 1233039227512 | | items.type | The type of the supplied input. | file | | items.content | The content of the item, often the text representation. | This article is about Box AI. | | dialogue_history.prompt | The prompt previously provided by the client and answered by the Large Language Model (LLM). | Make my email about public APIs sound more professional | | dialogue_history.answer | The answer previously provided by the LLM. | Here is a draft of your professional email about public APIs. | | dialogue_history.created_at | The ISO date formatted timestamp of when the previous answer to the prompt was created. | 2012-12-12T10:53:43-08:00 | | ai_agent | The AI agent used to override the default agent configuration. This parameter allows you to, for example, replace the default LLM with a custom one using the model parameter, tweak the base prompt to allow for a more customized user experience, or change an LLM parameter, such as temperature, to make the results more or less creative. Before you use the ai_agent parameter, you can get the default configuration using the GET 2.0/ai_agent_default request. For specific use cases, see the AI model overrides tutorial | | ## Use cases Generate text based on the provided file content and a prompt. ``` curl -i -L POST "https://api.box.com/2.0/ai/text_gen" \ -H "content-type: application/json" \ -H "authorization: Bearer <ACCESS_TOKEN>" \ -d '{ "items": [ { "id": "12345678", "type": "file" } ], "prompt": "Create a short blog post that provides information on Box AI for Documents and focuses on best practices for asking questions. You can add emoticons, but not too many." }' ``` The result will be as follows: ``` { "answer": "📝 **Box AI for Documents: Best Practices for Asking Questions** 🤔\n\n---\n\nWelcome to our blog post on Box AI for Documents! 🎉 Today, we're going to dive into some best practices when it comes to asking questions within this innovative platform.\n\n1. **Be Clear and Concise**: When formulating a question in Box Notes, make sure your query is clear and to the point. This helps Box AI understand exactly what you're looking for.\n\n2. **Provide Context**: Giving context around your question can significantly improve the accuracy of the response generated by Box AI. Include relevant details or background information.\n\n3. **Use Keywords**: Utilize keywords related to your query within the question itself. This can help Box AI better identify the main topic of your inquiry.\n\n4. **Avoid Ambiguity**: Try to avoid vague or ambiguous questions that could lead to misunderstandings. The more precise you are, the better Box AI can assist you.\n\n5. **Review Suggestions Carefully**: After receiving suggestions from Box AI, take the time to review them carefully before incorporating them into your document. Ensure they align with your intended message.\n\nBy following these best practices, you can maximize the effectiveness of Box AI for Documents and streamline your workflow like never before! 💼✨\n\nStay tuned for more tips and tricks on leveraging technology for enhanced productivity! 👩‍💻🚀", "created_at": "2024-11-04T02:46:23.459-08:00", "completion_reason": "done" } ``` **Reference:** https://developer.box.com/guides/box-ai/ai-tutorials/generate-text/ --- ## Untitled *Type: guide | Category: Box AI * Get started with Box AI To implement Box AI API in your solutions, you need to make sure you have access to the functionality. You will also… # Get started with Box AI To implement Box AI API in your solutions, you need to make sure you have access to the functionality. You will also need a platform application with enabled Box AI scope, and a developer token to authenticate your calls. To use Box AI API, make sure it is enabled by an admin in the Admin Console. If you want to use the Box AI APIs in your sandbox, request access from the Box AI team using [this form](https://forms.gle/Nsh3TwM3W8qg4U35A). ## Create a platform application First you need to create a platform application you will use to make calls. To create an application, follow the guide on [creating platform apps](g://applications/app-types/platform-apps). ## Enable Box AI API access To interact with Box AI API, you need the `ai.readwrite` [scope](g://api-calls/permissions-and-errors/scopes) added for your application. Before you add the scope, make sure that the Box Admin has granted you the access to Box AI API. If you can't see the **Manage AI** option in your app configuration settings, contact your admin. To add a scope: Open your application in Developer Console. Go to **Configuration** > **Required Access Scopes** > **Content Actions** Select the **Manage AI** scope. Box Platform will automatically include the scope when making the call. If you are added as an collaborator for a given app, but do not have Box AI API access, you will see the **Manage AI** scope checked and grayed out. This means the app owner has the AI scope enabled but you cannot change this setting. Submit your app for [authorization or enablement](g://authorization). If you want to enable Box AI API for an existing application, you must [re-authorize](g://authorization/custom-app-approval#re-authorization-on-changes) it. ## Generate a developer token You need a developer token to authenticate your app when sending requests. To generate a token: 1. Go to **Developer Console** > **My Platform Apps**. 2. Click the **Options menu** button (…) on the right. 3. Select **Generate Developer Token**. The token will be automatically generated and saved to clipboard. You can also open your app, go to **Configuration** > **Developer Token** and generate the token. A developer token is only valid for one hour. For additional details, see [developer token](g://authentication/tokens/developer-tokens). After you generate the token, you can use it in cURL or other clients, such as [Postman](g://tooling/postman), to make calls. **Reference:** https://developer.box.com/guides/box-ai/ai-tutorials/prerequisites/ --- ## Untitled *Type: guide | Category: Box AI * Box AI tutorials The listed tutorials provide you with an overview and use cases for Q&A and text generation, metadata extraction, and AI… # Box AI tutorials The listed tutorials provide you with an overview and use cases for Q&A and text generation, metadata extraction, and AI model configuration overrides. **Reference:** https://developer.box.com/guides/box-ai/ai-tutorials/ --- ## Untitled *Type: guide | Category: Box MCP Server * Remote Box MCP Server The remote Box MCP Server allows third party AI systems to securely connect and interact with your content in Box. Box… # Remote Box MCP Server The remote Box MCP Server allows third party AI systems to securely connect and interact with your content in Box. Box MCP Server offered subject to Box’s Main Beta Agreement, meaning the available capabilities may change at any time. Box MCP Server is available for customers with Enterprise Plus or Enterprise Advanced plans. ## Access and manage predefined Box MCP Servers 1. Click **Integrations** in the sidebar of Box Admin Console. 2. Click **Box Integrations & Clients** in the Integrations window. 3. Scroll down to **Individual Integration Controls**. 4. Search for predefined Box MCP Server, for example the **Box MCP Server for Copilot Studio (Beta)**. 5. Hover on the chosen integration, then click **Configure**. 6. Click Save. ## Create an unlisted Box MCP Server To create a new, unlisted Box MCP Server: 1. Click Integrations in the sidebar of Box Admin Console. 2. Scroll down to **Individual Integration Controls** in the Box Integrations & Clients tab. 3. Search for **Box MCP Server**. 4. Hover on the **Box MCP Server** application, then click **Configure**. 5. In the **Additional Configuration** section, click on **+ Add Integration Credentials**. 6. Enter the Redirect URI provided by the external MCP Client. 7. Copy the generated Client ID and Client Secret. 8. Under Scopes, ensure that *Manage AI Requests* is selected. ## Add Box MCP Server to MCP clients To connect to Box from the AI Agent platform, you need to: - Add an endpoint URL: `https://mcp.box.com` - Pass an MCP name: `box-remote-mcp` - Provide an `authorization_token` Exact steps may vary depending on the AI platform. Refer to your platform’s documentation for client-side setup instructions. Check this sample code for reference: ``` response = await client.beta.messages.create( model="claude-3-opus-20240229", # Or your preferred model max_tokens=4096, messages=conversation_history, mcp_servers=[ { "type": "url", "url": "https://mcp.box.com", "name": "box-remote-mcp", "authorization_token": BEARER_TOKEN, } ], betas=["mcp-client-2025-04-04"] ) ``` ### Anthropic's Messages API Connect the remote Box MCP Server with Anthropic's Messages API (beta). Clone [this sample chat bot project](https://github.com/box-community/remote-box-mcp-anthropic) to get started quickly. It allows you to have a conversation with an Anthropic model, which has access to tools provided by the Box remote MCP server. The chatbot runs in a terminal, maintains conversation history for context-aware responses, and uses `asyncio` for asynchronous operation. ### Copilot Studio To add an MCP server, follow the instructions provided by the Copilot Studio side. Detailed steps and guidance are available in the official Microsoft documentation: [Add an MCP Server in Copilot Studio](https://learn.microsoft.com/en-us/microsoft-copilot-studio/agent-extend-action-mcp#add-tools-from-an-existing-mcp-connector-to-an-agent). ### Azure API Center To add an MCP server in Azure API Center's Enterprise Registry, follow the instructions provided by Azure. Detailed steps and guidance are available in the official Microsoft documentation: [Add an MCP Server in Azure API Center's Enterprise Registry](https://learn.microsoft.com/en-us/azure/api-center/register-discover-mcp-server). ### Using Box AI Features with the MCP Server When you use Box AI in third party applications, you can achieve the best experience and highest quality results by accessing the applications through the Box MCP Server. This ensures full functionality, improved performance, and a seamless user experience. ## Available tools Remote Box MCP Server includes several tools you can use: | Tool | Description | | --- | --- | | box-remote-mcp_who_am_i | Returns detailed information about the currently authenticated Box user. | | box-remote-mcp_search_folders_by_name | Searches for folders within Box by name using keyword matching. | | box-remote-mcp_list_folder_content_by_folder_id | Lists files, folders, and web links in a folder. | | box-remote-mcp_search_files_keyword | Searches for files using keywords. Supports metadata filters, file extension filtering, and field selection. | | box-remote-mcp_search_files_metadata | Searches for files using SQL-like metadata queries. Supports complex filtering with parameters, field selection, and folder scoping. | | box-remote-mcp_ai_qa_single_file | Asks a question to a single file using Box AI. | | box-remote-mcp_ai_qa_multi_file | Asks a question to multiple files using Box AI. | | box-remote-mcp_ai_qa_hub | Asks a question to a Box Hub using Box AI. | | box-remote-mcp_ai_extract_freeform | Extracts metadata from files using Box AI in freeform format without requiring predefined template structures. | | box-remote-mcp_ai_extract_structured | Extracts structured metadata from files using Box AI based on either custom fields definition or an existing metadata template. | **Reference:** https://developer.box.com/guides/box-mcp/remote/ --- ## Untitled *Type: guide | Category: Box MCP Server * Self-hosted Box MCP Server The Self-hosted Box MCP Server is a Python project that integrates with the Box API to perform various operations… # Self-hosted Box MCP Server The [Self-hosted Box MCP Server](https://github.com/box-community/mcp-server-box.git) is a Python project that integrates with the Box API to perform various operations such as file search, text extraction, AI-based querying, and data extraction. It leverages the Box Python Next Gen SDK library and provides a set of tools to interact with Box files and folders. ## Installation ### Prerequisites - Python 3.13 or higher - Box Platform app credentials (Client ID, Client Secret) Follow the steps from this section to set up the self-hosted Box MCP Server. 1. Clone the repository: ``` git clone https://github.com/box-community/mcp-server-box.git cd mcp-server-box ``` 1. Install `uv` if it's not installed on your machine: ``` curl -LsSf https://astral.sh/uv/install.sh | sh ``` ``` powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" ``` Restart your terminal afterwards to ensure that the `uv` command gets picked up. 1. Create and set up our project: ``` # Create virtual environment and activate it uv venv source .venv/bin/activate # Lock the dependencies uv lock ``` ``` # Create virtual environment and activate it uv venv .venv\Scripts\activate # Lock the dependencies uv lock ``` 1. Create a `.env` file in the root directory and fill your Box Platform app credentials: ``` BOX_CLIENT_ID=your_client_id BOX_CLIENT_SECRET=your_client_secret ``` You can also watch a video tutorial and see example usage of Box MCP tools. ## Running Box MCP Server locally To start the Box MCP Server, run the following command: ``` uv --directory /Users/USER_NAME/PATH_TO_PROJECT/mcp-server-box run src/mcp_server_box.py ``` Update the path so it reflects your local setup. ### Use Cursor as the Box MCP client Prerequisites: - Download [Cursor desktop app](https://www.cursor.com/) Follow these instructions to start using Box MCP Sever with Cursor: 1. Open the Cursor app. 2. Click the cog icon to open settings. 3. Select `MCP` within the Cursor Settings tab. 4. Click the `Add new global MCP server` button. This opens the `mcp.json` file. 5. Update the values with your local setup and paste the following JSON: ``` { "mcpServers": { "box": { "command": "uv", "args": [ "--directory", "/Users/USER_NAME/PATH_TO_PROJECT/mcp-server-box", "run", "src/mcp_server_box.py" ] } } } ``` 1. Save and close the `mcp.json` file. 2. Restart Cursor if necessary. 3. Use the `box_authorize_app_tool` tool to start using Box MCP. ### Use Claude for Desktop as the Box MCP client Prerequisites: - Download [Claude for Desktop](https://claude.ai/download) client - Optionally, set up [`code`](https://code.visualstudio.com/docs/setup/mac#_configure-the-path-with-vs-code) command for VS Code Follow these steps to set up Box MCP in Claude for Desktop: 1. Edit your `claude_desktop_config.json`. You can run this command in your terminal: ``` code ~/Library/Application\ Support/Claude/claude_desktop_config.json ``` Alternatively, in the main Claude navigation choose `Settings`. Select the Developers tab and click `Edit Config`. This opens a folder window containing `claude_desktop_config.json`. 1. Add the configuration: ``` { "mcpServers": { "mcp-server-box": { "command": "uv", "args": [ "--directory", "/Users/Users/USER_NAME/PATH_TO_PROJECT/mcp-server-box", "run", "src/mcp_server_box.py" ] } } } ``` 1. Restart Claude for Desktop. 2. Authenticate the Box MCP Server using `box_authorize_app_tool` tool. ## Available tools ### Authentication and user tools | Tool | Description | Parameters | Returns | | --- | --- | --- | --- | | box_who_am_i | Gets the current user information and checks the connection status. | None | User information string. | | box_authorize_app_tool | Starts the Box application authorization process. | None | Authorization status message. | ### Search and navigation tools | Tool | Description | Parameters | Returns | | --- | --- | --- | --- | | box_search_tool | Search for files in Box | - query (str): Search query. - file_extensions (List[str], optional): Filter by extensions. - where_to_look_for_query (List[str], optional): Locations to search. - ancestor_folder_ids (List[str], optional): Folder IDs to limit the search. | Newline-separated list of file names and IDs | | box_search_folder_by_name | Locate a folder by name | folder_name (str): Name of the folder | Folder ID and information | | box_list_folder_content_by_folder_id | List folder contents | folder_id (str): ID of the folder. is_recursive (bool): Whether to list recursively. | Folder content in JSON format | ### File operations | Tool | Description | Parameters | Returns | | --- | --- | --- | --- | | box_read_tool | Read the text content of a Box file | - file_id (str): ID of the file to read. | File content | | box_upload_file_from_path_tool | Upload a file from local path | file_path (str): Local file path. folder_id (str, optional): Destination folder ID. new_file_name (str, optional): New file name. | File details or error message | | box_upload_file_from_content_tool | Upload content as a file | content (str|bytes): Content to upload. file_name (str): File name. folder_id (str, optional): Destination folder ID. is_base64 (bool, optional): If content is base64 encoded. | Upload success message | | box_download_file_tool | Download a file from Box | file_id (str): File ID. save_file (bool, optional): Whether to save locally. save_path (str, optional): Local save path. | File content or save confirmation | ### Folder Management | Tool | Description | Parameters | Returns | | --- | --- | --- | --- | | box_manage_folder_tool | Create, update, or delete folders | - action (str): create, delete, or update. - folder_id (str, optional): Folder ID. - name (str, optional): Folder name. - parent_id (str, optional): Parent folder ID. - description (str, optional): Folder description. - recursive (bool, optional): For recursive delete. Status message with folder details ### Box AI tools Tool | Description | | box_ask_ai_tool | Ask Box AI about a file | file_id (str): File ID. prompt (str): Question for the AI. | AI response | | box_ask_ai_tool_multi_file | Query Box AI using multiple files | file_ids (List[str]): List of file IDs. prompt (str): Instruction for AI. | AI-generated answer | | box_hubs_ask_ai_tool | Ask Box AI about a hub | hubs_id (str): ID of the hub. prompt (str): Question for the AI. | AI response | | box_ai_extract_data | Extract data from a file using AI | file_id (str): File ID. fields (str): Fields to extract. | Extracted data in JSON format | ### Box Doc Gen tools | Tool | Description | Parameters | Returns | | --- | --- | --- | --- | | box_docgen_create_batch_tool | Generate documents using a template | - file_id (str): Template file ID. - destination_folder_id (str): Output folder ID. - user_input_file_path (str): JSON input data path. - output_type (str, optional): Output format. | Batch generation result | | box_docgen_get_job_tool | Fetch a Doc Gen job by ID | job_id (str): Job identifier | Job details in JSON | | box_docgen_list_jobs_tool | List all Doc Gen jobs | - marker (str, optional): Pagination marker. - limit (int, optional): Max jobs to return. | List of jobs in JSON | | box_docgen_list_jobs_by_batch_tool | List jobs in a specific batch | - batch_id (str): Batch identifier. - marker (str, optional): Pagination marker. - limit (int, optional): Max jobs to return. | Batch jobs details | | box_docgen_template_create_tool | Mark a file as a template | file_id (str): File ID to mark | Template details | | box_docgen_template_list_tool | List all available templates | - marker (str, optional): Pagination marker. - limit (int, optional): Max templates to list. | List of templates | | box_docgen_template_delete_tool | Remove template marking | template_id (str): Template identifier | Deletion confirmation | | box_docgen_template_get_by_id_tool | Get template details | template_id (str): Template identifier | Template details | | box_docgen_template_list_tags_tool | List template tags | - template_id (str): Template ID. - template_version_id (str, optional): Version ID. - marker (str, optional): Pagination marker. - limit (int, optional): Max tags to return. | List of tags | | box_docgen_template_list_jobs_tool | List jobs using a template | - template_id (str): Template identifier. - marker (str, optional): Pagination marker. - limit (int, optional): Max jobs to list. | Job details | ### Box Metadata tools | Tool | Description | Parameters | Returns | | --- | --- | --- | --- | | box_metadata_template_get_by_key_tool | Retrieves a metadata template by its key. | template_name (str): The key of the metadata template to retrieve. | The metadata template associated with the provided key. | | box_metadata_template_get_by_name_tool | Retrieve a metadata template by its name. | template_name (str): The name of the metadata template to retrieve. | The metadata template associated with the provided name. | ## Troubleshooting In case of an `Error: spawn uv ENOENT` on MacOS when running the MCP server with Claude Desktop, you can: - Remove `uv` and reinstall it with Homebrew: `brew install uv`. - Provide the full path to the `uv` executable in your configuration: ``` /Users/USER_NAME/.local/bin/uv --directory /Users/USER_NAME/local/mcp-server-box run src/mcp_server_box.py ``` In case of additional issues related to setup, post your question on our [Developer Community forum](https://community.box.com/ai-developers-23). **Reference:** https://developer.box.com/guides/box-mcp/self-hosted/ --- ## Untitled *Type: guide | Category: CLI * Box CLI with JWT Authentication Previously, our Box CLI Quick Start Guide, followed the JWT or server authentication application setup flow… # Box CLI with JWT Authentication Previously, our [Box CLI Quick Start Guide](g://cli/quick-start), followed the JWT or server authentication application setup flow. Since we released a new OAuth 2.0 version of the Box CLI, we updated it to use the new feature. We migrated the original JWT setup instructions here, in case you would still like to use the server authentication application type. ## Setup a JWT application The first step to using the CLI with server authentication is creating a Box application in the [Developer Console](https://account.box.com/developers/console), which the CLI can use behind the scenes to make API calls. If you would like to associate your CLI with an existing JWT application you can skip this step. However, you will want to ensure that, at a minimum, the following scopes are set in the **Configuration** tab of your application: - Read all files and folders stored in Box - Write all files and folders stored in Box From the left-hand navigation panel on your All Files page, open the [Developer Console](https://account.box.com/developers/console). If this is your first time using the Box API and this option is not already available, you can add it to your account by clicking [here](https://account.box.com/developers/console). Click **Create Platform App** > **Platform App** > **Server Authentication (with JWT)** > name the application > **Create App** Server Authentication (with JWT) always requires Admin authorization before use. ## Configure the application This will bring you to the application’s configuration page where you need to choose its access and permissions. Again, keep in mind that because of the application’s authentication type, it will require Admin approval. At a minimum, you will need the following [scopes](g://api-calls/permissions-and-errors/scopes): - Read all files and folders stored in Box - Write all files and folders stored in Box You can choose either App Access Only or App + Enterprise Access as the [application access](g://authentication/jwt/jwt-setup/#application-access). ## Authorize the application All applications leveraging Server Authentication must be authorized in the Admin Console before making successful API calls. This is because all JWT applications have a [Service Account](page://platform/user-types/#service-account), which, based on the applications [scopes](g://api-calls/permissions-and-errors/scopes), may be able to perform Admin actions. Steps for developers and Admins can be found in our [authorization guide](g://authorization/custom-app-approval). If you would like more information on how scopes, application access, tokens, and permissions work together, please see our article on understanding [Box's security mechanisms](https://medium.com/box-developer-blog/box-api-understanding-security-9fcad7b1d72e). If configuration changes are made to this application, it will need to be reauthorized in order for the changes to take effect. You will know when an application is ready for use by visiting its Authorization tab in the [Developer Console](https://account.box.com/developers/console). The state and status must be enabled and authorized. ## Download Required Data The CLI needs a configuration file stored locally in order to make API calls. To download the configuration file, visit the **Configuration** tab in the [Developer Console](https://account.box.com/developers/console). Click **Generate a Public/Private Keypair**, which will send you through 2FA verification before automatically downloading the configuration file for your application. For more information see our [guide](g://authentication/jwt/jwt-setup/#public-and-private-key-pair). For security reasons 2FA must be enabled on your Box account to successfully generate a public/private keypair. Locate the downloaded file on your machine which has a default name in the format: `EnterpriseID_publicKeyID_config.json`. You may leave this name or choose to rename it. This guide assumes the file is renamed to `config.json`. It is critical you place the file in a location where it will not be inadvertently deleted or moved. If this occurs you will need to repeat the step 2 to reconfigure the CLI. ## CLI Installation and Configuration Installers are available for Windows and macOS. However, the raw source-code is available if you would like to build the CLI in other environments. ## Windows & macOS Installers To install the latest CLI on your machine, download the latest `.exe` for Windows or `.pkg` for macOS for the latest release. Download the latest CLI installer ## Linux & Node install Additionally, the CLI can be installed as a Node package on any platform, including Linux. For this to work you will need to have [Node JS](https://nodejs.org/) installed on your machine. ``` npm install --global @box/cli ``` ## Source Code The source code for the CLI is available via [GitHub](https://github.com/box/boxcli). ## Run configuration command You will now need to configure the CLI to point to to the configuration file downloaded in step 1. Open your terminal or command line and execute the command: `box configure:environments:add PathToConfigFileHere`, replacing `PathToConfigHere` with the path to your `config.json` file. For example: `box configure:environments:add /Users/ExampleUser/Documents/CLI/config.json` You can drag the csv file from the Finder/File Explorer to the terminal/command line window to auto-populate the path. ## Confirm configuration To confirm successful configuration, use the command `box users:get`. A successful response will provide details about the [Service Account](page://platform/user-types/#service-account) user associated with your [Access Token](g://authentication/tokens): ``` Type: user ID: ''0123456789'' Name: Box CLI - Quickstart Example Login: AutomationUser_123456_8jSo6Lqvko@boxdevedition.com Created At: '2020-01-01T09:45:01-07:00' Modified At: '2021-03-01T09:30:05-07:00' Language: en Timezone: America/Los_Angeles Space Amount: 999999999999999 Space Used: 6291500 Max Upload Size: 16106127360 Status: active Job Title: '' Phone: '' Address: example+user@box.com Avatar URL: '' Notification Email: [] ``` By default, JWT applications automatically obtain an Access Token for the Service Account. It is possible to change the default user, but this guide assumes you do not do this. ## Next Steps - You can checkout the [commands](https://github.com/box/boxcli#command-topics) page on GitHub for example code. - You can also go to the second part of the [OAuth 2.0 Quick Start](g://cli/quick-start/build-commands-help) for a tutorial on how to use the commands. **Reference:** https://developer.box.com/guides/cli/cli-docs/jwt-cli/ --- ## Untitled *Type: guide | Category: CLI * CLI guides Here you can find guides on how to use Box CLI features and capabilities. Set up and test Box CLI with JWT authentication. Set up… # CLI guides Here you can find guides on how to use Box CLI features and capabilities. - Set up and test [Box CLI with JWT authentication](g://cli/cli-docs/jwt-cli). - Set up and use [Box CLI bulk commands](g://cli/cli-docs/bulk-commands). **Reference:** https://developer.box.com/guides/cli/cli-docs/ --- ## Untitled *Type: guide | Category: CLI * CLI bulk commands The Box CLI bulk commands help you to automate repetitive tasks. By adding a --bulk-file-path flag to your command, you… # CLI bulk commands The Box CLI bulk commands help you to automate repetitive tasks. By adding a `--bulk-file-path` flag to your command, you are able to perform actions for many items at once. For example, this command creates several folders using a CSV file specifying folder `Name`, `Description`, and the `ParentId` of the parent folder: ``` box folders:create --bulk-file-path <PATH_TO_CSV>/folders-create.csv ``` ## CSV templates Predefined CSV templates help you to structure the data you want to manage in bulk. Templates reside in the [`Bulk actions`](https://github.com/box/boxcli/tree/main/docs/Bulk%20actions) directory, grouped into folders. The table below lists the currently available templates. | Templates | Description | | --- | --- | | box collaborations | Create, delete, and update collaborations. | | box files | Download, update, and upload files. | | box folders | Create and update folders, add metadata to folders. | | box groups | Create groups and add memberships. | | box metadata-templates | Create metadata templates and metadata cascade policies on folders. | | box shared-links | Delete shared links. | | box users | Create and update users, move one user's content to another user. | | box webhooks | Delete webhooks. | ## Prerequisites To use bulk commands, you will need a Box application with OAuth 2.0 authentication. If you don't have one, go to your [Developer Console](https://app.box.com/developers/console), and follow the guide [Setup with OAuth 2.0](g://authentication/oauth2/oauth2-setup). ## Set up and use bulk commands Clone the `boxcli` GitHub repository or download the files from [`Bulk actions`](https://github.com/box/boxcli/tree/main/docs/Bulk%20actions) directory. ``` git clone https://github.com/box/boxcli.git ``` Adjust the `.csv` template to your needs. For example, if you want to create several folders, you can use the [`folders-create.csv`](https://github.com/box/boxcli/blob/main/docs/Bulk%20actions/folders/folders-create.csv) template as your starting point. Run the command. ``` box users:create --bulk-file-path <PATH_TO_CSV>/folders-create.csv ``` **Reference:** https://developer.box.com/guides/cli/cli-docs/bulk-commands/ --- ## Untitled *Type: quick-start | Category: CLI * Building Commands and Help Feature A full list of CLI commands and usage information can be found in the GitHub repository. Only Service… # Building Commands and Help Feature A full list of CLI commands and usage information can be found in the [GitHub repository](https://github.com/box/boxcli#command-topics-1). Only Service Accounts and Admins are able to use some commands. If your user is not authorized with the necessary scopes or you configured your CLI to obtain a default token for another user, calls may fail. Add `-v` or `--verbose` to your command for verbose error logging. If you do not see a command for an endpoint you need, you can build a [custom request](https://github.com/box/boxcli/blob/master/docs/request.md). Use repository documentation in conjunction with reference documentation to see information the help command does not provide. This includes restrictions, token permission requirements, fields, etc. ## First: Reset browser storage Now that you've imported the Box API credentials into the CLI you should take a moment to remove these credentials from your browser's storage. Clear credentials Removing your API credentials from the browser storage ensures that no other script can read your **Client ID** or **Client Secret** ## Creating a folder with help Every CLI command begins with `box`. Add the option `--help` to any command for help building it. For example, executing `box --help` will bring you to a list of all possible object commands. Options are discussed more in [step 4](g://cli/quick-start/options-and-bulk-commands/#options). Then, for example, use the folder object and execute the command `box folders --help`. This provides a list of all eligible actions for this object. Discover the required arguments for creating a folder: `box folders:create --help` Execute the command `box folders:create 0 "My CLI Folder"` and note the folder ID returned in the response. The root level of the folder tree, the All Files page, is always represented by folder ID 0. Log into **your** Box account. Can you see this folder in your folder tree? If you set up the Box CLI using JWT authentication, you will not see the folder in your Box account. The folder will live in the service account of the application that was created after application approval. ## Summary - You used the **help** feature to create a folder I created my first folder **Reference:** https://developer.box.com/guides/cli/quick-start/build-commands-help/ --- ## Untitled *Type: quick-start | Category: CLI * Use Box CLI with OAuth 2.0 The Box CLI is a user-friendly command line tool which allows both technical and non-technical users to leverage… # Use Box CLI with OAuth 2.0 The Box CLI is a user-friendly command line tool which allows both technical and non-technical users to leverage the Box API to perform routine or bulk actions. There is no need to write any code, as these actions are executed through a set of [commands](https://github.com/box/boxcli#command-topics). ## Overview This guide will take you through the following steps. 1. [Create and configure](g://cli/quick-start/create-oauth-app) a Box application 2. [Install and configure](g://cli/quick-start/install-and-configure) the CLI 3. [Run commands](g://cli/quick-start/build-commands-help) with the CLI 4. Use [options and bulk commands](g://cli/quick-start/options-and-bulk-commands) 5. Explore automation using [Powershell scripts with the CLI](g://cli/quick-start/powershell-script-templates) 6. [Next steps](g://cli/quick-start/next-steps) I am ready to get started **Reference:** https://developer.box.com/guides/cli/quick-start/ --- ## Untitled *Type: quick-start | Category: CLI * Configure a Box App To use the Box CLI you will need to create a Box App. A Box App is an application that can be used for making API calls… # Configure a Box App To use the **Box CLI** you will need to create a **Box App**. A Box App is an application that can be used for making API calls. When using the Box CLI you can choose to either set up your own Box App or use our preconfigured one. The key benefit of setting up your own Box App is that you won't need to go through login every hour, but it does require a few extra steps to set up. ## Select a Box App to use # Create new Box App We can set up a Box App for you right here from the documentation. With a few clicks you will be ready to go! # Use existing Box App If you've already created a Box App before that you want to use, then we can use the credentials for that application. # Create a Box App To use your own **Box App** you will need to create a new Box App in the **Box Developer Console**. Click the button below and we will set it up for you. At the end you will have a **Client ID** and **Client Secret**. Create an app We will use these credentials to authenticate the Box CLI in the next step. # Use existing Box App If you have already created a Box App before you can use that as well. We require a few settings to be set for this to work. 1. Go to the [Developer Console](https://cloud.app.box.com/developers/console) 2. Select your application 3. Go to the application’s **Configuration** section 4. Make sure your application uses **Standard OAuth 2.0** as the authentication method 5. Scroll down to the **OAuth 2.0 redirect URI** configuration and set the **Redirect URI** to the value `http://localhost:3000/callback`. 6. Scroll down to the **Application Scopes** section to select your desired [permissions](g://api-calls/permissions-and-errors/scopes). **Your application must have at least one or more** **of the following scopes:** read all files and folders stored in Box, read and write all files and folders in Box. 7. At the top of the page click the button to **Save Changes** Next, copy the values for the Client ID and Client Secret into these 2 fields. Client ID Client Secret We will use these credentials to authenticate the Box CLI in the next step. # Security notice Your API credentials are now stored in the browser cache. We highly recommend clearing out this storage by clicking the **Reset** button later in this guide. ## Summary You either selected to create a new **Box App** - Signed up for a developer account (Optional) - Had us create a **Platform App** for you that uses **OAuth 2.0** authentication - Had us set up the **redirect URL** for the application Or you selected to use an **existing Box App** I have configured a Box app **Reference:** https://developer.box.com/guides/cli/quick-start/create-oauth-app/ --- ## Untitled *Type: quick-start | Category: CLI * CLI Installation and Configuration Installers are available for Windows and macOS. However, the raw source-code is available if you would… # CLI Installation and Configuration Installers are available for Windows and macOS. However, the raw source-code is available if you would like to build the CLI in other environments. ## Windows & macOS Installers To install the latest CLI on your machine, download the latest `.exe` for Windows or `.pkg` for macOS for the latest release. Download the latest CLI installer ## Linux & Node install Additionally, the CLI can be installed as a Node package on any platform, including Linux. For this to work you will need to have [Node JS](https://nodejs.org/) installed on your machine. ``` npm install --global @box/cli ``` ## Source Code The source code for the CLI is available via [GitHub](https://github.com/box/boxcli). ## Run configuration command You will now need to configure the CLI by logging in to your Box App. In this step, we will use the **Client ID** and **Client Secret** from the previous step to log you in and create an **Access Token** for your user. ## The reason to log in Currently you have provided us with the following information. Client ID Client Secret Open your terminal or command line and execute the command: `box login -n example_name`. Copy the Client ID and Client Secret into the terminal window when prompted. Click the **Grant access to Box** button that appears in the browser window. If successful, you will see the following success message. ## Confirm configuration To confirm successful configuration, make your first Box API call with the Box CLI by entering the command `box users:get me`. A successful response will provide details about your user account. ``` Type: user ID: ''0123456789'' Name: Aaron Levie Login: example@box.com Created At: '2020-01-01T09:45:01-07:00' Modified At: '2021-03-01T09:30:05-07:00' Language: en Timezone: America/Los_Angeles Space Amount: 999999999999999 Space Used: 6291500 Max Upload Size: 16106127360 Status: active Job Title: '' Phone: '' Address: example+user@box.com Avatar URL: '' Notification Email: [] ``` ## Summary - You installed the CLI - You configured the CLI to use the OAuth 2.0 Application created earlier - You **made your first Box CLI Box API call** confirmed the user associated with your Access Token I installed and configured the CLI # Incomplete previous step Please complete the previous step to set up the **Box App** you want to use. **Reference:** https://developer.box.com/guides/cli/quick-start/install-and-configure/ --- ## Untitled *Type: quick-start | Category: CLI * Next steps You've reached the end of this Quick Start guide. By now you should have taken the following steps. Created and configured an… # Next steps You've reached the end of this Quick Start guide. By now you should have taken the following steps. 1. [Created and configured](g://cli/quick-start/create-oauth-app) an OAuth 2.0 application. 2. [Installed and configured](g://cli/quick-start/install-and-configure) the CLI 3. [Built and executed CLI commands](g://cli/quick-start/build-commands-help) 4. Learned about [options and bulk commands](g://cli/quick-start/options-and-bulk-commands) 5. Explored using the CLI to [run a Powershell script](g://cli/quick-start/powershell-script-templates) To take the next step, the following resources are recommended: 1. Review all [commands](https://github.com/box/boxcli#command-topics). 2. Review [token cache](https://github.com/box/boxcli/blob/master/docs/configure.md#box-configureenvironmentsupdate-name) settings. 3. Review [autocomplete](https://github.com/box/boxcli/blob/master/docs/autocomplete.md) settings. 4. Review all of the [CLI sample scripts](g://cli/scripts). **Reference:** https://developer.box.com/guides/cli/quick-start/next-steps/ --- ## Untitled *Type: quick-start | Category: CLI * Using Options and Bulk Commands Options Options provide additional, optional functionality to use with a CLI command. You may also hear… # Using Options and Bulk Commands ## Options Options provide additional, optional functionality to use with a CLI command. You may also hear these referred to as flags or arguments. As mentioned in the previous step, `--help` is an example of an option. To see all valid options for a command, visit the [GitHub repository](https://github.com/box/boxcli#command-topics). For example, look at the command documentation for [deleting folders](https://github.com/box/boxcli/blob/master/docs/folders.md#box-foldersdelete-id). You will see a list of options to use with this command, such as `--recursive` or `--force`. ## As-User Header To use the [as-user header](g://authentication/jwt/as-user), add the `--as-user=USERID` option to the end of the command. For example, the following command will create a folder called `Example_Folder` at the root level in user ID 123456’s account. ``` box folders:create 0 Example_Folder --as-user=123456 ``` Only Service Accounts and Admins are able to use the as-user header. If your application was not authorized with the necessary scopes or you configured your CLI to obtain a default token for another user, this call may fail. Add `-v` or `--verbose` to your command for verbose error logging. ## Bulk Commands You can use a CSV file to execute commands in bulk. Each row of the spreadsheet is treated as an individual API call. To execute a bulk command, use the option `--bulk-file-path=<PATH_TO_CSV>`, where `<PATH_TO_CSV>` is the local path of a CSV file containing the necessary information. As an example, let's create folders using the command `box folders:create --bulk-file-path=pathtoacsv` Drag the CSV file from your finder window/file explorer to the terminal/command line window to auto-populate the path. To specify the column names for your CSV file, go to the [GitHub repository](https://github.com/box/boxcli#command-topics) documentation and look at the argument names or use the `--help` option. In this case, these are `PARENTID` and `NAME` and are case insensitive. You can also use a CSV [template](https://github.com/box/boxcli/blob/main/docs/Bulk%20actions/folders/folders-create.csv) for this example bulk create folders command. Executing the command below creates three folders at the root level, 0, of the Service Account's folder tree. ``` box folders:create --bulk-file-path=/Users/ExampleUser/Desktop/bulkcreatefolders.csv ``` ## Bulk Commands with Options Passing an option in a command will automatically apply to it to each row in the CSV file. For example, `box folders:collaborations:create --bulk-file-path=pathtocsv --role=editor` will create collaborations for each user in the csv as an editor. However, you can also use options in the CSV file. Building on the last example, instead of using the `--role=editor` option in the command itself, it can be a column called `role`. The command itself becomes: `box folders:collaborations:create --bulk-file-path=pathtocsv` For more details on bulk commands read [this document](g://cli/cli-docs/bulk-commands). ## Summary - You used an option with a command and/or a bulk command. I know how to use options and bulk commands **Reference:** https://developer.box.com/guides/cli/quick-start/options-and-bulk-commands/ --- ## Untitled *Type: quick-start | Category: CLI * Using PowerShell Scripts with the Box CLI By itself, the CLI is already powerful, but using it alongside a PowerShell script lets you… # Using PowerShell Scripts with the Box CLI By itself, the CLI is already powerful, but using it alongside a PowerShell script lets you complete repetitive tasks even quicker. We created an example [scripts](https://github.com/box/boxcli/tree/main/examples) folder within our CLI GitHub repository to help jump start your development. To show you how CLI scripts work, we are going to use the provision and create users [script](https://github.com/box/boxcli/tree/main/examples/User%20Creation%20&%20Provisioning) template. This script uses the Box CLI to build and create a personal folder structure, create managed users in bulk, and provision such new users by adding them to the newly created folder structure as collaborators with viewer or uploader roles. This part of the quick start is for service accounts and users with administrative privileges only. Skip this step if you are not a Box administrator or you do not use a service account. ## Use case To automatically create users and folder structure, the script performs the following steps: 1. Uses a `.csv` file to load employee data in bulk. 2. Defines folder structure using a JSON file or uploads the structure from the user's local directory. 3. Creates each new managed user a predetermined personal folder structure ## Prerequisites ### Windows Install the latest version of [dotnet core](https://dotnet.microsoft.com/download). ### MacOS & Linux Install [PowerShell](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell?view=powershell-7.2) If you encounter issues make sure you installed both [dotnet core](https://dotnet.microsoft.com/download) and [PowerShell](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell?view=powershell-7.2) ### Box CLI To use the script, you will need the Box CLI installed and configured. If you haven't done so yet, see [step 1](g://cli/quick-start/create-oauth-app) of this quick start guide. Alternatively, go to your [developer console](https://app.box.com/developers/console), and follow the guide [Setup with OAuth 2.0](g://authentication/oauth2/oauth2-setup). ### Create a personal folder parent folder This script works by creating a folder structure for each user that is created. In order to do this, you should go ahead and create a parent folder for all of the personal folders to live in. Otherwise, every folder will live in the root of the user that the CLI is setup with. You may name and place the folder wherever you wish, but the user you setup the CLI with must have access to the folder. Here is an example of what the structure will look like after running the script. ## Download the script Clone the script to a directory of your choice and navigate to that directory. ``` git clone https://github.com/box/boxcli.git box-cli cd box-cli/examples/User\ Creation\ \&\ Provisioning/ ``` ## Configure the script settings Adapt the script to run in your own environment. In this example, you will use the sample data provided with the script. ### Provide the parameters There are a few parameters you need to supply before running the script: - `EmployeeList`: Path to `Employee List` CSV. - `PersonalFolderParentID`: Destination folder ID for all personal folders to be created in, either when using JSON file as input to create folder structure, or uploading local structure. This folder should be made prior to running the script the first time. It is not advised to make this value `0`, as this will create individual Personal folders in root of the account you set up the CLI with. - `FolderStructureJSONPath`: Your own folder structure JSON path. - `PersonalFolderSlug`: Ending name of the folder that will be created as parent for personal folders. It's set to `Personal Folder` by default, but feel free to set it to your needs. The username is concatenated with this value to create each user's personal folder name. ex - `rsmith2's Personal Folder`. - `LocalUploadPath`: Local directory to upload folder structure directly. Specify either a local upload path or a JSON file with the folder structure, not both. ### Update the user list You can use the following sample files to load users: `Employees_1.csv`, `Employees_5.csv`, and `Employees_10.csv`. Each will load 1, 5 or 10 new users. Customize these files for a test run. For example, update the `Employees_1.csv` with the following data: ``` firstName,lastName,email,username Isaac,Newton,abc@abc.local,INewton23 ``` With the `EmployeeList` parameter, specify which `.csv` file you would like to load data from. Emails must be unique across all of Box, and usernames must be unique across your specific Box instance. Otherwise, an error will be thrown when running the script. ### Create folder structure You can either create a folder structure from a JSON file or upload it from your local drive. #### Use a JSON file The `Folder_Structure.json` file contains the folder structure you want to create. As an example, you will create a `Market Research` and a `Sales Plays` folder, each with a subfolder `Statistics` and `Big Pharma` respectively. The script will place this folder structure under the `Personal Folder` folder for that user inside the parent folder you designate. With the `FolderStructureJSONPath` parameter, provide the location of the `Folder_Structure.json` file. #### Upload file from local drive You can also upload a folder structure directly from the local file system. With the `LocalUploadPath` parameter, provide the path to your local folder you want to upload. The folder is still named and uploaded identically to the JSON file method. ### Update the parameters You have 3 ways to pass parameters before running the script: Use static values in the script Remember to update all required parameters in the script before running. ``` # Set Employee List CSV Path # firstname, lastname, email, username $EmployeeList = "" # Personal Folder Structure: Set either path build off JSON or directly upload # a local folder $FolderStructureJSONPath = "" $LocalUploadPath = "" # Ending slug of folder that will be used in creating personal folders for new # users. Value will get concatenated with username # If username is RSMITH, the personal folder name would be # RSMITH's Personal Folder $PersonalFolderSlug = "" # ID of parent folder for created personal folders to be created in # This folder should be created before running the script the first time. # It is not advised to make this value 0, as this will create individual # Personal folders in root of the account you set up the cli with $PersonalFolderParentID = "" ``` Run the script with parameters You can specify parameters while running the script, for example: ``` PS > ./Users_Create_Provision.ps1 -EmployeeList ./Employees_1.csv ` -LocalUploadPath ./PersonalLocalUpload ` -PersonalFolderSlug "Personal Folder" ` -PersonalFolderParentID 123456789 Starting User Creation & Provisioning script... ``` Provide the parameters when prompted If some parameters are still missing at runtime, the script will prompt you to provide them: ``` PS > ./Users_Create_Provision.ps1 Please enter the path to the employee list CSV file: ./Employees_1.csv Please enter the path to the folder structure JSON file or the local upload path: Folder_Structure.json Folder structure JSON path set to: Folder_Structure.json Please enter the ID of the folder where you would like to create the personal folders: 0 Starting User Creation & Provisioning script... ``` ## Run the script Change the directory to the folder containing the script. In this example, it is the `User Creation & Provisioning` folder. ``` pwsh ``` Run the script: ``` PS /home/rvb/box-cli/examples/User Creation & Provisioning> ./Users_Create_Provision.ps1 ``` The response will be similar to the following: ``` Starting User Creation & Provisioning script... firstName lastName email --------- -------- ----- Isaac Newton abc@abc.local Extracting folder structure Found current User ID: 18622116055 Created a user owned Onboarding folder with id: 164734146745 Created subfolder Market Research under Onboarding folder with id: 164735375585 Created subfolder under Statistics folder with id: 164734956242 Created subfolder Sales Plays under Onboarding folder with id: 164735683001 Created subfolder under Big Pharma folder with id: 164736160637 Creating employee Managed User account with first name: Isaac, last name: Newton, email: abc@abc.local, and Created Managed user with id: 19605663027 Type: collaboration ID: '37250833128' Created By: Type: user ID: '18622116055' Name: Rui Barbosa Login: barduinor@gmail.com Created At: '2022-06-07T13:58:05-07:00' Modified At: '2022-06-07T13:58:05-07:00' Expires At: null Status: accepted Accessible By: Type: user ID: '19605663027' Name: Isaac Newton Login: abc@abc.local Invite Email: null Role: viewer uploader Acknowledged At: '2022-06-07T13:58:05-07:00' Item: Type: folder ID: '164734146745' Sequence ID: '0' ETag: '0' Name: Onboarding Collaborated Managed User Isaac Newton to current users Onboarding folder for provisioning ``` ## Running the script again for new users It is common to run this script regularly as your company hires new employees. You can simply edit the `.csv` file, removing the previous rows of users and adding the information for the new users. Then, the script may be ran again. ## Summary You explored automation using a PowerShell script with the Box CLI to provision users and create an initial folder structure. Make sure to explore our other [sample scripts](g://cli/scripts) for more use cases. I know how to use the sample scripts to automate repetitive tasks **Reference:** https://developer.box.com/guides/cli/quick-start/powershell-script-templates/ --- ## Untitled *Type: guide | Category: CLI * Deprovision users and archive folders This script allows you to deprovision and delete a list of users. It performs the following steps… # Deprovision users and archive folders This script allows you to deprovision and delete a list of users. It performs the following steps: 1. Transfers the user content to the another user's root folder under specified in `EmployeeArchiveFolderName` parameter. 2. Deletes the user. ## Prerequisites ### Windows Install the latest version of [dotnet core](https://dotnet.microsoft.com/download). ### MacOS & Linux Install [PowerShell](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell?view=powershell-7.2). Run the `pwsh` command to test the installation. ``` pwsh ``` Depending on the directory you are running the command in, the output may differ. For example: ``` PowerShell 7.2.5 Copyright (c) Microsoft Corporation. https://aka.ms/powershell Type 'help' to get help. PS /Users/user/repos/boxcli/examples> ``` If you encounter issues make sure you installed both [dotnet core](https://dotnet.microsoft.com/download) and [PowerShell](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell?view=powershell-7.2). ### Box CLI To use the script, you will need the Box CLI installed and configured. You can get this done by going through our [quick start guide](g://cli/quick-start/create-oauth-app). The user you use to login with should be the main Box admin or co-admin. ## Configure the script Clone the `boxcli` GitHub repository and cd into this example's folder or download the files from [`examples`](https://github.com/box/boxcli/tree/main/examples/User%20Deprovisioning) directory. ``` git clone https://github.com/box/boxcli.git boxcli cd boxcli/examples/User\ Deprovisioning/ ``` Create the list of employees for deletion in `.csv`. The header row should look like as follows: ``` name, email ``` where: - `name` is the name of the user in Box. - `email` is the primary email address of the user in Box. For example: | name | email | | --- | --- | | Managed User 1 | ManagedUser1@test.com | | Managed User 2 | ManagedUser2@test.com | | Managed User 3 | ManagedUser3@test.com | ### List of parameters | Parameter | Description | Required | Default Value | | --- | --- | --- | --- | | EmployeeList | Path to Employee List CSV with employees to be deleted. | Yes | - | | SkipTransferContent | Set this flag to skip transfer of user content before deletion when running the script. Otherwise user's content will be transferred. | No | False | | NewFilesOwnerID | The ID of the user to transfer files to before deleting the user. If not specified, the script will prompt to input in the interactive mode, or use the current authenticated user ID to receive the content. | No | If not specified, the script will prompt to input in the interactive mode, or use the current authenticated user ID. | | EmployeeArchiveFolderName | The name of a folder, where users' content will be moved to if SkipTransferContent is set to False. If a folder with this name already exists in the user's NewFilesOwnerID root folder, it will be used. Otherwise, a new one will be created. | Yes | Employee Archive | | DryRun | A flag that determines the script should be run in a mode, where no delete/create/update calls will be made, only read ones. | No | False | ### Define script parameters You can the following options to pass parameters. Use hardcoded value in script. To use this option, update all required parameters listed in the [script parameters section](https://github.com/box/boxcli/tree/main/examples/User%20Deprovisioning/Users_Deprovision.ps1#L17-L36) before running. Run script with parameters. You can specify parameters while providing the command. For example: ``` PS > ./Users_Deprovision.ps1 -EmployeeList ./Employees_to_delete.csv ` -NewFilesOwnerID 123456789 -EmployeeArchiveFolderName "Employee Archive" ``` or ``` PS > ./Users_Deprovision.ps1 -EmployeeList ./Employees_to_delete.csv ` -SkipTransferContent ``` If you don't specify parameters, the script will prompt you to enter it. ``` PS > ./Users_Deprovision.ps1 Please enter the path to the employee list CSV file: ./Employees_to_delete.csv Please specify the user ID of the user who will own the files of the users being deprovisioned. Press Enter if you want to use the current user as the new owner. User ID: 1234567689 Starting User Deprovisioning script... ``` ## Run the script Now all you need to do is run the script. Run the Powershell command. ``` pwsh ``` Run the script: ``` ./Users_Deprovision.ps1 ``` When all parameters are defined, you will see following output to confirm the script started: ``` PS /home/rvb/box-cli/examples/User Deprovisioning> ./Users_Deprovision.ps1 Starting User Deprovisioning script... ``` ## Logging Logs are stored in a `logs` folder located in the main folder. You have access to these log files: - `Users_Deprovision_all.txt` that contains all log entries - `Users_Deprovision_errors.txt` that contains only errors. **Reference:** https://developer.box.com/guides/cli/scripts/deprovision-users/ --- ## Untitled *Type: guide | Category: CLI * CLI sample scripts Box CLI scripts are designed to help you automate your tasks. Currently, the sample scripts library provides several… # CLI sample scripts Box CLI scripts are designed to help you automate your tasks. Currently, the sample scripts library provides several PowerShell scripts you can use and customize. In order to run them, you will need to have the Box CLI installed and configured. You can follow the [quick start guide](g://cli/quick-start/create-oauth-app) to get that done. Most of the below scripts require the user you run commands with to have Box [administrative privileges](https://support.box.com/hc/en-us/articles/360043694174-Understanding-Administrator-and-Co-Administrator-Permissions). ## PowerShell scripts - [Provision users and folders](g://cli/quick-start/powershell-script-templates) - [Deprovision users and archive folders](g://cli/scripts/deprovision-users) - [Manage groups and collaborations](g://cli/scripts/manage-groups-collaborations) - [Report inactive users](g://cli/scripts/report-inactive-users) - [Extract metadata](g://cli/scripts/metadata-extraction) - [Update user zones](g://cli/scripts/user-zones-mass-update) - [Manage Slack integration folder mappings](g://cli/scripts/slack-integration-mappings) **Reference:** https://developer.box.com/guides/cli/scripts/ --- ## Untitled *Type: guide | Category: CLI * Manage groups and collaborations Script structure This script uses Box CLI to create or update groups, add users to them, and finally to… # Manage groups and collaborations ## Script structure This script uses Box CLI to create or update groups, add users to them, and finally to create collaborations between groups and folders. The script consists of two parts described in detail in the sections below. You can run them both or use the optional flags to decide which part to run. ### Create or update groups The script uses the `.csv` file you specify for the `UserGroupAdditionPath` parameter. The file lists group names and user emails. When creating the file, you can use the same group name for several users, and assign one user to several groups. For example: | GroupName | UserEmail | | --- | --- | | Group1 | ManagedUser1@test.com | | Group1 | ManagedUser2@test.com | | Group2 | ManagedUser3@test.com | | Group3 | ManagedUser1@test.com | If the group doesn't exist, the script creates it. If it does exist, the script can update the entries based on the provided data. ### Create or update collaborations The script uses the `.csv` file you specify for the `CollaborationsCreationPath` parameter. The file lists group names, folder IDs, and collaboration roles. For each row, the script checks if a group exists and if it's not already added as a collaborator to the corresponding folder. For example: | GroupName | FolderId | CollaborationRole | | --- | --- | --- | | Group1 | 1111111 | editor | | Group2 | 1111111 | viewer_uploader | | Group2 | 2222222 | viewer | | Group3 | 1111111 | viewer_uploader | If both of these conditions are met, the script assigns the group to a folder using the role defined in the `CollaborationRole` column. Also, if a group already exists, but the `CollaborationRole` changed, the script will update it if you pass the `-UpdateExistingCollabs` flag when running the script. ## Prerequisites ### Windows Install the latest version of [dotnet core](https://dotnet.microsoft.com/download). ### MacOS & Linux Install [PowerShell](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell?view=powershell-7.2). Run the `pwsh` command to test the installation. ``` pwsh ``` Depending on the directory you are running the command in, the output may differ. For example: ``` PowerShell 7.2.5 Copyright (c) Microsoft Corporation. https://aka.ms/powershell Type 'help' to get help. PS /Users/user/repos/boxcli/examples> ``` If you encounter issues make sure you installed both [dotnet core](https://dotnet.microsoft.com/download) and [PowerShell](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell?view=powershell-7.2). ### Box CLI To use the script, you will need the Box CLI installed and configured. You can get this done by going through our [quick start guide](g://cli/quick-start/create-oauth-app). The user you use to login with should be the main Box admin or co-admin. ## Configure the script Clone the `boxcli` GitHub repository and cd into this example's folder or download the files from [`examples`](https://github.com/box/boxcli/tree/main/examples) directory. ``` git clone https://github.com/box/boxcli.git boxcli cd boxcli/examples/Mass\ Groups\ \&\ Collaborations\ Update/ ``` Set the path to the `.csv` file with the list of groups and user emails. ``` $UserGroupAdditionPath = "./User_Group_Addition.csv" ``` - `UserEmail` is the primary email address for the user in Box. - `GroupName` is the name of the group. Set your own path to the `.csv` file with the list groups and user emails. ``` $CollaborationsCreationPath = "./Collaborations_Creation.csv" ``` - `GroupName` is name of the group the script will add as a collaborator to the folder. - `FolderId` is the folder ID the collaborator will be added to. - `CollaborationRole` is the name of the role used when creating a collaboration. You can configure the available roles by setting the `AvailableCollaborationRoles` parameter: ``` $AvailableCollaborationRoles = @("editor", "viewer", "previewer", "uploader", "previewer_uploader", "viewer_uploader", "co-owner") ``` ## Run the script Run the Powershell command. ``` pwsh ``` Run the script. ``` ./Mass_Groups_Collabs_Update.ps1 ``` ### Optional flags You can use flags to run run or skip specific parts of the script. If a group already is set as a collaborator for a specific folder but with a role other than defined in the .`csv` file, the script will inform you about it. It will not make any changes to an existing collaboration. If you want to update an existing collaboration with role defined in `.csv` file, set an additional `-UpdateExistingCollabs` flag when running the script. ``` Mass_Groups_Collabs_Update.ps1 -UpdateExistingCollabs ``` To update groups without creating collaborations, add the `-SkipCollabsCreation` boolean flag when running the script: ``` Mass_Groups_Collabs_Update.ps1 -SkipCollabsCreation ``` To create collaborations without any group updates, add the `-SkipGroupsUpdate` boolean flag when running the script: ``` Mass_Groups_Collabs_Update.ps1 -SkipGroupsUpdate ``` ## Logging Logs are stored in the `logs` folder located in the main folder. You have access to these log files: - `Mass_Groups_Collabs_Update_all.txt` that contains all log entries - `Mass_Groups_Collabs_Update_errors.txt` that contains only errors. **Reference:** https://developer.box.com/guides/cli/scripts/manage-groups-collaborations/ --- ## Untitled *Type: guide | Category: CLI * Provision users and folders This script uses the Box CLI to build and create a personal folder structure, create managed users in bulk, and… # Provision users and folders This script uses the Box CLI to build and create a personal folder structure, create managed users in bulk, and provision such new users by adding them to the newly created folder structure as collaborators with viewer or uploader role. For details, see the quick start guide on [using Box CLI and sample scripts](g://cli/quick-start/powershell-script-templates). **Reference:** https://developer.box.com/guides/cli/scripts/provision-users-folders/ --- ## Untitled *Type: guide | Category: CLI * Extract metadata This script extracts metadata details for all the files and folders in any Box folder and saves the result in a CSV… # Extract metadata This script extracts metadata details for all the files and folders in any Box folder and saves the result in a CSV spreadsheet for each metadata template. ## Prerequisites ### Windows Install the latest version of [dotnet core](https://dotnet.microsoft.com/download). ### MacOS & Linux Install [PowerShell](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell?view=powershell-7.2). Run the `pwsh` command to test the installation. ``` pwsh ``` Depending on the directory you are running the command in, the output may differ. For example: ``` PowerShell 7.2.5 Copyright (c) Microsoft Corporation. https://aka.ms/powershell Type 'help' to get help. PS /Users/user/repos/boxcli/examples> ``` If you encounter issues make sure you installed both [dotnet core](https://dotnet.microsoft.com/download) and [PowerShell](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell?view=powershell-7.2). ### Box CLI To use the script, you will need the Box CLI installed and configured. You can get this done by going through our [quick start guide](g://cli/quick-start/create-oauth-app). ## Configure the script Clone the `boxcli` GitHub repository and cd into this example's folder or download the files from [`examples`](https://github.com/box/boxcli/tree/main/examples/Metadata%20Extraction) directory. ``` git clone https://github.com/box/boxcli.git cd boxcli/examples/Metadata\ Extraction/ ``` Specify the `folderID` and `userID` parameters to tell the script which folder to scan, and who is the user running the script. ``` [string]$FolderID = "", [string]$UserID = "", ``` If you don't want to specify the parameters directly in the script, you can either pass them as flags or allow the script to prompt you to enter them. A sample command with flags looks as follows: ``` ./Metadata-extraction.ps1 -folderId 123456789 -userId 123456789 ``` ## Run the script Run the Powershell command. ``` pwsh ``` Run the script. ``` ./Metadata-extraction.ps1 -folderId 123456789 -userId 123456789 ``` When the script finishes, you will see the following output or a similar one. ``` Pulling data from Folder ID: 173961139760 metadata as user ID: 20718545815 Reading Item ID: 1016853559790 Metadata saved to: MetadataTemplate_properties.csv ``` ## Logging Logs are stored in a `logs` folder located in the main folder. You have access to these log files: - `Metadata-extraction_all.txt` that contains all log entries. - `Metadata-extraction_errors.txt` that contains only errors. **Reference:** https://developer.box.com/guides/cli/scripts/metadata-extraction/ --- ## Untitled *Type: guide | Category: CLI * Report inactive users This script generates a CSV file with a list of users who have been inactive for a number of days. It performs the… # Report inactive users This script generates a CSV file with a list of users who have been inactive for a number of days. It performs the following steps: Looks for the users who have the role `user`. The script does not consider other roles, such as `AppUser`. Uses [Box Events](resource://event) to check if the user performed any actions for a specified number of days. The default list of event types includes: `LOGIN`,`UPLOAD`,`COPY`,`MOVE`,`PREVIEW`,`DOWNLOAD`,`EDIT`,`DELETE`,`UNDELETE`,`LOCK`,`UNLOCK`, `NEW_USER`. You can modify this list in the script settings. Adds users who didn't perform any actions to a `.csv` file with inactive users. You can use this file as input for other scripts, for example to [deprovision users](g://cli/scripts/deprovision-users). ## Prerequisites ### Windows Install the latest version of [dotnet core](https://dotnet.microsoft.com/download). ### MacOS & Linux Install [PowerShell](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell?view=powershell-7.2). Run the `pwsh` command to test the installation. ``` pwsh ``` Depending on the directory you are running the command in, the output may differ. For example: ``` PowerShell 7.2.5 Copyright (c) Microsoft Corporation. https://aka.ms/powershell Type 'help' to get help. PS /Users/user/repos/boxcli/examples> ``` If you encounter issues make sure you installed both [dotnet core](https://dotnet.microsoft.com/download) and [PowerShell](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell?view=powershell-7.2). ### Box CLI To use the script, you will need the Box CLI installed and configured. You can get this done by going through our [quick start guide](g://cli/quick-start/create-oauth-app). The user you use to login with should be the main Box admin or co-admin. ## Configure the script Clone the `boxcli` GitHub repository and cd into this example's folder or download the files from [`examples`](https://github.com/box/boxcli/tree/main/examples/Inactive%20Users%20Report) directory. ``` git clone https://github.com/box/boxcli.git cd boxcli/examples/Inactive\ Users\ Report/ ``` Set the number of days you want the script to scan for user events. If you don't specify this value or leave the default, the script will prompt you to enter it. ``` $daysInactive = "10" ``` (Optional) To change the report output file name, define the `ReportOutputFile` parameter. ``` $ReportOutputFile = $ReportName + ".csv" ``` (Optional) To change event types, define the list for `eventType` parameter. ``` $eventType = "LOGIN,UPLOAD,COPY,MOVE" ``` ## Run the script Run the Powershell command. ``` pwsh ``` Run the script. ``` ./Inactive_Users_Report.ps1 ``` When the script run is completed, you will see the following output or a similar one. ``` Looking for users inactive for more than 3 days. Found 6 users. Found 7 events in last 3 days Enterprise has: 0 App user, 6 regular users. With 1 admin role, 5 user roles. Need to check 5 users (regular user, with user role) for inactive. Found 5 users inactive for more than 3 days. Report is available at InactiveUsers.csv ``` ## Logging Logs are stored in the `logs` folder located in the main folder. You have access to these log files: - `Inactive_Users_Report_all.txt` that contains all log entries - `Inactive_Users_Report_errors.txt` that contains only errors. **Reference:** https://developer.box.com/guides/cli/scripts/report-inactive-users/ --- ## Untitled *Type: guide | Category: CLI * Manage Slack integration folder mappings This script helps manage the folder mappings between Slack and Box if using Box as the content… # Manage Slack integration folder mappings This script helps manage the folder mappings between Slack and Box if using Box as the content store for Slack. It creates a list of current Slack channel and Box folder mappings and can create or update mappings based on input csv. This script will maintain all permissions. For more details, you can checkout the [Github repo](https://github.com/box/boxcli/tree/main/examples/Integration%20Mappings). ## Prerequisites ### Clone script Clone this GitHub repo or download files from the `/examples` directory ``` git clone https://github.com/box/boxcli.git ``` ### Windows Install the latest version of [dotnet core](https://dotnet.microsoft.com/download). ### MacOS & Linux Install [PowerShell](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell?view=powershell-7.2). Run the `pwsh` command to test the installation. ``` pwsh ``` Depending on the directory you are running the command in, the output may differ. For example: ``` PowerShell 7.2.5 Copyright (c) Microsoft Corporation. https://aka.ms/powershell Type 'help' to get help. PS /Users/user/repos/boxcli/examples> ``` If you encounter issues make sure you installed both [dotnet core](https://dotnet.microsoft.com/download) and [PowerShell](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell?view=powershell-7.2). ### Box CLI Install Configure and install the Box CLI using the OAuth [CLI Setup Quick Start](g://cli/quick-start). Make sure the user you use is an admin or co-admin. ### Enterprise configuration - Configure and [install Box for Slack](https://support.box.com/hc/en-us/articles/360044195313-Installing-and-Using-the-Box-for-Slack-Integration) in the relevant Slack work spaces and organizations - Box as the [content layer for Slack](https://support.box.com/hc/en-us/articles/4415585987859-Box-as-the-Content-Layer-for-Slack) in enabled ## Run the script Change the directory to the folder containing the script. In this example, it is the `Integration Mappings` folder. ``` rvb@lab:~/box-cli/examples/Integration Mappings$ pwsh PowerShell 7.2.4 Copyright (c) Microsoft Corporation. https://aka.ms/powershell Type 'help' to get help. PS /home/rvb/box-cli/examples/Integration Mappings> ``` Run the script with EXTRACT to extract current mappings: ``` ./integration-mappings.ps1 -Action EXTRACT ``` or Run the script with UPDATE to update current mappings: ``` ./integration-mappings.ps1 -Action UPDATE ``` or Run the script with CREATE to create new mappings: ``` ./integration-mappings.ps1 -Action CREATE -MappingPath ./mapping_create_example.csv ``` By default, the csv file will save to and load from ./mappings.csv. If you wish to change this location, you can pass in a new path like so: ``` ./integration-mappings.ps1 -Action EXTRACT -MappingPath ./mappings_new_location.csv ``` If you don't specify parameters, the script will prompt you to enter them. When the script run is completed, you will see the following output or a similar one. When creating a mapping on a new channel, you must input a Box folder id, Slack channel id and Slack org id. You may use a Slack workspace ID in lieu of the org id. In that case, you would replace the csv column header `SlackOrgId` with `SlackWorkspaceId`. ``` Starting Process Applying new mappings Output [...] All bulk input entries processed successfully. ``` ## Logging Logs are stored in a `logs` folder located in the main folder. You have access to these log files: - `Integration-mappings_all.txt` that contains all log entries. - `Integration-mappings_errors.txt` that contains only errors. ## Disclaimer This project is a collection of open source examples and should not be treated as an officially supported product. Use at your own risk and as a source of example how to use Box CLI. **Reference:** https://developer.box.com/guides/cli/scripts/slack-integration-mappings/ --- ## Untitled *Type: guide | Category: CLI * Update user zones This script provisions users to a specific data residency zone within a Multizone Box tenant. It performs the following… # Update user zones This [script](https://github.com/box/boxcli/tree/main/examples/Mass%20Update%20User%20Zones) provisions users to a specific data residency zone within a Multizone Box tenant. It performs the following steps: 1. It uses admin or co-admin login email address to find the associated enterprise and the zone policy assigned to this enterprise. An assigned zone policy is inherited by all users unless specified otherwise. It is sometimes called the **default zone**. 2. It performs zone assignment based on an input `.csv` file containing user email addresses and zone mappings. Usually, you use the script once to do the initial provisioning of user zones, but you can also use it for subsequent runs to make zone assignment updates. If you would like to use Admin Console for zone assignment, see [this guide](https://support.box.com/hc/en-us/articles/360044193533-Assigning-Zones-through-the-Admin-Console). For more information about Box Zones, see the [official website](https://www.box.com/zones). ## Prerequisites ### Windows Install the latest version of [dotnet core](https://dotnet.microsoft.com/download). ### MacOS & Linux Install [PowerShell](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell?view=powershell-7.2). Run the `pwsh` command to test the installation. ``` pwsh ``` Depending on the directory you are running the command in, the output may differ. For example: ``` PowerShell 7.2.5 Copyright (c) Microsoft Corporation. https://aka.ms/powershell Type 'help' to get help. PS /Users/user/repos/boxcli/examples> ``` If you encounter issues make sure you installed both [dotnet core](https://dotnet.microsoft.com/download) and [PowerShell](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell?view=powershell-7.2). ### Set up application with JWT authentication To use the script, you will need the [Box CLI with JWT authentication](g://cli/cli-docs/jwt-cli) installed and configured. When creating the app, use the **Configuration** tab to configure the following settings: - In **App Access Level**, select `App + Enterprise Access`. - In **Application Scopes** > **Administrative Actions**, select `Manage Enterprise Properties`, `Manage Users`. - In **Advanced Features**, select `Generate user access tokens`. ### Adjust admin settings Make sure Box Admin or Co-Admin has at least `Manage Users` privileges. To check this setting: 1. Go **Users & Groups** section in the Admin Console. 2. Click the user account you want to verify. 3. Go to **Edit User Access permissions** section to grant the administrative privileges for users and groups. ## Prepare the .csv file The `.csv` file must have two columns with the following headers: **Email** and **Region**. **Email** contains the primary email address of a Box user. **Region** contains the user-friendly name for the zone to which the script will assign the user. This name is provided by the [ZonesTable](https://github.com/box/boxcli/blob/main/examples/Mass%20Update%20User%20Zones/Mass_Update_User_Zones.ps1#L23) that is a hash table used to define zones. The keys are the zone's user-friendly names, and the corresponding value is the global ID of the zone. ``` $ZonesTable = @{ US = "100001" #US GermanyIreland = "100002" #Germany/Ireland with in region uploads/downloads/previews Australia = "100003" #Australia Japan = "100004" #Japan with in region uploads/downloads/previews Canada = "100005" #Canada JapanSingapore = "100007" #Japan/Singapore with in region uploads/downloads/previews UKGermany = "100008" #UK/Germany UK = "100009" #UK with in region uploads/downloads/previews France = "100012" #France } ``` Consult the Box Consulting or Customer Success manager to get the IDs corresponding to the zones enabled in a specific enterprise. A sample input `.csv` file containing emails and zone names is provided with this script. Its content looks as follows: | Email | Region | | --- | --- | | betty@company.com | US | | roger@company.com | France | | sally@company.com | JapanSingapore | ## Configure the script Set the `UserZonesUpdatePath` to point to your `.csv` file. ``` $UserZonesUpdatePath = "./your_file_name.csv" ``` Update the `adminEmail` to the admin or `co-admin` login email address of the account the script will use to make zone assignments. If you don't specify this value, the script will prompt you for it. ``` $adminEmail = "john@box.com" ``` ## Run the script Run the Powershell command. ``` pwsh ``` Run the script. ``` ./Mass_Update_User_Zones.ps1 ``` ### Optional flags To run the script in a simulation mode, add the `DryRun` boolean flag. Dry run doesn't mean that API calls won't be made, but that any create/update/delete calls will be skipped. ``` ./Mass_Update_User_Zones.ps1 -DryRun ``` ## Logging Logs are stored in a `logs` folder located in the main folder. You have access to these log files: - `Mass_Update_User_Zones_all.txt` that contains all log entries. - `Mass_Update_User_Zones_errors.txt` that contains only errors. **Reference:** https://developer.box.com/guides/cli/scripts/user-zones-mass-update/ --- ## Untitled *Type: guide | Category: Collaborations * Remove a previously allowed domain for collaboration Deleting a domain from the list of allowed collaboration domains will remove the… # Remove a previously allowed domain for collaboration Deleting a domain from the list of allowed collaboration domains will remove the ability for collaborations to be created between your enterprise and users in that domain. To remove a domain from the allowed list, supply the ID of the list entry to the delete request. This ID is returned when [allowing a new domain](guide://collaborations/allowed-domains/create) or [listing the allowed domains in the enterprise](guide://collaborations/allowed-domains/list); **Reference:** https://developer.box.com/guides/collaborations/allowed-domains/delete/ --- ## Untitled *Type: guide | Category: Collaborations * Allow a domain for collaboration An enterprise that normally restricts creating collaborations can add domains, such as example.com, to a… # Allow a domain for collaboration An enterprise that normally restricts creating collaborations can add domains, such as `example.com`, to a list for which collaborations may be created within the enterprise. The [endpoint](endpoint://post_collaboration_whitelist_entries) will require the `domain` to allow the collaborations between, and a `direction`, which may be set to: - `inbound`: Whether external users may be collaborated in on content in your enterprise. - `outbound`: Whether your enterprise managed users may be collaborated in on content owned within an external enterprise. - `both`: Both of the above. Supply both parameter to set up the new allowed domain. **Reference:** https://developer.box.com/guides/collaborations/allowed-domains/create/ --- ## Untitled *Type: guide | Category: Collaborations * Allowed collaboration domains Within the content and sharing settings for an enterprise, an admin can specify collaboration restrictions for… # Allowed collaboration domains Within the content and sharing settings for an enterprise, an admin can specify [collaboration restrictions](https://support.box.com/hc/en-us/articles/4404822772755-Enterprise-Settings-Content-Sharing-Tab) for the enterprise. These settings include the ability to limit collaborations to only a series of allowed domains. The allowed domain APIs are available to allow applications with appropriate permissions to add, fetch, and delete these allowed domains programmatically for the enterprise. **Reference:** https://developer.box.com/guides/collaborations/allowed-domains/ --- ## Untitled *Type: guide | Category: Collaborations * List allowed domains for collaboration Listing the domains that are allowed for collaboration will return all domains that permit… # List allowed domains for collaboration Listing the domains that are allowed for collaboration will return all domains that permit collaborations to be created with the current enterprise. There are no required parameters for the request, but `limit` and `market` parameters may optionally be set to limit and page through the full result set. **Reference:** https://developer.box.com/guides/collaborations/allowed-domains/list/ --- ## Untitled *Type: quick-start | Category: Collaborations * Configure Slack The first step in this guide is to create and configure a Slack application. This Slack application will act as a bot that… # Configure Slack The first step in this guide is to create and configure a Slack application. This Slack application will act as a bot that listens for user events in Slack channels, and responds to **slash commands** by users in those channels - allowing them to share Box files and folders with the group. This section will take you through the following steps. - Create a minimal Slack application within the Slack API dashboard - Configure the Slack application to send notifications to our application whenever a user joins or leaves the channel - allowing our code to update the Box Box group. - Configure a `/boxadd` **slash command** that will allow users to share a Box file or folder with all the users in the channel. ## Create a minimal Slack app Go to the **[Slack apps page](https://api.slack.com/apps)** and click **Create an App**. Add an **App Name**, select your **Development Slack Workspace** from the dropdown list where the bot will be deployed to, then click **Create App**. Once created, you will be redirected to the basic information section of the application. You may adjust the icon and description of your app within the **Display Information** section at the bottom to customize the application in your workspace. ## Configure the Slack app's event listener Setting up an event listener for our Slack app will allow us to monitor for events within the channel. For this bot, we want to monitor three [Slack events](https://api.slack.com/events) in order to perform actions within Box. - [`bot_added`](https://api.slack.com/events/bot_added): When the bot is first added to a channel, it will get a list of all users in the channel, then create a Box group for those users. We can then use this group later on to add that group to any content that is shared with the **slash command**. - [`member_joined_channel`](https://api.slack.com/events/member_joined_channel): When a new user joins a Slack channel they will be added to the Box group. - [`member_left_channel`](https://api.slack.com/events/member_left_channel): When a user leaves a Slack channel, or the user is removed, they will be removed from the Box group. To set up a notification URL to which these Slack event payloads will be sent, Slack requires a verification step. When you set an event listener URL for your bot application code, Slack will immediately send a challenge to that URL to verify that it's valid. This will be an HTTP POST with a payload that looks something like the following: ``` { "token": "Jhj5dZrVaK7ZwHHjRyZWjbDl", "challenge": "3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P", "type": "url_verification" } ``` To set up the URL for the event listener, that URL that is set needs to respond with a verification payload containing the challenge value back to Slack during this step. The payload will look similar to the following. ``` HTTP 200 OK Content-type: application/json {"challenge":"3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P"} ``` To do this we will deploy a small bit of code to respond to the challenge event. Choose your preferred language / framework below to get started. # Node (Express Framework) # Java (Spring Boot Framework) Within the project directory, run `npm install express --save` to install the Express dependency, then deploy the following code to your public endpoint along with the appropriate Node modules. ``` const express = require('express'); const app = express(); const port = process.env.PORT || 3000; app.use(express.urlencoded({ extended: true })); app.use(express.json()); app.post('/event', (req, res) => { if ( req.body && req.body.challenge && req.body.type === 'url_verification' ) { res.send({ challenge: req.body.challenge }); } else { res.status(400).send({ error: "Unrecognized request" }); } }); app.listen(port, function(err) { console.log("Server listening on PORT", port); }); ``` [`Spring Initializr`](https://start.spring.io/) is a useful service for auto-generating a new Spring boot application with all dependencies defined. This may be used instead of creating a blank Java application. - From Eclipse, create a new project. When prompted, select a Gradle project. - Enter a unique name for the project, we used `slack.box` for this guide. - Open your `build.gradle` file and add the following. Ensure that the group matches the group that you used for the application. Once saved, refresh the Gradle project. ``` plugins { id 'org.springframework.boot' version '2.3.1.RELEASE' id 'io.spring.dependency-management' version '1.0.9.RELEASE' id 'java' } group = 'com.box' version = '0.0.1-SNAPSHOT' sourceCompatibility = '1.8' repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation('org.springframework.boot:spring-boot-starter-test') { exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' } compile 'com.box:box-java-sdk:2.44.1' } test { useJUnitPlatform() } ``` - Within your `src/main/java` path, create a new Java class file named `Application.java`. - Open the file, add the following code, and save. ``` package com.box.slack.box; import org.jose4j.json.internal.json_simple.JSONObject; import org.jose4j.json.internal.json_simple.parser.JSONParser; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController @EnableAutoConfiguration public class Application { @PostMapping("/event") public JSONObject challenge(@RequestBody String data) throws Exception { JSONObject returnJSON = new JSONObject(); Object dataObj = new JSONParser().parse(data); JSONObject inputJSON = (JSONObject) dataObj; String challenge = (String) inputJSON.get("challenge"); String type = (String) inputJSON.get("type"); if (type.equals("url_verification")) { returnJSON.put("challenge", challenge); } else { System.err.println("Invalid input"); } return returnJSON; } public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` # Incomplete previous step Please select a preferred language / framework above to get started. Now that we have the code to respond to the Slack challenge when adding an event URL, we can configure that within the Slack application. From your Slack application **Basic Information** tab, under **Add features and functionality**, click on the button titled **Event Subscriptions** and do the following. - Toggle **Enable Events** to **On**. - Under **Request URL** add in the public URL that you deployed the above code to, and be aware that we are listening at `{YOUR_APP_DOMAIN}/event` (such as `https://myapp.com/event`). Once you add the URL and click outside the field, Slack will immediately send the challenge to the URL that you were hosting the code at above. If the code responds correctly, you will see a green verified note beside the **Request URL** header. - Expand the **Subscribe to bot events** section and click on the **Add Bot User Event** button. - Add `member_joined_channel` and `member_left_channel` to the events the bot is subscribed to. These will send events when anyone new is added to the channel. - Click the **Save Changes** button at the bottom of the page. ## Configure the Slack app slash command To provide every user in a Slack channel access to a file or folder in Box, we can use a Slack **"slash commands"**. A slash command will allow any person in the channel to share content they own in Box with the rest of the channel. Through this command, a channel member will be able to type `/boxadd [file / folder] [id]`, for example `boxadd file 1459732312`, into the channel to share the file / folder with every user in the channel. To do this, the file is automatically collaborated with the Box group of users that are in that channel. From the **Basic Information** tab of your application, under Add features and functionality, click on the button titled **Slash Commands**. In the page that comes up, click **Create New Command** and input the following: - **Command**: This is the command that a channel user will use to share a Box file / folder ID with the channel. Use `/boxadd` for this quick start. - **Request URL**: The URL that is listening for and responding to slash commands in our Slack bot. In this quick start we use the same event URL that was used for the app event listener section above. - **Short Description**: A description of what the Slash command will do. - **Usage Hint**: Additional parameters that may be passed to the command. In our case, that's the Box file / folder ID and type of content. Click **Save** to add the command to our Slack app. ## Add Additional Scopes When slash commands or notifications are sent to our application from Slack they will contain a Slack user ID, which relates to the person that took or was affected by the action. To translate that ID to a Box user we need to get the Slack user's email, which we can then use to associate that Slack user to a corresponding Box user. This action requires two extra scopes in the Slack application configuration. From your Slack application configuration, click on **OAuth & Permissions** in the left menu, then do the following. - Scroll down to the **Scopes** section. - Click on the **Add an OAuth Scope** button under **Bot Token Scopes**.. - Search for and add `users:read` and `users:read.email`. ## Deploy Bot to Slack Workspace The last step is to install the application into your Slack workspace. From the **Basic Information** page of the app, expand the **Install your app to your workspace** section. Click the button to **Install App to Workspace**. Once the **Allow** button is clicked you should see a success message. Your bot is now installed within the workplace. ## Summary - You've created your Slack application. - You've configured user event notifications, slash commands, and additional scoping. - You've deployed your Slack bot to your workspace. I have my local application set up **Reference:** https://developer.box.com/guides/collaborations/connect-slack-to-group-collabs/configure-slack/ --- ## Untitled *Type: quick-start | Category: Collaborations * Connect Bot to Box We're now handling and processing events coming from Slack, then obtaining all information needed to connect with Box… # Connect Bot to Box We're now handling and processing events coming from Slack, then obtaining all information needed to connect with Box users and groups. We now need to connect that functionality to Box functions. In this step we'll expand several functions from the last step to incorporate new Box features. - Instantiate a Box client. - Add a Box user to a Box group. - Remove a Box user from a Box group. - Fetch a Box group ID from a group name. - Add content that is shared to a group. ## Instantiate a Box Client To make calls to the Box APIs, you'll first need to set up a Box client. Within `process.js`, replace the `// INSTANTIATE BOX CLIENT` comment at the top with the following. ``` const boxConfig = require("./boxConfig.json"); const sdk = box.getPreconfiguredInstance(boxConfig); const client = sdk.getAppAuthClient("enterprise"); ``` The `boxConfig` assignment line will use the `boxConfig.json` file you downloaded from your Box app at the end of [step 2](g://collaborations/connect-slack-to-group-collabs/configure-box). The sample above is assuming that you have it stored in the same folder as `process.js`. If that's not the case, change the path to point to where your `boxConfig.json` file is, and what it may be named. The last `client` assignment line is creating a Box client object which may be used to make API calls. At this point it is scoped to the [service account](page://platform/user-types/#service-account/) of the application, and not a specific user. Within `Application.java`, replace the `// INSTANTIATE BOX CLIENT` comment within the `processEvent` method with the following. ``` this.fileReader = new FileReader("boxConfig.json"); this.boxConfig = BoxConfig.readFrom(fileReader); this.boxAPI = BoxDeveloperEditionAPIConnection.getAppEnterpriseConnection(boxConfig); ``` The `boxConfig` assignment line will use the `boxConfig.json` file you downloaded from your Box app at the end of [step 2](g://collaborations/connect-slack-to-group-collabs/configure-box). The sample above is assuming that you have it stored at the root of the Java project. If that's not the case, change the path in the `fileReader` assignment to point to where your `boxConfig.json` file is, and what it may be named. The last `boxAPI` assignment line is creating a Box client object which may be used to make API calls. At this point it is scoped to the [service account](page://platform/user-types/#service-account/) of the application, and not a specific user. # Incomplete previous step Please select a preferred language / framework in step 1 to get started. ## Add a Box user to a group Let's add a function that adds a Box user to a group. When a bot is added to a channel and needs to create a Box group with all users of the channel, or when a single user joins the channel after that action, this function will perform that task. Replace the `addGroupUser` function with the following. ``` function addGroupUser(groupId, email) { client.enterprise.getUsers({ filter_term: email }).then((users) => { if (users.entries.length > 0) { const userId = users.entries[0].id; const groupRole = client.groups.userRoles.MEMBER; client.groups .addUser(groupId, userId, { role: groupRole }) .then((membership) => { if (membership.id) { console.log(`Member added with membership ID: ${membership.id}`); } else { console.log(`Member not added`); } }) .catch(function (err) { console.log(err.response.body); }); } else { console.log("No Box user found to add to group"); } }); } ``` Replace the `addGroupUser` method with the following. ``` public void addGroupUser(String groupId, String userEmail) { Iterable<BoxUser.Info> users = BoxUser.getAllEnterpriseUsers(this.boxAPI, userEmail); for (BoxUser.Info user : users) { if (user.getLogin().toUpperCase().equals(userEmail.toUpperCase())) { try { BoxGroup group = new BoxGroup(boxAPI, groupId); BoxUser boxUser = new BoxUser(this.boxAPI, user.getID()); BoxGroupMembership.Info groupMembershipInfo = group.addMembership(boxUser); } catch (Exception ex) { System.err.println("User already present"); } } } } ``` # Incomplete previous step Please select a preferred language / framework in step 1 to get started. Since we're matching a Slack user to a Box user via their email address, we first find a matching Box user using the Slack profile email. If found, a call is made to add that user to the channel group. The group was created when the bot was first added. The Box [Get User](endpoint://get-users-id) endpoint only permits user lookup by user ID. To lookup a user by email address, use the [List Enterprise Users](endpoint://get-users) endpoint and set the `filter_term` option to the email address you're searching for. ## Remove a Box user to a group When a user leaves or is removed from a Slack channel, we also want to remove them from the Box group so that they can no longer access the shared group content. Replace the `removeGroupUser` function with the following. ``` function removeGroupUser(groupId, email) { client.groups.getMemberships(groupId).then(memberships => { for (let i = 0; i < memberships.entries.length; i++) { if (memberships.entries[i].user.login === email) { client.groups .removeMembership(memberships.entries[i].id) .then(() => { console.log('Group user removed') }); break; } } }); } ``` Replace the `removeGroupUser` method with the following. ``` public void removeGroupUser(String groupId, String userEmail) { BoxGroup boxGroup = new BoxGroup(this.boxAPI, groupId); Iterable<BoxGroupMembership.Info> memberships = boxGroup.getAllMemberships(); for (BoxGroupMembership.Info membershipInfo : memberships) { if (membershipInfo.getUser().getLogin().toUpperCase().equals(userEmail.toUpperCase())) { BoxGroupMembership membership = new BoxGroupMembership(this.boxAPI, membershipInfo.getID()); membership.delete(); } } } ``` # Incomplete previous step Please select a preferred language / framework in step 1 to get started. This code will take the group ID, which will be the Slack channel ID, and get all members of the group. If a matching member is found for the person that left the Slack channel, based on email address, that person is removed from the group using their membership ID. # Improving performance with a data store While looking up group memberships to obtain a membership ID negates the need to store membership IDs in a local data store (like a database), this code can be made more efficient by having a data store that saves the Box membership ID with the user record. By using a local data store, the membership ID can be retrieved from the data store rather than having to call the Box API repeatedly to search for the membership ID. ## Fetch a Box group ID for a group name The next Box function we need has two main purposes. - Return the Box group ID of an existing group. - If a group doesn't exist, create the Box group and return the ID. Replace the `getGroupId` function with the following. ``` function getGroupId(groupName, callback) { client.groups.getAll().then((groups) => { const group = groups.entries.filter((g) => g.name === groupName)[0]; if (!group) { client.groups .create(groupName, { description: "Slack channel collaboration group", invitability_level: "all_managed_users", }) .then((group) => { callback(group.id); }); } else { callback(group.id); } }); } ``` Replace the `getGroupId` method with the following. ``` public String getGroupId(String groupName) { String groupId = new String(); Iterable<BoxGroup.Info> groups = BoxGroup.getAllGroups(this.boxAPI); for (BoxGroup.Info groupInfo : groups) { if (groupInfo.getName().toUpperCase().equals(groupName)) { groupId = groupInfo.getID(); } } if (groupId.isEmpty()) { BoxGroup.Info groupInfo = BoxGroup.createGroup(boxAPI, groupName); groupId = groupInfo.getID(); } return groupId; } ``` # Incomplete previous step Please select a preferred language / framework in step 1 to get started. The code fetches all the groups in the enterprise, and then tries to match the Slack channel ID to the group name. If any of the groups matches, the group ID is returned. If there are no matches, a new Box group is created and the ID of the group is returned for use. The group will be named after the Slack channel ID since that is a constant that is returned with both slash commands and user events, making it easier to lookup without additional functions. ## Add shared content to a group Finally, the main purpose of our whole application is to allow users to share files and folders from their own Box accounts with everyone else in the group. Building upon all previous functionality, the following function performs that task. Replace the `processContent` function with the following. ``` function processContent(user, channel, itemType, itemId) { getGroupId(channel, function (groupId) { const email = user.profile.email; client.enterprise.getUsers({ filter_term: email }).then((users) => { if (users.entries.length > 0) { client.asUser(users.entries[0].id); const collabRole = client.collaborationRoles.VIEWER; const collabOptions = { type: itemType }; client.collaborations .createWithGroupID(groupId, itemId, collabRole, collabOptions) .then((collaboration) => { console.log( `Content added with collaboration ID ${collaboration.id}` ); }) .catch(function (err) { console.log( util.inspect(err.response.body, { showHidden: false, depth: null, }) ); }); } }); }); } ``` Replace the `processContent` method with the following. ``` public void processContent(JSONObject userResponse, String channel, String fType, String fId) { String groupId = getGroupId(channel); JSONObject userObj = (JSONObject) userResponse.get("user"); JSONObject userProfile = (JSONObject) userObj.get("profile"); String userEmail = (String) userProfile.get("email"); Iterable<BoxUser.Info> users = BoxUser.getAllEnterpriseUsers(this.boxAPI, userEmail); for (BoxUser.Info user : users) { if (user.getLogin().toUpperCase().equals(userEmail.toUpperCase())) { String uid = user.getID(); boxAPI.asUser(uid); BoxCollaborator collabGroup = new BoxGroup(boxAPI, groupId); try { if (fType.equals("file")) { BoxFile file = new BoxFile(boxAPI, fId); file.collaborate(collabGroup, BoxCollaboration.Role.VIEWER, false, false); } else if (fType.equals("folder")) { BoxFolder folder = new BoxFolder(boxAPI, fId); folder.collaborate(collabGroup, BoxCollaboration.Role.VIEWER); } } catch (Exception ex) { System.err.println("Collaboration failed"); } boxAPI.asSelf(); } } } ``` # Incomplete previous step Please select a preferred language / framework in step 1 to get started. The code starts by capturing the Box group ID for the Slack channel, which is where content will be shared to. Since we want to share files and folders from the Box account of the person who sent the slash command, we next capture their Box user profile based on their email address. Lastly, we make a call to collaborate content with the group via the group ID. ## Summary - You've instantiated a Box client - You've created Box group user add and remove functions. - You've created a function to share content with the group. I've set up my Box functions **Reference:** https://developer.box.com/guides/collaborations/connect-slack-to-group-collabs/connect-box-functions/ --- ## Untitled *Type: quick-start | Category: Collaborations * Configure Box We will need to set up a Box application to manage Box groups and add collaborators to files and folders in Box. Set up a Box… # Configure Box We will need to set up a Box application to manage Box groups and add collaborators to files and folders in Box. ## Set up a Box app # Create a new Box app Create and configure a new Box JWT application to use for this integration. # Use an existing approved app Use one of your existing admin-approved Box JWT applications from the Box developer console. # Create a new Box app To create a new Box application that can be used to call the Box APIs, use the following steps. 1. Go to the [Developer Console](https://cloud.app.box.com/developers/console) 2. Select **Create New App** 3. Select **Platform App** as the type of application to create, and click **Next** 4. Select **OAuth 2.0 with JWT** as the authentication method, and click **Next** 5. Give your Box app a unique name and click **Create App** 6. Go to the app's configuration by clicking **View Your App**. 7. Scroll down to **Application Access** and ensure that **Enterprise** is selected. 8. Scroll to the **Application Scopes** section of the same screen and ensure that at least the following scopes are enabled: **Read and write all files and folders stored in Box**, **Manage users**, and **Manage groups**. 9. Under **Advanced Features** ensure that **Perform Actions as Users** is enabled to perform actions on behalf of Box users. 10. At the top of the page click the button to **Save Changes** # App approval Once the application is created it will still need to be approved by an enterprise admin before you will be able to make calls to the Box APIs. Follow [this guide](g://authorization/custom-app-approval) to have the application approved in your enterprise. # Use an Existing JWT Box application If you have an existing JWT based Box application in your [developer console](https://cloud.app.box.com/developers/console) that you would like to use, ensure that the following options are set in the **Configuration** section of your application. Authentication Method: Should be set to OAuth 2.0 with JWT (Server Authentication). Application Scopes: Set at least the following scopes. - Read and write all files and folders stored in Box - Manage users - Manage groups Advanced Features: Both options should be enabled to perform actions as users and generate user access tokens. # App approval Once the application is updated it will need to be re-approved by an enterprise admin before you will be able to make calls to any of the Box APIs that need any of the new permissions. Follow [this guide](g://authorization/custom-app-approval) to have the application approved in your enterprise. ## Download app configuration To begin working with the Box SDKs used in this tutorial, you will need the application configuration file from the **Configuration** page of your application. This will include all information needed to verify your application to start making API requests with the Box SDKs. Within the **Add and Manage Public Keys** section of the **Configuration** page, click to **Generate a Public/Private Keypair**. This will send you through 2FA verification before downloading the configuration file for your application. Store that file as `boxConfig.json` in a location accessible by your application. ## Summary - You created a new, or are using an existing, Box app which is approved by an enterprise admin. - You downloaded your application configuration file and stored it in a location accessible by your application. I downloaded my application configuration file **Reference:** https://developer.box.com/guides/collaborations/connect-slack-to-group-collabs/configure-box/ --- ## Untitled *Type: quick-start | Category: Collaborations * Connect Slack to Box Group Collaborations Slack is a popular communication and productivity tool, allowing for real-time coordination when… # Connect Slack to Box Group Collaborations Slack is a popular communication and productivity tool, allowing for real-time coordination when working with individuals or groups in and outside of the company. When connected to a custom Box application, the Slack channel structure, along with the [slash command](https://api.slack.com/apps/A0155185TT3/slash-commands) and [event API](https://api.slack.com/events-api) systems, can be used to provide a logical grouping and collaboration system for Box files and folders. ## Overview This quick start guide will walk you through all the steps needed to group Box users based on the channels they are in within a Slack workspace, and then permit individuals within those Slack channels to share Box files and folders with that group using a Slack slash command. At the end of this tutorial you will have a Slack bot that will be deployed to a channel in your workspace. This bot will create a Box group containing all the people present in the channel, and then listen for a `/boxadd` command in the channel. It will then parse that command and automatically collaborate the Box fie or folder with the entire group of users in the channel. This guide will take you through the following steps. 1. [Setup and configure your Slack app](g://collaborations/connect-slack-to-group-collabs/configure-slack) to handle the event notification and Slash command structures. 2. [Setup and configure your Box application](g://collaborations/connect-slack-to-group-collabs/configure-box) to connect the web application to Box. 3. [Listen for Slack events and commands](g://collaborations/connect-slack-to-group-collabs/scaffold-application-code) when users join and leave channels, or share a Box file or folder with the group. 4. [Structure Box groups and file / folder collaborations](g://collaborations/connect-slack-to-group-collabs/handle-slack-events) based on the Slack events or slash commands. 5. [And finally, deploy the application to your workspace](g://collaborations/connect-slack-to-group-collabs/connect-box-functions) and invite the Slack app bot to your channels to begin listening for events. ## Requirements This quick start guide has two requirements that are worth noting before we proceed. 1. **User emails need to match between Box and Slack**: We are connecting a Slack user account to a Box user account by comparing the Slack user email address. Therefore, a matching Box user account using the same email address must be present in your Box enterprise. 2. **You must have a publicly accessible server**: Slack will need to send event and command notification data to a public URL for your application. This guide assumes that you have a public location where your application code will be hosted, such as `https://mysite.com/`. If you don't have access to any public hosting then application platforms like [Heroku](https://heroku.com/), serverless options like [AWS lambda](https://aws.amazon.com/lambda/), or exposing localhost with services like [ngrok](https://ngrok.com/) are all options that you might want to consider. I am ready to get started **Reference:** https://developer.box.com/guides/collaborations/connect-slack-to-group-collabs/ --- ## Untitled *Type: quick-start | Category: Collaborations * Handle Slack Events With the application scaffold in place, the next step is to build the handling and processing functionality for user… # Handle Slack Events With the application scaffold in place, the next step is to build the handling and processing functionality for user events, as well as the handling of slash commands coming from Slack. Each one of these will eventually be passed to a Box API endpoint to perform group and content collaboration tasks. In this step we'll expand the empty functions we wrote in the last step. These functions will perform the following tasks. - Listen for new events and slash commands from Slack. - Process those events and commands to route to the appropriate function. - Process all Slack users in a channel to be added to a Box group when the bot is first added to a channel. - Fetch profile information for a Slack user to get their email. ## Listen for Slack events When the Slack application was configured, it was instructed to send events to our application code for three events. - When a user joins a channel. - When a user leaves a channel. - When a user enters a `/boxadd` Slash command. Our application needs to have a public route that listens for those messages from Slack. The payloads of these messages will like something like this. ``` { "token": "cF1PwB1eIMcRHZWwFHJR1tgs", "team_id": "T932DQSV12P", "team_domain": "slacktest", "channel_id": "C078N43MFHU", "channel_name": "bottest", "user_id": "U016JCDPN56", "user_name": "testuser", "command": "/boxadd", "text": "file 123456", "response_url": "https://hooks.slack.com/commands/T541DQSV12P/3977594927231/ankvsRb42WKnKPRp002FeyTx", "trigger_id": "1189442196855.1183332180295.cca20c3ca1ea193dab432ad8e9e95431" } ``` ``` { "token": "cF1PwB1eIMcRHZWwFHJR1tgs", "team_id": "T932DQSV12P", "api_app_id": "A321V573PQT", "event": { "type": "member_joined_channel", "user": "U0431JM4RLZ", "channel": "C078N43MFHU", "channel_type": "C", "team": "T932DQSV12P", "inviter": "U016JCDPN56", "event_ts": "1592858788.000700" }, "type": "event_callback", "event_id": "Ev032NRJYASJ", "event_time": 1592858788, "authed_users": [ "U0431JM4RLZ" ] } ``` ``` { "token": "cF1PwB1eIMcRHZWwFHJR1tgs", "team_id": "T932DQSV12P", "api_app_id": "A321V573PQT", "event": { "type": "member_left_channel", "user": "U0431JM4RLZ", "channel": "C078N43MFHU", "channel_type": "C", "team": "T932DQSV12P", "event_ts": "1593033236.000600" }, "type": "event_callback", "event_id": "Ev032NRJYASJ", "event_time": 1593033236, "authed_users": [ "U0431JM4RLZ" ] } ``` To start processing these events, load `process.js` in your preferred editor and replace the `app.post("/event" ...` listener with the following. ``` app.post("/event", (req, res) => { if (req.body.token !== slackConfig.verificationToken) { res.send("Slack Verification Failed"); } handler.process(res, req.body); }); ``` When an event comes through, the listener verifies that the message came from Slack, using the verification token from our Slack application. If it's a valid request, the event payload is sent to our event process function. Load `Application.java` in your preferred editor, then replace the `@PostMapping("/event")` block with the following. ``` @PostMapping("/event") @ResponseBody public void handleEvent(@RequestBody String data, @RequestHeader("Content-Type") String contentType, HttpServletResponse response) throws Exception { int code = HttpServletResponse.SC_OK; java.io.PrintWriter wr = response.getWriter(); response.setStatus(code); if (contentType.startsWith(MediaType.APPLICATION_JSON_VALUE)) { wr.write("Adding content to group"); } else { wr.print(response); } wr.flush(); wr.close(); if (! contentType.startsWith(MediaType.APPLICATION_JSON_VALUE)) { JSONObject returnJSON = new JSONObject(); String[] inputParts = data.split("&"); for (String part: inputParts) { String[] keyval = part.split("="); try { keyval[1] = java.net.URLDecoder.decode(keyval[1], StandardCharsets.UTF_8.name()); } catch (UnsupportedEncodingException e) { System.err.println(e); } returnJSON.put(keyval[0], keyval[1]); } data = returnJSON.toString(); } processEvent(data); } ``` When an event comes through, the handler will send an immediate 200 response back before code processing. Slash commands will be sent as URL encoded strings, while member join / leave events will be sent as JSON. If a slash command is encountered we respond with a processing message, otherwise we send the `HttpServletResponse` response. In this example we send a `HTTP 200` response before the event is fully processed. This is done because Slack requires a response to an event within 3 seconds from dispatch. When the code execution takes longer than 3 seconds then duplicate event will be dispatched by Slack. To make event processing easier, we want to standardize all event objects as JSON. If a content type isn't JSON it'll be the URL encoded string. If that's encountered the string is converted into a JSON object before being sent to `processEvent`. Replace `processEvent` with the following. ``` @Async public void processEvent(String data) throws Exception { Object dataObj = new JSONParser().parse(data); JSONObject inputJSON = (JSONObject) dataObj; String token = (String) inputJSON.get("token"); if (token.equals(slackConfig.verificationToken)) { // INSTANTIATE BOX CLIENT process(inputJSON); } else { System.err.println("Invalid event source"); } } ``` This method will convert the JSON event string to a JSON object, then verify that the event came from Slack by comparing the verification token. If valid, the event is routed to `process`. # Incomplete previous step Please select a preferred language / framework in step 1 to get started. ## Process Slack events Next, we will want to determine what event was received and pass this on to the right part of our application. Replace the `process` function with the following. ``` function process(res, data) { if (data.type && data.type === "event_callback") { const eventType = data.event.type; const channel = data.event.channel; const userId = data.event.user; getSlackUser(userId, function (user) { processUser(user, eventType, channel); }); res.send(); } else if (data.command && data.command === "/boxadd") { const [itemType, itemId] = data.text.split(" "); if (["file", "folder"].includes(itemType) && !isNaN(itemId)) { const userId = data.user_id; getSlackUser(userId, function (user) { processContent(user, data.channel_id, itemType, itemId); }); res.send("Adding content"); } else { res.send("Invalid input. Example usage: /boxadd file 123456"); } } else { res.send("Invalid action"); } } ``` The purpose of this function is to figure out if the payload from Slack is a user event or a Slash command, fetch any needed information, then route to the appropriate function to process the results. If the payload is a user event, denoted by `data.type` being set to `event_callback`, we extract a few pieces of information. - `eventType`: The type of event to determine if a user is leaving (`member_left_channel`) or joining (`member_joined_channel`) the channel. - `channel`: The channel ID, which will be used as the Box group name. - `userId`: The ID of the user, to look up their profile email which will bind to a user profile in Box that uses the same email. The process function then fetches the profile of the user by calling `getSlackUser`, and once obtained that user profile is sent to the `processUser` function to add or remove them from the Box group. If the payload is a slash command, denoted by `data.command` being set to `/boxadd`, the content of the command that represents the Box ID and whether it's a file or folder, such as `file 1234`, is extracted and split to get the individual values. Those values are validated for proper content. Once validated, the profile of the Slack user is obtained to get the email, then the user profile is sent to `processContent` to collaborate the Box content in with the Box group so that everyone has access. The reason for fetching the Slack user's email in this step is because the file or folder is owned by the user, not by the application's service account. When we share content (by creating a collaboration) the action will need to be performed by a user who has sharing permissions on that file or folder. For this reason, we need to match the Slack user's email address against a Box user's email address so that we can create the collaboration on their behalf. Replace the `process` method with the following. ``` public void process(JSONObject inputJSON) throws Exception { if (inputJSON.containsKey("event")) { JSONObject event = (JSONObject) inputJSON.get("event"); String eventType = (String) event.get("type"); String eventUserId = (String) event.get("user"); String eventChannel = (String) event.get("channel"); processUser(getSlackUser(eventUserId), eventType, eventChannel); } else if (inputJSON.containsKey("command")) { String eventCommand = (String) inputJSON.get("command"); if (eventCommand.equals("/boxadd")) { String eventChannelId = (String) inputJSON.get("channel_id"); String eventUserId = (String) inputJSON.get("user_id"); String cInput = (String) inputJSON.get("text"); String[] cInputParts = cInput.split(" "); if (cInputParts[0].matches("file|folder")) { processContent(getSlackUser(eventUserId), eventChannelId, cInputParts[0], cInputParts[1]); } } } else { System.err.println("Invalid event action"); } } ``` The purpose of this method is to figure out if the payload from Slack is a user event or a Slash command, fetch any needed information, then route to the appropriate method to process the results. If the payload is a user event, denoted by the event node being present in the JSON payload, we extract a few pieces of information. - `eventType`: The type of event to determine if a user is leaving (`member_left_channel`) or joining (`member_joined_channel`) the channel. - `eventUserId`: The ID of the user, to look up their profile email which will bind to a user profile in Box that uses the same email. - `eventChannel`: The channel ID, which will be used as the Box group name. We then route to `processUser`, passing in the return value from the `getSlackUser` method (a user object), the type of event, and the channel. If the payload is a slash command, denoted by the `command` node being present in the JSON payload, we extract a few pieces of information. - `eventChannelId`: The Slack channel ID, to be used as the Box group name. - `eventUserId`: The ID of the user who issued the command. - `cInputParts`: The type and ID of the command input, from a string such as `file 1234`. We then route to `processContent`, passing in the return value from the `getSlackUser` method (a user object), the channel ID, the content type (file or folder), and the content ID for the file or folder stored in Box. # Incomplete previous step Please select a preferred language / framework in step 1 to get started. ## Process Slack user Next, we need to define how user events should be processed. There are three events that we need to account for: - The bot was added to the channel. - A regular user joined the channel. - A regular user left the channel. Replace the `processUser` function with the following. ``` function processUser(user, event, channel) { getGroupId(channel, function (groupId) { // if bot was added, add all channel users if (user.is_bot) { processSlackChannel(channel, groupId); } else if ( user.profile && user.profile.email && event === "member_joined_channel" ) { addGroupUser(groupId, user.profile.email); } else if ( user.profile && user.profile.email && event === "member_left_channel" ) { removeGroupUser(groupId, user.profile.email); } }); } ``` Replace the `processUser` method with the following. ``` public void processUser(JSONObject userResponse, String event, String channel) throws Exception { String groupId = getGroupId(channel); JSONObject userObj = (JSONObject) userResponse.get("user"); Boolean isBot = (Boolean) userObj.get("is_bot"); JSONObject userProfile = (JSONObject) userObj.get("profile"); String userEmail = (String) userProfile.get("email"); if (isBot) { processSlackChannel(channel, groupId); } else if (event.equals("member_joined_channel")) { addGroupUser(groupId, userEmail); } else if (event.equals("member_left_channel")) { removeGroupUser(groupId, userEmail); } } ``` # Incomplete previous step Please select a preferred language / framework in step 1 to get started. The code starts by fetching the Box group ID for the channel, which will be defining in the next step. Once obtained, it processes users as follows. - If the user is a bot, it needs to initialize the Box group and add all current users of the channel as Box users in the group. This is to account for the bot being added to existing channels, and this is ignored if the bot is being re-added to a channel that they were already present in previously. - If the user joined the channel it needs to add them to the group. - If the user left the channel it needs to remove them from the group. ## Process Slack channel users When a bot is first added to a channel, it needs to list all users currently in the channel and create a Box group with those people in order to create a baseline for the channel. Replace the `processSlackChannel` function with the following. ``` function processSlackChannel(channel, groupId) { const limit = 100; const channelUsersPath = `https://slack.com/api/conversations.members?token=${slackConfig.botToken}&channel=${channel}&limit=${limit}`; axios.get(channelUsersPath).then((response) => { response.data.members.forEach((uid) => { getSlackUser(uid, function (user) { if (user.profile.email && !user.is_bot) { addGroupUser(groupId, user.profile.email); } }); }); }); } ``` Replace the `processSlackChannel` method with the following. ``` public void processSlackChannel(String channel, String groupId) throws Exception { String limit = "100"; String channelUsersPath = String.format("%s/conversations.members?token=%s&channel=%s&limit=%s", slackConfig.slackApiUrl, slackConfig.botToken, channel, limit); JSONObject channelUserList = sendGETRequest(channelUsersPath); JSONArray channelUserIds = (JSONArray) channelUserList.get("members"); @SuppressWarnings("rawtypes") Iterator i = channelUserIds.iterator(); while(i.hasNext()) { String uid = (String)i.next(); JSONObject userResponse = (JSONObject) getSlackUser(uid.toString()); JSONObject userObj = (JSONObject) userResponse.get("user"); JSONObject userProfile = (JSONObject) userObj.get("profile"); Boolean isBot = (Boolean) userObj.get("is_bot"); String userEmail = new String(); if (!isBot) { userEmail = (String) userProfile.get("email"); } if (!userEmail.isEmpty() && !isBot) { addGroupUser(groupId, userEmail); } } } ``` # Incomplete previous step Please select a preferred language / framework in step 1 to get started. This code runs a number of actions in sequence. - First, it calls the Slack APIs to fetch all members of the channel. The - `limit` can be adjusted to collect more users in the channel. - For every user that is found, it calls `getSlackUser` to get their profile, allow it to map their email address to a Box user's email address. - Each user is then sent to `addGroupUser` to add them into the group. ## Fetch Slack user profile The last Slack related function is a utility mechanism used by the other functions. It calls the Slack API to fetch the user profile given the user ID provided by either Slack event / command or when fetching a list of channel users. Since we're matching Slack users to Box users via their email address, that is the field that we care about from the user profile lookup. Email addresses in Box are unique and cannot be used for multiple accounts, meaning that they can be used effectively for user account lookup. Replace the `getSlackUser` function with the following. ``` function getSlackUser(userId, callback) { const userPath = `https://slack.com/api/users.info?token=${slackConfig.botToken}&user=${userId}`; axios.get(userPath).then((response) => { if (response.data.user && response.data.user.profile) { callback(response.data.user); } else { console.log("No user data found"); } }); } ``` This function makes a call to the Slack user profile endpoint, then sends the user profile information (if valid) to the specified callback. Replace the `getSlackUser` method with the following. ``` public JSONObject getSlackUser(String userId) throws Exception { String usersPath = String.format("%s/users.info?token=%s&user=%s", slackConfig.slackApiUrl, slackConfig.botToken, userId); return sendGETRequest(usersPath); } ``` This method sends a request to Slack to capture the user profile, then returns the response from that request, which should be a user profile JSON object. # Incomplete previous step Please select a preferred language / framework in step 1 to get started. ## Summary - You've verified incoming events and forwarded them to be processed. - You've processed events and routed to the appropriate function. - You've implemented functions for processing all users in a channel and for fetching the Slack profile of a single user. I've set up my Slack functions **Reference:** https://developer.box.com/guides/collaborations/connect-slack-to-group-collabs/handle-slack-events/ --- ## Untitled *Type: quick-start | Category: Collaborations * Scaffold application code With our Slack and Box applications configured, we're ready to write the code for our application which will… # Scaffold application code With our Slack and Box applications configured, we're ready to write the code for our application which will listen for incoming slash commands and events from Slack. This application will be split up into three parts: - Set up the initial application skeleton and configuration information. - Set up the handlers for Slack events and slash commands. - Connect those handlers to the required functions in Box. ## Add dependencies and scaffold code Let's start by scaffolding the files and minimal code needed to run the application. - Either create a new local directory for your application or load the existing code created for the Slack event URL challenge from [step 1](g://collaborations/connect-slack-to-group-collabs/configure-slack). - Create a new `package.json` file, or update the existing one, inside the local directory, open it in your preferred editor, copy / paste the following into it, and save / exit the file. ``` { "name": "box-slack", "version": "1.0.0", "description": "Box Slack integration", "main": "process.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "node process.js" }, "author": "Box", "license": "ISC", "dependencies": { "axios": "^0.19.2", "body-parser": "^1.19.0", "box-node-sdk": "^1.33.0", "express": "^4.17.1" } } ``` - Run `npm install` from the terminal / console to install the dependencies. - Create two files, `process.js` and `slackConfig.json` in the local directory. - Open `slackConfig.json` and save the following default configuration. ``` { "verificationToken": "TOKEN", "botToken": "TOKEN" } ``` There are two values above that will need to be replaced with details from your Slack application. Replace the `TOKEN` strings with the appropriate values. - `verificationToken`: Load up your Slack application configuration page. Within the **Basic Information** page, scroll down to the **App Credentials** section. The **Verification Token** string will be available there. - `botToken`: Within your Slack application, go to the **OAuth & Permissions** page. The **Bot User OAuth Access Token** string is available at the top and was auto-populated once the bot was added to your Slack workspace. Open up the blank `process.js` file, copy and paste the following code, and save the file. ``` const box = require("box-node-sdk"); const slackConfig = require("./slackConfig.json"); const boxConfig = require("./boxConfig.json"); const express = require("express"); const app = express(); const axios = require("axios"); const util = require("util"); app.use(express.urlencoded({ extended: true })); app.use(express.json()); // INSTANTIATE BOX CLIENT app.post('/event', (req, res) => { //HANDLE INCOMING EVENTS }); const handler = (() => { function process(res, data) { // PROCESS EVENTS } function processUser(user, event, channel) { // PROCESS USER ADD / REMOVE REQUEST } function addGroupUser(groupId, email) { // ADD USER TO BOX GROUP } function removeGroupUser(groupId, email) { // REMOVE USER FROM BOX GROUP } function processContent(user, channel, type, fid) { // COLLABORATE CONTENT WITH GROUP } function processSlackChannel(channel, gid) { // ADD ALL SLACK CHANNEL USERS TO GROUP } function getSlackUser(userId, _callback) { // GET SLACK USER PROFILE } function getGroupId(groupName, _callback) { // GET AND CREATE BOX GROUP } return { process }; })(); const port = process.env.PORT || 3000; app.listen(port, function(err) { console.log("Server listening on PORT", port); }); ``` This code contains all of the main functions that will be needed to handle and process the communication between Slack and Box. From top to bottom, the functions are: - `/event` handler: Captures all incoming Slack traffic, verifies the content, and routes them to the `process` function. - `process`: Parses the Slack event and routes the event to either Box group processing (user channel events) or to add Box content to the group (slash commands). - `processUser`: Handles user events, either adding or removing a user from a Box group by routing to the appropriate functions. - `addGroupUser`: Adds a user to a Box group. - `removeGroupUser`: Removes a user from a Box group. - `processContent`: Collaborates Box content with the Box group. - `processSlackChannel`: Adds all Slack channel users to a Box group. - `getSlackUser`: Utility function to fetch a Slack user's profile from their Slack user ID. - `getGroupId`: Utility function to fetch a Box group ID from a Box group name. A configuration file needs to be created to house our Slack credentials and URLs. - Within your `src/main/java` path, where your `Application.java` file is located, create a new Java class file named `slackConfig.java`. - Open the file and save the following into it. ``` package com.box.slack.box; public class slackConfig { public static String verificationToken = "TOKEN"; public static String botToken = "TOKEN"; public static String slackApiUrl = "https://slack.com/api"; } ``` There are two values above that will need to be replaced with details from your Slack application. Replace the `TOKEN` strings with the appropriate values. - `verificationToken`: Load up your Slack application configuration page. Within the **Basic Information** page, scroll down to the **App Credentials** section. The **Verification Token** string will be available there. - `botToken`: Within your Slack application, go to the **OAuth & Permissions** page. The **Bot User OAuth Access Token** string is available at the top and was auto-populated once the bot was added to your Slack workspace. Open up the `Application.java` file that was created for the previous Slack event challenge setup and replace the content of the file with the following. ``` package com.box.slack.box; import java.io.BufferedReader; import java.io.FileReader; import java.io.InputStreamReader; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.Iterator; import javax.servlet.http.HttpServletResponse; import org.jose4j.json.internal.json_simple.JSONArray; import org.jose4j.json.internal.json_simple.JSONObject; import org.jose4j.json.internal.json_simple.parser.JSONParser; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.http.MediaType; import org.springframework.scheduling.annotation.Async; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import com.box.sdk.BoxAPIConnection; import com.box.sdk.BoxCollaborator; import com.box.sdk.BoxCollaboration; import com.box.sdk.BoxConfig; import com.box.sdk.BoxDeveloperEditionAPIConnection; import com.box.sdk.BoxFile; import com.box.sdk.BoxFolder; import com.box.sdk.BoxGroup; import com.box.sdk.BoxGroupMembership; import com.box.sdk.BoxUser; @RestController @EnableAutoConfiguration public class Application extends slackConfig { private Reader fileReader; private BoxConfig boxConfig; private BoxAPIConnection boxAPI; @PostMapping("/event") @ResponseBody public void handleEvent(@RequestBody String data, @RequestHeader("Content-Type") String contentType, HttpServletResponse response) throws Exception { // HANDLE EVENTS } @Async public void processEvent(String data) throws Exception { // VERIFY EVENTS } public void process(JSONObject inputJSON) throws Exception { // PROCESS EVENTS } public void processUser(JSONObject userResponse, String event, String channel) throws Exception { // PROCESS USER ADD / REMOVE REQUEST } public void addGroupUser(String groupId, String userEmail) { // ADD USER TO BOX GROUP } public void removeGroupUser(String groupId, String userEmail) { // REMOVE USER FROM BOX GROUP } public void processContent(JSONObject userResponse, String channel, String fType, String fId) { // COLLABORATE CONTENT WITH GROUP } public void processSlackChannel(String channel, String groupId) throws Exception { // ADD ALL SLACK CHANNEL USERS TO GROUP } public JSONObject getSlackUser(String userId) throws Exception { // GET SLACK USER PROFILE } public String getGroupId(String groupName) { // GET AND CREATE BOX GROUP } public JSONObject sendGETRequest(String reqURL) throws Exception { StringBuffer response = new StringBuffer(); URL obj = new URL(reqURL); HttpURLConnection httpURLConnection = (HttpURLConnection) obj.openConnection(); httpURLConnection.setRequestMethod("GET"); int responseCode = httpURLConnection.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { BufferedReader in = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream())); String inputLine; while ((inputLine = in .readLine()) != null) { response.append(inputLine); } in .close(); } else { System.err.println("GET request failed"); } Object dataObj = new JSONParser().parse(response.toString()); JSONObject outputJSON = (JSONObject) dataObj; return outputJSON; } public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` This code contains all of the main functions that will be needed to handle and process the communication between Slack and Box. From top to bottom, the functions are: - `handleEvent`: Captures all incoming Slack traffic and responds with an HTTP 200 response to notify Slack that the event was received. Since slash commands will transmit their payload as `application/x-www-form-urlencoded` rather than `application/json`, we convert those payloads to JSON objects to standardize the input. - `processEvent`: Verifies that the event is from Slack, instantiates a Box client, and routes for processing. - `process`: Parses the Slack event and routes to either Box group processing (user channel events) or to add Box content to the group (slash commands). - `processUser`: Handles user event requirements to either add or remove a user to a Box group by routing to the appropriate functions. - `addGroupUser`: Adds a user to a Box group. - `removeGroupUser`: Removes a user from a Box group. - `processContent`: Collaborates Box content with the Box group. - `processSlackChannel`: Adds all Slack channel users to a Box group. - `getSlackUser`: Utility function to fetch a Slack user profile from a Slack user ID. - `getGroupId`: Utility function to fetch a Box group ID from a Box group name. - `sendGETRequest`: Utility function to send an HTTP GET request. # Incomplete previous step Please select a preferred language / framework in step 1 to get started. ## Summary - You created a minimal application scaffold, and provided the basic configuration details. - You installed all the project dependencies. I have my local application set up **Reference:** https://developer.box.com/guides/collaborations/connect-slack-to-group-collabs/scaffold-application-code/ --- ## Untitled *Type: quick-start | Category: Collaborations * Test the bot In this last section we'll test the full range of functionality of the Slack bot. Group creation: When a bot is added to a… # Test the bot In this last section we'll test the full range of functionality of the Slack bot. - **Group creation**: When a bot is added to a channel, a new Box group should be created with all of the current channel participants. Only those people in the channel with matching Box accounts (based on email address matching) should be added. - **User event functions**: When a user enters or leaves a channel, they should be added or removed from the channel group. - **Content add functions**: When a user enters a valid `/boxadd` slash command, that content should be shared with the group through a new collaboration. If you haven't done so already, ensure that all the code we've written so far is deployed as an application that is publicly accessible. ## Testing group creation When a bot is first added to a channel, there are a number of expectations. - A new group is created with a name that matches the Slack channel ID. - All people currently in the channel are added to the group, as long as their Slack email address matches an enterprise account with the same email. When the Slack bot was configured in [step 1](g://collaborations/connect-slack-to-group-collabs/configure-slack), we installed it within the Slack workspace. To test group creation we need to add it to a channel. From any Slack channel, invite the Slack bot, either through the Slack UI or using a `/invite @bot_app_name` command. Once added, verify that the group is created in Box and members have been added. From a Box enterprise admin account, go to the **[Users and Groups](https://app.box.com/master/groups)** section of the admin console. If successful, you will see a group with an random alpha-numeric string as the group name. This is the Slack channel ID, which is mirrored in the group name. Under the **Members** column, you should also see a number indicating the number of Box users with matching email addresses that were found in the enterprise and added to the group during group creation. If you see your group and members, this step is a success. If you see no members added to the group, and no errors being returned from the bot application, the most likely cause is email mismatch. Ensure that the email addresses used by the accounts in Slack match the emails used by users in your Box enterprise. ## Testing user event functions Keep the **[Users and Groups](https://app.box.com/master/groups)** section of the Box admin console open and take note of the number in the **Members** column beside your Slack group. From the Slack channel with the bot invited, add or remove someone other than the bot from the channel. Refresh the users and groups section of the Box admin console and you should see the members number drop or increase depending on if you added or removed a user. If the number of members changes, this step is a success. ## Testing content add functions To test the functionality of sharing content with the group, you will need access to two users in the channel, one person to share the content from their Box account, and another person in the group to view their list of files to verify that the content was shared. From the Slack channel with the bot invited, type in the slash command to share files or folders with the group, in the format of `/boxadd [file/folder] [ID of file/folder]`, such as `/boxadd folder 324554221`. The file or folder ID specified needs to be content within the Box account of the person sharing it. To find the ID of a file or folder in a Box account, load up the file or folder within [the Box site](https://box.com), then look at the URL. The ID will be the last string in the URL, often right after a section which includes either `/file/` or `/folder/`. Once the command is typed in, go to the [the Box site](https://box.com) account of another person within the Slack channel and Box group. The content that was shared should now be available to that account. If the shared content is available to others in the group, this step is a success. ## Next Steps With our minima bot deployed we can now think about ways to improve the experience on top of what we've created so far. Next steps in this journey may include any number of different expansion areas. - Add new slash commands for [creating shared links](g://shared-links/create-or-update) for the files and folders shared with the group, allowing you to share the content with others who are not in the group and people outside your enterprise. - [Creating retention policies](e://post-retention-policies) and [assigning them](e://post-retention-policy-assignments) to any content shared with the group, allowing you to control the lifespan and governance of shared content. - Adding new slash commands for allowing people in the channel to [comment on the files](e://post-comments) that are shared with the group. **Reference:** https://developer.box.com/guides/collaborations/connect-slack-to-group-collabs/test-bot/ --- ## Untitled *Type: guide | Category: Events * Migrating From History To Stream Box recommends that applications subscribing to live events through admin_logs migrate to admin_logs… # Migrating From History To Stream Box recommends that applications subscribing to live events through `admin_logs` migrate to `admin_logs_streaming`. `admin_logs_streaming` provides lower and more consistent latency, as well as ensures that late arriving events will not be missed. Events can be deduplicated between `admin_logs` and `admin_logs_streaming` by their event IDs. ## Enterprise stream_type comparison ### Benefits of admins_logs_streaming - Ensures late arriving events are not missed by your subscribing application - Provides 80% lower latency (during normal operations) - Has much more consistent latency (during normal operations) - Recovers much more gracefully from a fault, because your service no longer has to manage backfilling late events ### Differences between admin_logs and admin_logs_streaming - Provides two weeks of event history (i.e. retention) - Does not support the `created_after` and `created_before` filter parameters - May contain duplicates (provides an 'at least once' guarantee) - No longer returns events in chronological order (events are returned in roughly the order they are processed) ### Similarities between admin_logs and admin_logs_streaming - Shares the same [`GET /events`](e://events) API endpoint - Returns the same events payload (events can be deduplicated across the two stream types by event ID) - Enables filtering by `event_type` - Allows paginating through events via a `stream_position` ## How to migrate from admin_logs to admin_logs_streaming ### 1. Existing requests will look something like the below ``` curl https://api.box.com/2.0/events?stream_type=admin_logs&stream_position=1632893855 \ -H "authorization: Bearer <ACCESS_TOKEN>" ``` ### 2. Begin overlapping existing requests with admin_logs_streaming Start two weeks ago and backfill: ``` curl https://api.box.com/2.0/events?stream_type=admin_logs_streaming&stream_position=0 \ -H "authorization: Bearer <ACCESS_TOKEN>" ``` or Start now and run in parallel: ``` curl https://api.box.com/2.0/events?stream_type=admin_logs_streaming&stream_position=now \ -H "authorization: Bearer <ACCESS_TOKEN>" ``` ### 3. Paginate through results until now and deduplicate with admin_logs events ``` curl https://api.box.com/2.0/events?stream_type=admin_logs_streaming&stream_position=1632893855 \ -H "authorization: Bearer <ACCESS_TOKEN>" ``` ### 4. Continue to overlap until confident ### 5. Turn off old admin_logs requests **Reference:** https://developer.box.com/guides/events/enterprise-events/migrate-to-stream/ --- ## Untitled *Type: guide | Category: Events * Get Enterprise Events To get a enterprise's events, make a call to the GET /events API with the stream_type set to admin_logs or admin_logs… # Get Enterprise Events To get a enterprise's events, make a call to the [`GET /events`](e://get_events) API with the `stream_type` set to `admin_logs` or `admin_logs_streaming`. This API requires the user to be an enterprise admin or co-admin with the permission to **Run new reports and access existing reports**. ## Stream Types | Stream Type | | | --- | --- | | admin_logs | Enables querying historical events up to one year | | admin_logs_streaming | Enables subscribing to live events in near real time | ## Live Monitoring To monitor recent events that have been generated within Box across the enterprise, set the `stream_type` to `admin_logs_streaming`. This is also known as the Enterprise Event Stream API. The emphasis for this feed is on low latency rather than chronological accuracy, which means that Box may return events more than once and out of chronological order. Events are returned via the API in near real time after they are processed by Box. A small delay/buffer ensures that new events are not written after your cursor position. Only two weeks of events are available via this `stream_type`. ## Historical Querying To query historical events across the enterprise up to one year old, set the `stream_type` to `admin_logs`. This is also known as the Enterprise Event History API. The emphasis for this feed is on completeness over latency, which means that Box will deliver admin events in chronological order and without duplicates, but with higher latency than the user or `admin_logs_streaming` feed. Consuming events in near real time may lead to missed events as events can arrive later than your filtering window. ## Anonymous Users In some cases, the event feed might list a user with an ID of `2`. This is Box's internal identifier for anonymous users. An anonymous user is a user that is not logged in. This can happen any time a user interacts with content and they aren't asked to log in first. An example would be when a user downloads a file through an open shared link. ## Limitations The enterprise event feed does not support long polling. Box does not store events indefinitely. Two weeks of enterprise events are available when `stream_type` is set to `admin_logs_streaming`. One year of enterprise events are available when `stream_type` is set to `admin_logs`. Seven years of enterprise events are available via the Box Admin Console’s [exported reports](https://support.box.com/hc/en-us/articles/360043696534-Running-Reports). The emphasis of the `admin_logs_streaming` feed is to return the complete results quickly, which means that Box may return events more than once and out of order. Duplicate events can be identified by their event IDs. ## Filter by Event Type The enterprise event feed supports filtering by event type. A full list of event types can be found below. ## Event Types The following events can be triggered for an enterprise. This list is not exhaustive, so it is possible events appear that are not listed. | Event name | Description | | --- | --- | | ACCESS_GRANTED | A user has granted Box support access to their account | | ACCESS_REVOKED | A user has revoked Box support access to their account | | ADD_DEVICE_ASSOCIATION | Added device association | | ADD_LOGIN_ACTIVITY_DEVICE | A user is logging in from a device we have not seen before | | ADMIN_LOGIN | Admin console used to log in to a managed user's account | | ANNOTATIONV2_CREATE | An annotation is created | | ANNOTATIONV2_DELETE | An annotation is deleted | | ANNOTATIONV2_EDIT | An annotation is edited | | APPLICATION_CREATED | A new application is created in the Box Developer Console | | APPLICATION_PUBLIC_KEY_ADDED | An application public key is added. | | APPLICATION_PUBLIC_KEY_DELETED | An application public key is deleted. | | BOX_AI_USER_REQUEST | User asks Box AI a question or makes a request | | BOX_AI_USER_FAILED_REQUEST | The question or request returns an error | | CHANGE_ADMIN_ROLE | When an admin role changes for a user | | CHANGE_FOLDER_PERMISSION | Edit the permissions on a folder | | COLLABORATION_ACCEPT | Accepted invites | | COLLABORATION_EXPIRATION | Set collaborator expiration | | COLLABORATION_INVITE | Invited | | COLLABORATION_REMOVE | Removed collaborators | | COLLABORATION_ROLE_CHANGE | Changed user roles | | COLLECTION_CREATE | A collection is created | | COLLECTION_DELETE | A collection is deleted | | COLLECTION_UPDATE | A collection is updated | | COLLECTION_ITEM_CREATE | An item is added to a collection | | COLLECTION_ITEM_DELETE | An item is removed from a collection | | COLLECTION_ITEM_UPDATE | An item in a collection is updated | | COMMENT_CREATE | A comment is created on a file | | COMMENT_DELETE | A comment is deleted on a file | | CONTENT_ACCESS | A file is accessed by an authorized end user or programmatically by a Box application | | CONTENT_RECOVERY_REPORT_CREATE | A Content Recovery report is created | | CONTENT_RECOVERY_REPORT_DELETE | A Content Recovery report is deleted | | CONTENT_RECOVERY_REPORT_INITIATE | A Content Recovery report is initiated | | CONTENT_WORKFLOW_ABNORMAL_DOWNLOAD_ACTIVITY | A policy set in the Admin console is triggered | | CONTENT_WORKFLOW_AUTOMATION_ADD | An automation is added | | CONTENT_WORKFLOW_AUTOMATION_DELETE | An automation is deleted | | CONTENT_WORKFLOW_POLICY_ADD | A content policy is added | | CONTENT_WORKFLOW_SHARING_POLICY_VIOLATION | There is a sharing policy violation. | | CONTENT_WORKFLOW_UPLOAD_POLICY_VIOLATION | A collaborator violated an admin-set upload policy | | COPY | Copied | | DATA_RETENTION_CREATE_RETENTION | Retention is created | | DATA_RETENTION_REMOVE_RETENTION | Retention is removed | | DELETE | Deleted | | DELETE_USER | Deleted user | | DEVICE_TRUST_CHECK_FAILED | Device Trust check failed | | DISABLE_MULTI_FACTOR_AUTH | When multifactor authentication has been disabled | | DOWNLOAD | Downloaded | | EDIT | Edited | | EDIT_USER | Edited user | | EDR_CROWDSTRIKE_DEVICE_DETECTED | Device detected by the CrowdStrike Falcon platform | | EDR_CROWDSTRIKE_NO_BOX_TOOLS | Box Tools package not detected on device with CrowdStrike Falcon platform support | | EDR_CROWDSTRIKE_BOX_TOOLS_OUTDATED | Box Tools package outdated on device with CrowdStrike Falcon platform support | | EDR_CROWDSTRIKE_DRIVE_OUTDATED | Box Drive application outdated on device with CrowdStrike Falcon platform support | | EDR_CROWDSTRIKE_ACCESS_ALLOWED_NO_CROWDSTRIKE_DEVICE | Access allowed to a device not identified by the CrowdStrike Falcon platform | | EDR_CROWDSTRIKE_ACCESS_REVOKED | Access revoked to a device identified by the CrowdStrike Falcon platform | | EMAIL_ALIAS_CONFIRM | A user email alias is confirmed | | EMAIL_ALIAS_REMOVE | A user email alias is removed | | ENABLE_MULTI_FACTOR_AUTH | When multifactor authentication has been enabled | | ENTERPRISE_APP_AUTHORIZATION_UPDATE | When a JWT application has been authorized or reauthorized | | EXTERNAL_COLLAB_SECURITY_SETTINGS | Changes in external collaboration security settings | | FAILED_LOGIN | Failed login | | FILE_MARKED_MALICIOUS | Virus found on a file. Event is only received by enterprises that have opted in to be notified. | | FILE_WATERMARKED_DOWNLOAD | A watermarked file is downloaded | | GROUP_ADD_ITEM | An item is added to a group | | GROUP_ADD_USER | Added user to group | | GROUP_ADMIN_CREATED | Group admin is created | | GROUP_ADMIN_DELETED | Group admin is deleted | | GROUP_ADMIN_PERMISSIONS_UPDATED | Group admin permissions are updated | | GROUP_CREATION | Created new group | | GROUP_DELETION | Deleted group | | GROUP_EDITED | Edited group | | GROUP_REMOVE_ITEM | Folders were removed from a group in the Admin console | | GROUP_REMOVE_USER | Removed user from group | | ITEM_EMAIL_SEND | An email is sent to a collaborator on an item | | ITEM_MODIFY | Item is modified | | ITEM_OPEN | Item is opened | | ITEM_SHARED_UPDATE | Share links settings updated | | ITEM_SYNC | Synced folder | | ITEM_UNSYNC | Unmarked folder for synced | | LEGAL_HOLD_ASSIGNMENT_CREATE | A legal hold assignment is created | | LEGAL_HOLD_ASSIGNMENT_DELETE | A legal hold assignment is deleted | | LEGAL_HOLD_POLICY_CREATE | A legal hold policy is created | | LEGAL_HOLD_POLICY_DELETE | A legal hold policy is deleted | | LEGAL_HOLD_POLICY_UPDATE | A legal hold policy is updated | | LOCK | Locked | | LOGIN | Login | | METADATA_INSTANCE_CREATE | Creation of metadata instance | | METADATA_INSTANCE_DELETE | Deletion of metadata instance | | METADATA_INSTANCE_UPDATE | Update of metadata instance | | METADATA_TEMPLATE_CREATE | Creation of metadata template | | METADATA_TEMPLATE_UPDATE | Update of metadata template | | METADATA_TEMPLATE_DELETE | Deletion of metadata template | | MOVE | Moved | | NEW_USER | Created user | | OAUTH2_ACCESS_TOKEN_REVOKE | An OAuth 2.0 access token has been revoked | | PREVIEW | Previewed | | REMOVE_DEVICE_ASSOCIATION | Removed device association | | REMOVE_LOGIN_ACTIVITY_DEVICE | We invalidated a user session associated with an app | | RENAME | A file or folder name or description is changed | | RETENTION_POLICY_ASSIGNMENT_ADD | A retention policy assignment is added | | SHARE | Enabled shared links | | SHARE_EXPIRATION | Set shared link expiration | | SHARED_LINK_SEND | An email is sent with a shared link and an optional message | | SHARED_LINK_REDIRECT_OUT_OF_SHARED_CONTEXT | Shared link causes a redirect | | SHIELD_ALERT | Shield detected an anomalous download, session, location, or malicious content based on enterprise Shield rules. See shield alert events for more information. | | SHIELD_DOWNLOAD_BLOCKED | End user blocked from downloading a file based on a shield access policy | | SHIELD_EXTERNAL_COLLAB_ACCESS_BLOCKED | Access to an external collaboration is blocked | | SHIELD_EXTERNAL_COLLAB_ACCESS_BLOCKED_MISSING_JUSTIFICATION | Access to an external collaboration is blocked due to missing a justification | | SHIELD_EXTERNAL_COLLAB_INVITE_BLOCKED | An invite to externally collaborate is blocked | | SHIELD_EXTERNAL_COLLAB_INVITE_BLOCKED_MISSING_JUSTIFICATION | An invite to externally collaborate is blocked due to missing a justification | | SHIELD_EXTERNAL_COLLAB_INVITE_JUSTIFIED | An invite to externally collaborate is justified | | SHIELD_JUSTIFICATION_APPROVAL | A Shield justification is approved | | SHIELD_SHARED_LINK_ACCESS_BLOCKED | Access to a shared link is blocked | | SHIELD_SHARED_LINK_STATUS_RESTRICTED_ON_CREATE | A shared link is created and access to the content is restricted | | SHIELD_SHARED_LINK_STATUS_RESTRICTED_ON_UPDATE | A shared link is updated and access to the content is restricted | | SHIELD_INFORMATION_BARRIER_ENABLED | A Shield information barrier is enabled | | SHIELD_INFORMATION_BARRIER_DISABLED | A Shield information barrier is deactivated | | SHIELD_INFORMATION_BARRIER_PENDING | A Shield information barrier is not yet active | | SHIELD_INFORMATION_BARRIER_GROUP_ADD_USER_BLOCKED | Adding user blocked due to information barrier restrictions | | SHIELD_INFORMATION_BARRIER_COLLAB_BLOCKED | Creating collaborations for users blocked due to information barrier restrictions | | SHIELD_INFORMATION_BARRIER_SHARED_ITEM_ACCESS_BLOCKED | Access to shared items blocked for users due to information barrier restrictions | | SHIELD_INFORMATION_BARRIER_ITEM_MOVE_BLOCKED | Moving items blocked due to information barrier restrictions | | SHIELD_INFORMATION_BARRIER_ITEM_COPY_BLOCKED | Copying items blocked due to information barrier restrictions | | SHIELD_INFORMATION_BARRIER_ITEM_OWNER_TRANSFER_BLOCKED | Transferring items blocked due to information barrier restrictions | | SIGN_DOCUMENT_ASSIGNED | A sign request was sent to a signer | | SIGN_DOCUMENT_CANCELLED | A sign request was cancelled via API or UI | | SIGN_DOCUMENT_COMPLETED | A sign request was signed by all signers | | SIGN_DOCUMENT_CONVERTED | A sign request was converted to a .pdf for signing | | SIGN_DOCUMENT_CREATED | A sign request was created via API or UI. The document is not yet sent to signers | | SIGN_DOCUMENT_DECLINED | A sign request was declined by a signer | | SIGN_DOCUMENT_EXPIRED | A sign request expired with incomplete signatures | | SIGN_DOCUMENT_SIGNED | A sign request was signed by a signer | | SIGN_DOCUMENT_VIEWED_BY_SIGNER | A signer clicked on Review Document in the signer email or visited the signing URL | | SIGNER_DOWNLOADED | A signer downloaded the signing document | | SIGNER_FORWARDED | A signer forwarded the signing document | | STORAGE_EXPIRATION | Set file auto-delete | | TASK_ASSIGNMENT_UPDATE | Update of a task assignment | | TASK_ASSIGNMENT_CREATE | A task assignment is created | | TASK_ASSIGNMENT_DELETE | A task assignment is deleted | | TASK_CREATE | A task is created. | | TASK_UPDATE | A task's comment is edited | | TERMS_OF_SERVICE_ACCEPT | Accepted terms | | TERMS_OF_SERVICE_REJECT | Rejected terms | | UNDELETE | Restored | | UNLOCK | Unlocked | | UNSHARE | Shared link removed | | UPDATE_COLLABORATION_EXPIRATION | Extend collaborator expiration | | UPDATE_SHARE_EXPIRATION | Extend shared link expiration | | UPLOAD | Uploaded | | USER_AUTHENTICATE_OAUTH2_ACCESS_TOKEN_CREATE | An OAuth 2.0 access token has been created | | WATERMARK_LABEL_CREATE | A watermark is added to a file | | WATERMARK_LABEL_DELETE | A watermark is removed from a file | **Reference:** https://developer.box.com/guides/events/enterprise-events/for-enterprise/ --- ## Untitled *Type: guide | Category: Events * Enterprise Events Enterprise events provide an event feed for all users and content in an enterprise Box instance. Depending upon the stream… # Enterprise Events Enterprise events provide an event feed for all users and content in an enterprise Box instance. Depending upon the `stream_type` your application can either subscribe to live events or query historical events. Access to these stream types is limited to users with admin permissions to **Run new reports and access existing reports**. **Reference:** https://developer.box.com/guides/events/enterprise-events/ --- ## Untitled *Type: guide | Category: Events * Shield Alert Events Box Shield must be purchased and enabled on a Box enterprise to take advantage of the advanced security offerings… # Shield Alert Events [Box Shield](https://www.box.com/shield) must be purchased and enabled on a Box enterprise to take advantage of the advanced security offerings outlined below. ## Threat detection alerts Shield [threat detection](https://support.box.com/hc/en-us/articles/360044196113-Using-Threat-Detection) delivers context-rich alerts on potential threats, such as compromised accounts and data theft, based on anomalous user behavior. The possible alerts produced by Shield are for: 1. Suspicious locations 2. Suspicious sessions 3. Anomalous downloads 4. Malicious content All Shield threat detection alert events are produced within the [enterprise event](g://events/enterprise-events/for-enterprise) stream. These events follow the standard event object schema and the `event_type` value is set to `SHIELD_ALERT`. ``` { "source": null, "created_by": { "type": "user", "id": "2", "name": "Unknown User", "login": "" }, "action_by": null, "created_at": "2019-12-20T11:38:56-08:00", "event_id": "97f1b31f-f143-4777-81f8-1b557b39ca33", "event_type": "SHIELD_ALERT", "ip_address": "10.1.2.3", "type": "event", "session_id": null, "additional_details": { "..." } } ``` The `additional_details` object provides information about the specific type of shield alert that triggered the event. ### Suspicious locations alert A suspicious locations alert is produced when Shield detects a user accessing content from an unusual, excluded geographic location, or 'host' IP address. It can be identified by the `Suspicious Locations` value within `additional_details.shield_alert.rule_category`. The `additional_details` payload will provide the following details: ``` "additional_details": { "shield_alert": { "rule_category": "Suspicious Locations", "rule_id": 123, "rule_name": "Suspicious Location", "rule_response_action": { "restrict_user": true }, "risk_score": 60, "alert_summary": { "alert_activities": [ { "occurred_at": "2019-12-20T11:37:05-08:00", "event_type": "Download", "item_name": "xyz.txt", "item_type": "file", "item_id": "127", "item_path": "ABC/DEF", "ip_info": { "ip": "1.2.3.4", "latitude": "37.5555", "longitude": "-120.6789", "registrant": "Microsoft Corporation", "country_code": "US", "city_name": "San Jose", "region_name": "California" }, "service_name": "Box Excel Online Previewer" } ] }, "alert_id": 2398, "priority": "medium", "user": { "id": 2320, "name": "Some name", "email": "some@email.com" }, "link": "https://app.box.com/master/shield/alerts/2398", "created_at": "2019-12-20T11:37:15-08:00" } } ``` ### Suspicious sessions alert A suspicious sessions alert is produced when Shield detects a user accessing content in a session characterized by unusual user-agent strings, unusual IDs, uncommon types of applications, new IP addresses, and an improbably rapid change in the person's log-in location. It can be identified by the `Suspicious Sessions` value within `additional_details.shield_alert.rule_category`. The `additional_details` payload will provide the following details: ``` "additional_details": { "shield_alert": { "rule_category": "Suspicious Sessions", "rule_id": 123, "rule_name": "Suspicious Session", "rule_response_action": null, "risk_score": 77, "alert_summary": { "description": "First time in prior month user connected from ip 2.3.4.5 First time user agent Some User Agent (Some UA 4.5.6) appeared for user within prior month Apparent distance 9580.0 km between events 59 seconds apart is faster than possible", "sessions": [ { "session_type": "suspicious", "activities": [ { "occurred_at": "2019-12-19T11:37:00-08:00", "event_type": "Set shared link expiration", "item_name": "xyz.txt", "item_type": "file", "item_id": "123456", "item_path": "ABC/DEF", "ip_info": { "ip": "2.3.4.5", "latitude": "37.5555", "longitude": "-120.6789", "registrant": "Microsoft Corporation", "country_code": "US", "city_name": "San Jose", "region_name": "California" }, "service_name": "ServiceName" } ] }, { "session_type": "typical", "activities": [ { "occurred_at": "2019-12-19T11:37:59-08:00", "event_type": "Item Modified", "item_name": "abc.boxnote", "item_type": "file", "item_id": "123123", "item_path": "folder/sub folder", "ip_info": { "ip": "4.5.6.7", "latitude": "37.5555", "longitude": "-20.6789", "country_code": "US", "city_name": "Some City", "region_name": "XYZ" }, "service_name": "Box Notes" } ] } ] }, "alert_id": 500, "priority": "medium", "user": { "id": 50500, "name": "A b c", "email": "a@b.c" }, "link": "https://cloud.app.box.com/master/shield/alerts/500", "created_at": "2019-12-20T11:38:16-08:00" } } ``` ### Anomalous download alert An anomalous download alert is produced when Shield detects an account holder who may be stealing sensitive content. It can be identified by the `Anomalous Download` value within `additional_details.shield_alert.rule_category`. The `additional_details` payload will provide the following details: ``` "additional_details": { "shield_alert": { "rule_category": "Anomalous Download", "rule_id": 123, "rule_name": "Anomalous Download Rule", "rule_response_action": null, "risk_score": 77, "alert_summary": { "description": "Significant increase in download content week over week, 9200% (25.04 MB) more than last week 12 additional files downloaded week over week)", "download_delta_size": "25 Mb", "download_delta_percent": 9200, "historical_period": { "date_range": { "start_date": "2019-12-01T01:01:00-08:00", "end_date": "2019-12-08T01:01:00-08:00" }, "download_size": "0 Mb", "downloaded_files_count": 1 }, "anomaly_period": { "date_range": { "start_date": "2019-12-08T01:01:00-08:00", "end_date": "2019-12-15T01:01:00-08:00" }, "download_size": "25 Mb", "downloaded_files_count": 13 }, "download_ips": [ { "ip": "1.2.3.4" } ] }, "alert_id": 444, "priority": "medium", "user": { "id": 567, "name": "Some user", "email": "some@user.com" }, "link": "https://cloud.app.box.com/master/shield/alerts/444", "created_at": "2019-12-20T11:38:16-08:00" } } ``` ### Malicious content alert A malicious content alert is produced when Shield detects potential malware in content uploading to an account. It can be identified by the `Malicious Content` value within `additional_details.shield_alert.rule_category`. The `additional_details` payload will provide the following details: ``` "additional_details": { "shield_alert": { "rule_category": "Malicious Content", "rule_id": 123, "rule_name": "Viruses and stuff", "rule_response_action": null, "risk_score": 100, "alert_summary": { "upload_activity": { "occurred_at": "2019-12-20T11:37:05-08:00", "event_type": "Upload", "item_name": "virus.exe", "item_type": "file", "item_id": "127", "item_path": "ABC/DEF", "sha1_hash": "", "ip_info": { "ip": "1.2.3.4", "latitude": "37.5555", "longitude": "-120.6789", "registrant": "Microsoft Corporation", "country_code": "US", "city_name": "San Jose", "region_name": "California" }, "service_name": "Service name" } }, "malware_info": { "file_id": 127, "file_name": "malware.exe", "file_version": 4239023, "file_created": "2019-12-20T11:37:05-08:00", "file_created_by": { "id": 1010, "name": "Bob", "email": "bob@enterprise.com" }, "file_hash": "d869db7fe62fb07c25a0403ecaea55031744b5fb", "file_hash_type": "SHA-1", "file_size_bytes": 51345, "file_version_uploaded": "2019-12-20T11:37:05-08:00", "file_version_uploaded_by": { "id": 1011, "name": "Jane", "email": "jane@enterprise.com" }, "status": "Malicious", "categories": [ "Adware", "SpyWare" ], "tags": [ "FILE_MALICIOUS_EXECUTION", "FILE_OTHER_TAG" ], "description": "This is a really bad file", "detail_link": "https://some.link/xyz", "malware_name": "BadMalware", "first_seen": "2019-12-19T11:37:05-08:00", "last_seen": "2019-12-20T11:37:05-08:00", "family": "MalwareBot4000" }, "alert_id": 2398, "priority": "medium", "user": { "id": 2320, "name": "Some Name", "email": "some@email.com" }, "link": "https://app.box.com/master/shield/alerts/2398", "created_at": "2019-12-20T11:37:15-08:00" } } ``` **Reference:** https://developer.box.com/guides/events/event-triggers/shield-alert-events/ --- ## Untitled *Type: guide | Category: Events * Event Source If a user or item triggers an event, The response of the GET /events endpoint contains an event source object. User Source… # Event Source If a user or item triggers an event, The response of the [`GET /events`](e://get_events) endpoint contains an event source object. ## User Source Object If a user triggers an event, the source object will be the standard representation of the [user resource](e://resources/user). ``` { "source": { "id": 11446498, "type": "user", "address": "900 Jefferson Ave, Redwood City, CA 94063", "avatar_url": "https://www.box.com/api/avatar/large/181216415", "created_at": "2012-12-12T10:53:43-08:00", "job_title": "CEO", "language": "en", "login": "ceo@example.com", "max_upload_size": 2147483648, "modified_at": "2012-12-12T10:53:43-08:00", "name": "Aaron Levie", "notification_email": { "email": "notifications@example.com", "is_confirmed": true }, "phone": 6509241374, "space_amount": 11345156112, "space_used": 1237009912, "status": "active", "timezone": "Africa/Bujumbura" } } ``` ## Item Source Object If an item triggers an event, the source object will be the [event source resource](e://resources/event-source). The below example contains a `classification` object. This does not appear if the item does not have a classification set. Also, it does not appear for every event type. ``` { "source": { "item_type": "file", "item_id": "8903212345", "item_name": "example.docx", "parent": { "type": "folder", "name": "All Files", "id": "0" }, "owned_by": { "type": "user", "id": "11446498", "name": "Aaron Levie", "login": "notifications@example.com" }, "classification": { "name": "Top Secret" } } } ``` **Reference:** https://developer.box.com/guides/events/event-triggers/event-source/ --- ## Untitled *Type: guide | Category: Events * Event Triggers Events appear in the event stream based on upon actions committed by users, items, or other resources. The response of the… # Event Triggers Events appear in the event stream based on upon actions committed by users, items, or other resources. The response of the [`GET /events`](e://get_events) endpoint changes based upon the event that triggered the event. **Reference:** https://developer.box.com/guides/events/event-triggers/ --- ## Untitled *Type: guide | Category: Events * Shield Information Barrier Events Information barrier prevents exchanges or communication that could lead to conflicts of interest or… # Shield Information Barrier Events Information barrier prevents exchanges or communication that could lead to conflicts of interest or potential legal issues. For example, admins can use information barrier to separate teams based on projects to prevent collaboration on content restricted to specific groups. ## Events triggered when information barrier is configured Configuring information barrier produces events in the [enterprise event](g://events/enterprise-events/for-enterprise/) stream. For example, activating or deactivating the barrier triggers an event. These events follow the standard event object schema with the `event_type` value set to one of the following: - `SHIELD_INFORMATION_BARRIER_ENABLED` - `SHIELD_INFORMATION_BARRIER_PENDING` - `SHIELD_INFORMATION_BARRIER_DISABLED` ### Shield information barrier enabled A `SHIELD_INFORMATION_BARRIER_ENABLED` event is triggered when the information barrier is enabled for a file or folder. For example: ``` { "chunk_size": 1, "next_stream_position": "1152923169537420243", "entries": [ { "source": { "barrier_id": 123456, "barrier_status": "ENABLED", "barrier_segments": [ { "name": "8", "member_count": 1 }, { "name": "9", "member_count": 1 } ] }, "created_by": { "type": "user", "id": "12345667", "name": "Unknown User", "login": "user@email.com" }, "action_by": null, "created_at": "2022-10-04T17:42:53-07:00", "event_id": "f82c3ba03e41f7e8a7608363cc6c0390183c3f83", "event_type": "SHIELD_INFORMATION_BARRIER_ENABLED", "ip_address": "Unknown IP", "type": "event", "session_id": null, "additional_details": null } ] } ``` ### Shield information barrier pending A `SHIELD_INFORMATION_BARRIER_PENDING` event is triggered when the information barrier is not yet enabled for a particular file or folder. The activation process was triggered, but the information barrier is not yet in place. For example: ``` { "chunk_size": 1, "next_stream_position": "1152923169531664551", "entries": [ { "source": { "barrier_id": 123456, "barrier_status": "PENDING", "barrier_segments": [ { "name": "8", "member_count": 1 }, { "name": "9", "member_count": 1 } ] }, "created_by": { "type": "user", "id": "12345667", "name": "Unknown User", "login": "user@email.com" }, "action_by": null, "created_at": "2022-10-04T16:06:57-07:00", "event_id": "f82c3ba03e41f7e8a7608363cc6c0390183c3f83", "event_type": "SHIELD_INFORMATION_BARRIER_PENDING", "ip_address": "Unknown IP", "type": "event", "session_id": null, "additional_details": null } ] } ``` ### Shield information barrier deactivated A `SHIELD_INFORMATION_BARRIER_DISABLED` event is triggered when the information barrier is deactivated for a particular file or folder. For example: ``` { "chunk_size": 1, "next_stream_position": "1152923169767928414", "entries": [ { "source": { "barrier_id": 1234567, "barrier_status": "DISABLED", "barrier_segments": [ { "name": "8", "member_count": 1 }, { "name": "9", "member_count": 1 } ] }, "created_by": { "type": "user", "id": "123435567", "name": "Unknown User", "login": "user@email.com" }, "action_by": null, "created_at": "2022-10-07T09:44:41-07:00", "event_id": "f82c3ba03e41f7e8a7608363cc6c0390183c3f83", "event_type": "SHIELD_INFORMATION_BARRIER_DISABLED", "ip_address": "Unknown IP", "type": "event", "session_id": null, "additional_details": null } ] } ``` ## Events triggered by restricted actions When the information barrier is set up, each user attempt to perform restricted actions or access restricted data also results in events. These events follow the standard event object schema with the `event_type` value set to one of the following: - `SHIELD_INFORMATION_BARRIER_GROUP_ADD_USER_BLOCKED` - `SHIELD_INFORMATION_BARRIER_COLLAB_BLOCKED` - `SHIELD_INFORMATION_BARRIER_ITEM_OWNER_TRANSFER_BLOCKED` - `SHIELD_INFORMATION_BARRIER_SHARED_ITEM_ACCESS_BLOCKED` - `SHIELD_INFORMATION_BARRIER_ITEM_MOVE_BLOCKED` - `SHIELD_INFORMATION_BARRIER_ITEM_COPY_BLOCKED` ### Adding user blocked A `SHIELD_INFORMATION_BARRIER_GROUP_ADD_USER_BLOCKED` event is triggered when the information barrier prohibits adding a user to a specific group. The `additional_details` payload provides details of the restricted groups. ``` { "source": { "type": "user", "id": "123456677", "name": "Unknown User", "login": "user@email.com" }, "created_by": { "type": "user", "id": "12345666", "name": "Unknown User", "login": "user@email.com" }, "action_by": null, "created_at": "2022-10-07T09:26:50-07:00", "event_id": "f82c3ba03e41f7e8a7608363cc6c0390183c3f83", "event_type": "SHIELD_INFORMATION_BARRIER_GROUP_ADD_USER_BLOCKED", "ip_address": "10.1.2.3", "type": "event", "session_id": null, "additional_details": { "group_id": "12345678", "group_name": "Support" } } ``` ### Collaboration blocked A `SHIELD_INFORMATION_BARRIER_COLLAB_BLOCKED` event is triggered when the information barrier prohibits adding collaborations for users that have restricted access to a file or folder. The `additional_details` payload provides details of the restricted collaboration. ``` { "source": { "folder_id": "12334556", "folder_name": "Contracts", "user_id": "1234567", "user_name": "Unknown User", "parent": { "type": "folder", "name": "All Files", "id": "0" }, "owned_by": { "type": "user", "id": "12345678", "name": "Unknown User", "login": "user@email.com" } }, "created_by": { "type": "user", "id": "16335351460", "name": "Unknown User", "login": "user@email.com" }, "action_by": null, "created_at": "2022-10-05T14:15:14-07:00", "event_id": "f82c3ba03e41f7e8a7608363cc6c0390183c3f83", "event_type": "SHIELD_INFORMATION_BARRIER_COLLAB_BLOCKED", "ip_address": "Unknown IP", "type": "event", "session_id": null, "additional_details": { "type": "box://event/additional_details/collaboration", "collab_id": "0", "is_performed_by_admin": false } } ``` ### Shared item access blocked A `SHIELD_INFORMATION_BARRIER_SHARED_ITEM_ACCESS_BLOCKED` event is triggered when the information barrier prohibits accessing a file or folder using the shared link. The `additional_details` payload provides details of the shared link and additional security information. ``` { "source": { "item_type": "folder", "item_id": "123456789", "item_name": "Contracts", "parent": { "type": "folder", "name": "All Files", "id": "0" }, "owned_by": { "type": "user", "id": "123456789", "name": "Unknown User", "login": "user@email.com" } }, "created_by": { "type": "user", "id": "123456789", "name": "Unknown User", "login": "user@email.com" }, "action_by": null, "created_at": "2022-10-06T13:27:58-07:00", "event_id": "f82c3ba03e41f7e8a7608363cc6c0390183c3f83", "event_type": "SHIELD_INFORMATION_BARRIER_SHARED_ITEM_ACCESS_BLOCKED", "ip_address": "Unknown IP", "type": "event", "session_id": null, "additional_details": { "shared_link_id": "abcdefghijklm", "security_information": { "accessFromSharedObject": { "sharedId": 123456789, "sharedName": "abcdefghijklmnop", "passwordSet": false, "accessLevel": "open", "createdAt": "2022-10-06T13:27:21-07:00" } } } } ``` ### Moving item blocked A `SHIELD_INFORMATION_BARRIER_ITEM_MOVE_BLOCKED` event is triggered when the information barrier prohibits moving an item (a file or a folder) to a a folder the user has no access to. The `additional_details` payload provides details of the folder. ``` { "source": { "item_type": "folder", "item_id": "123456789", "item_name": "Contracts", "parent": { "type": "folder", "name": "All Files", "id": "0" }, "owned_by": { "type": "user", "id": "123456789", "name": "Unknown User", "login": "user@email.com" } }, "created_by": { "type": "user", "id": "123456789", "name": "Unknown User", "login": "user@email.com" }, "action_by": null, "created_at": "2022-10-06T13:26:58-07:00", "event_id": "f82c3ba03e41f7e8a7608363cc6c0390183c3f83", "event_type": "SHIELD_INFORMATION_BARRIER_ITEM_MOVE_BLOCKED", "ip_address": "Unknown IP", "type": "event", "session_id": null, "additional_details": { "destination_folder": { "item_type": "folder", "item_id": "123456789", "item_name": "Contracts Signed" } } } ``` ### Copying item blocked A `SHIELD_INFORMATION_BARRIER_ITEM_COPY_BLOCKED` event is triggered when the information barrier prohibits copying an item (a file or a folder) to a a folder the user has no access to. The `additional_details` payload provides details of the destination folder. ``` { "source": { "item_type": "folder", "item_id": "123456789", "item_name": "Contracts", "parent": { "type": "folder", "name": "All Files", "id": "0" }, "owned_by": { "type": "user", "id": "123456789", "name": "Unknown User", "login": "user@email.com" } }, "created_by": { "type": "user", "id": "123456789", "name": "Unknown User", "login": "user@email.com" }, "action_by": null, "created_at": "2022-10-05T14:25:15-07:00", "event_id": "f82c3ba03e41f7e8a7608363cc6c0390183c3f83", "event_type": "SHIELD_INFORMATION_BARRIER_ITEM_COPY_BLOCKED", "ip_address": "Unknown IP", "type": "event", "session_id": null, "additional_details": { "destination_folder": { "item_type": "folder", "item_id": "123456789", "item_name": "Contracts Signed" } } } ``` ### Item transfer ownership blocked A `SHIELD_INFORMATION_BARRIER_ITEM_OWNER_TRANSFER_BLOCKED` event is triggered when the information barrier prohibits transferring the item ownership to a user that is subject to restrictions. The `additional_details` payload provides details of the user that cannot be set as the new owner. ``` { "source": { "item_type": "folder", "item_id": "", "item_name": "All Files", "parent": { "type": "folder", "name": "", "id": "" }, "owned_by": { "type": "user", "id": "123456789", "name": "Unknown User", "login": "user@email.com" } }, "created_by": { "type": "user", "id": "123456789", "name": "Unknown User", "login": "user@email.com" }, "action_by": null, "created_at": "2022-10-07T09:29:20-07:00", "event_id": "f82c3ba03e41f7e8a7608363cc6c0390183c3f83", "event_type": "SHIELD_INFORMATION_BARRIER_ITEM_OWNER_TRANSFER_BLOCKED", "ip_address": "10.1.2.3", "type": "event", "session_id": null, "additional_details": { "restricted_user": { "type": "user", "id": "123456789", "name": "Unknown User", "login": "user@email.com" }, "service_id": "123456789", "service_name": "App" } } ``` **Reference:** https://developer.box.com/guides/events/event-triggers/shield-information-barrier-events/ --- ## Untitled *Type: guide | Category: Events * Shield Smart Access Events Smart Access enables Box Admins to define and enforce classification-based access policies to control access and… # Shield Smart Access Events [Smart Access](https://support.box.com/hc/en-us/articles/7711416297747-About-Smart-Access) enables Box Admins to define and enforce classification-based access policies to control access and prevent the unintentional leakage of sensitive content. Smart Access policies can be configured in [enforced or monitoring mode](https://support.box.com/hc/en-us/articles/14596333776403-Shield-Access-Policy-Settings). In all event types, a field named `controlMode` appears to say whether the policy is in `enforced` or `monitoring` mode. ## Download and Print Restriction If an admin creates a Shield access policy that enforces download or print restriction and an end user is blocked from downloading or printing a file, an event is produced within the [enterprise event](g://events/enterprise-events/for-enterprise) stream. If the access policy is set to monitor potential download and print violations, events will also be generated when a user is viewing a folder with a file restricted from download or print, viewing a file in preview that is restricted from download or print, and when a user requests to download a file through the API that is restricted from download or print. These events follow the standard event object schema and the `event_type` value set to `SHIELD_DOWNLOAD_BLOCKED`. If downloading is blocked, the `additional-details` payload of the `SHIELD_DOWNLOAD_BLOCKED` event will provide the below details. For the Box Web App, the `additional_details` payload will provide the following details: ``` "additional_details": { "shield_download_enforcement": { "item": { "type": "file", "id": 987654321, "name": "testFile.docx", "file_version_id": 38495726173, "size": 370, "sha1": "db0a61e73b5e6985d190134e0a4b9982c716afeb" }, "access_user": { "type": "user", "id": 123456789, "name": "Some Name", "login": "somename@box.com" }, "service": null, "additional_info": "", "created_at": "2022-02-22T10:35:08-08:00", "classification": "Confidential", "controlMode": "enforced" } } ``` For the Box Desktop App, the `additional_details` payload will provide the following details: ``` "additional_details": { "shield_download_enforcement": { "item": { "type": "file", "id": 123456789, "name": "testFile.docx", "file_version_id": 987654321, "size": 11640, "sha1": "368acd076a89ce82e62cac004fa27ea9ce3019d7" }, "access_user": { "type": "user", "id": 123456789, "name": "Some Name", "login": "somename@box.com" }, "service": { "service": 254429, "name": "Box Drive" }, "additional_info": "", "created_at": "2022-02-22T10:38:58-08:00", "classification": "Confidential", "controlMode": "enforced" }, "service_id": "254429", "service_name": "Box Drive" } ``` For Box Mobile apps, the `additional_details` payload will provide the following details: ``` "additional_details": { "shield_download_enforcement": { "item": { "type": "file", "id": 987654321, "name": "testFile.docx", "file_version_id": 38495726173, "size": 11640, "sha1": "368acd076a89ce82e62cac004fa27ea9ce3019d7" }, "access_user": { "type": "user", "id": 123456789, "name": "Some Name", "login": "somename@box.com" }, "service": { "service": 4715, "name": "Box for Android" }, "additional_info": "", "created_at": "2022-01-18T14:51:37-08:00", "classification": "Confidential", "controlMode": "monitoring" }, "service_id": "4715", "service_name": "Box for Android" } ``` ## External collaboration restriction If an external collaboration invitation is restricted, an event is produced within the [enterprise event](g://events/enterprise-events/for-enterprise) stream. These events follow the standard event object schema, with the `event_type` value set to: `SHIELD_EXTERNAL_COLLAB_INVITE_BLOCKED_MISSING_JUSTIFICATION`, `SHIELD_EXTERNAL_COLLAB_INVITE_JUSTIFIED`, `SHIELD_EXTERNAL_COLLAB_INVITE_BLOCKED` `SHIELD_EXTERNAL_COLLAB_ACCESS_BLOCKED_MISSING_JUSTIFICATION`, or `SHIELD_EXTERNAL_COLLAB_ACCESS_BLOCKED`. If an external collaboration invitation is blocked, the `additional-details` payload of the `SHIELD_EXTERNAL_COLLAB_INVITE_BLOCKED` or `SHIELD_EXTERNAL_COLLAB_INVITE_BLOCKED_MISSING_JUSTIFICATION` event will provide the following details: ``` "additional_details": { "shield_external_collab_enforcement": { "item": { "type": "file", "id": 987654321, "name": "testFile.docx", "file_version_id": 987654321, "size": 11640, "sha1": "368acd076a89ce82e62cac004fa27ea9ce3019d7" }, "inviter": { "type": "user", "id": 123456789, "name": "Some Name", "login": "somename@box.com" }, "invitee": { "type": "user", "id": 123456789, "name": "Some Name", "login": "somename@box.com" }, "accessUser": null, "service": null, "additionalInfo": "", "createdAt": null, "justification": null, "classification": "Confidential", "controlMode": "enforced" } } ``` If an external collaboration invitation is justified, the `additional_details` payload of the `SHIELD_EXTERNAL_COLLAB_INVITE_JUSTIFIED` event will provide the following details: ``` "additional_details": { "shield_external_collab_enforcement": { "item": { "type": "file", "id": 123456789, "name": "testFile.docx", "file_version_id": 123456789, "size": 11640, "sha1": "368acd076a89ce82e62cac004fa27ea9ce3019d7" }, "inviter": { "type": "user", "id": 123456789, "name": "Some Name", "login": "somename@box.com" }, "invitee": { "type": "user", "id": 123456789, "name": "Some Name", "login": "somename@box.com" }, "accessUser": null, "service": null, "additionalInfo": "", "createdAt": null, "justification": { "justification_id": "17786127", "request_at": 1644874023, "requested_by": { "type": "user", "id": 123456789, "name": "Some Name", "login": "somename@box.com" }, "request_type": "EXTERNAL_COLLAB", "item": { "type": "file", "id": 987654321, "name": "testFile.docx", "file_version_id": 941051265322, "size": 11640, "sha1": "368acd076a89ce82e62cac004fa27ea9ce3019d7" }, "user": { "type": "user", "id": 123456789, "name": "Some Name", "login": "somename@box.com" }, "title": "Approved", "description": "", "additional_info": null, "approved_by": { "type": "user", "id": 123456789, "name": "Some Name", "login": "somename@box.com" }, "action": "APPROVED", "action_at": 1644874023, "details": null }, "classification": "Confidential", "controlMode": "enforced" } } ``` If external collaboration access is blocked, the `additional_details` payload of the `SHIELD_EXTERNAL_COLLAB_ACCESS_BLOCKED` or `SHIELD_EXTERNAL_COLLAB_ACCESS_BLOCKED_MISSING_JUSTIFICATION` event will provide the following details: ``` "additional_details": { "shield_external_collab_enforcement": { "item": { "type": "file", "id": 987654321, "name": "testFile.docx", "file_version_id": 987654321, "size": 11640, "sha1": "368acd076a89ce82e62cac004fa27ea9ce3019d7" }, "inviter": { "type": "user", "id": 123456789, "name": "Some Name", "login": "somename@box.com" }, "invitee": { "type": "user", "id": 123456789, "name": "Some Name", "login": "somename@box.com" }, "accessUser": null, "service": null, "additionalInfo": "", "createdAt": null, "justification": null, "classification": "Confidential", "controlMode": "enforced" } } ``` If a Shield justification is approved, an event is produced within the [enterprise event](g://events/enterprise-events/for-enterprise) stream. These events follow the standard event object schema and the `event_type` value set to `SHIELD_JUSTIFICATION_APPROVAL`. The `additional_details` payload will provide the following details: ``` "additional_details": { "shield_justification": { "justification_id": "18428718", "request_at": 1645556286, "requested_by": { "type": "user", "id": 123456789, "name": "Some Name", "login": "somename@box.com" }, "request_type": "EXTERNAL_COLLAB", "item": { "type": "file", "id": 987654321, "name": "testFile.docx", "file_version_id": 987654321, "size": 11640, "sha1": "368acd076a89ce82e62cac004fa27ea9ce3019d7" }, "user": { "type": "user", "id": 123456789, "name": "Some Name", "login": "somename@box.com" }, "title": "Partner Project", "description": "", "additional_info": null, "approved_by": { "type": "user", "id": 123456789, "name": "Some Name", "login": "somename@box.com" }, "action": "APPROVED", "action_at": 1645556286, "details": null } } ``` Admins, please note that you may see two enterprise events instead of one when a justification is chosen from the share modal. For example, one `SHIELD_EXTERNAL_COLLAB_INVITE_BLOCKED_MISSING_JUSTIFICATION` event and one `SHIELD_EXTERNAL_COLLAB_INVITE_JUSTIFIED` event. ## Integration Restriction If a 3rd-party application, including published platform applications with which your organization is integrated, is restricted from downloading a file or a folder, an event is produced within the [enterprise event](g://events/enterprise-events/for-enterprise) stream. These events follow the standard event object schema, with the `event_type` value set to `SHIELD_DOWNLOAD_BLOCKED`. For 3rd-party applications, the `additional_details` payload will provide the following details: ``` "additional_details": { "shield_download_enforcement": { "item": { "type": "file", "id": 875644956551, "name": "blaha.docx", "file_version_id": 941051265322, "size": 11640, "sha1": "368acd076a89ce82e62cac004fa27ea9ce3019d7" }, "access_user": { "type": "user", "id": 11754686560, "name": "Ming Feng", "login": "mfeng+demo@boxdemo.com" }, "service": "docusign", "additional_info": "", "created_at": "2022-01-18T14:53:53-08:00", "classification": "Confidential", "controlMode": "enforced" } ``` For platform applications, the `additional_details` payload will provide the following details: ``` "additional_details": { "shield_download_enforcement": { "item": { "type": "file", "id": 123456789, "name": "testFile.docx", "file_version_id": 987654321, "size": 11640, "sha1": "368acd076a89ce82e62cac004fa27ea9ce3019d7" }, "access_user": { "type": "user", "id": 123456789, "name": "Some Name", "login": "somename@box.com" }, "service": { "service": 123456, "name": "CustomApp" }, "additional_info": "", "created_at": "2022-01-18T13:31:25-08:00", "classification": "Confidential", "controlMode": "enforced" }, "service_id": "123456", "service_name": "CustomApp" } ``` ## FTP Restriction If download of a file or folder is restricted via the FTP protocol, an event is produced within the [enterprise event](g://events/enterprise-events/for-enterprise) stream. These events follow the standard event object schema, with the `event_type` value set to `SHIELD_DOWNLOAD_BLOCKED`. The `additional_details` payload will provide the following details: ``` "additional_details": { "shield_download_enforcement": { "item": { "type": "file", "id": 123456789, "name": "textFile.txt", "file_version_id": 987654321, "size": 3606, "sha1": "ab7a79ff8e2a6b576e1c62d850290a09312fb387" }, "access_user": { "type": "user", "id": 123456789, "name": "Some Name", "login": "somename@box.com" }, "service": { "service": 4082, "name": "Box FTP Server" }, "additional_info": "", "created_at": "2022-01-18T14:19:51-08:00", "classification": null, "controlMode": "enforced" }, "service_id": "4082", "service_name": "Box FTP Server" } ``` **Reference:** https://developer.box.com/guides/events/event-triggers/shield-smart-access-events/ --- ## Untitled *Type: guide | Category: Events * Sign Events Audit Box Sign events using the enterprise events stream. To learn more about Box Sign, visit our guide. The status provided… # Sign Events Audit Box Sign events using the enterprise events stream. To learn more about Box Sign, visit our [guide](g://box-sign). The status provided below in each `additional_details` payload may differ from the example based on the specific sign request details. For example, if the requester is the only signer, the status of the `SIGN_DOCUMENT_CREATED` event will immediately be `viewed`. ## Document Events ### Created A `SIGN_DOCUMENT_CREATED` `event_type` is produced when a sign request is created via API or UI. At this stage the sign request is not sent to signers yet. The`additional_details` payload will provide the following details: ``` "additional_details": { "sign_request": { "sign_request_id": "123e4567-e89b-12d3-a456-426614174000", "sign_request_short_id": "426614174000", "status": "sent", "signer_ip_address": null, "requestor_ip_address": "", "files": [ { "id": "1234567890", "type": "file", "name": "example_doc.pdf", "parent": { "id": "987654321", "type": "folder" } } ], "requestor": { "id": "13579246", "type": "user", "name": "John Doe", "login": "johndoe@box.com" }, "signer": null, "template": { "id": "987abC5423", "template_type": "Signing", "name": "Work Contact" }, "batch_send": { "id": "W23YVL46" }, "redirection": { "redirect_url": "https://www.google.com", "declined_redirect_url": "https://www.googledecline.com" }, "sender_message": { "subject": "Can you please sign this document?", "message": "This document shows the terms agreed to on the phone." }, "forward": null } } ``` ### Converted A `SIGN_DOCUMENT_CONVERTED` `event_type` is produced when a sign request is converted to a `.pdf` for signing. The`additional_details` payload will provide the following details: ``` "additional_details": { "sign_request": { "sign_request_id": "123e4567-e89b-12d3-a456-426614174000", "sign_request_short_id": "426614174000", "status": "created", "signer_ip_address": null, "requestor_ip_address": "", "files": [ { "id": "1234567890", "type": "file", "name": "example_doc.pdf", "parent": { "id": "987654321", "type": "folder" } } ], "expires": null, "requestor": { "id": "13579246", "type": "user", "name": "John Doe", "login": "johndoe@box.com" }, "signer": null, "template": null, "batch_send": null, "redirection": { "redirect_url": "https://www.google.com", "declined_redirect_url": "https://www.googledecline.com" }, "sender_message": { "subject": "", "message": "" }, "forward": null } } ``` ### Completed A `SIGN_DOCUMENT_COMPLETED` `event_type` is produced when all signers have successfully signed the document. The`additional_details` payload will provide the following details: ``` "additional_details": { "sign_request": { "sign_request_id": "123e4567-e89b-12d3-a456-426614174000", "sign_request_short_id": "426614174000", "status": "signed", "signer_ip_address": null, "requestor_ip_address": "", "files": [ { "id": "1234567890", "type": "file", "name": "example_doc.pdf", "parent": { "id": "987654321", "type": "folder" } } ], "expires": null, "requestor": { "id": "13579246", "type": "user", "name": "John Doe", "login": "johndoe@box.com" }, "signer": null, "template": null, "batch_send": null, "redirection": { "redirect_url": "https://www.google.com", "declined_redirect_url": "https://www.googledecline.com" }, "sender_message": { "subject": "", "message": "" }, "forward": null } } ``` ### Cancelled A `SIGN_DOCUMENT_CANCELLED` `event_type` is produced when a sign request is cancelled by the requester via API or UI. The`additional_details` payload will provide the following details: ``` "additional_details": { "sign_request": { "sign_request_id": "123e4567-e89b-12d3-a456-426614174000", "sign_request_short_id": "426614174000", "status": "cancelled", "signer_ip_address": null, "requestor_ip_address": "", "files": [ { "id": "1234567890", "type": "file", "name": "example_doc.pdf", "parent": { "id": "987654321", "type": "folder" } } ], "expires": null, "requestor": { "id": "13579246", "type": "user", "name": "John Doe", "login": "johndoe@box.com" }, "signer": null, "template": null, "batch_send": null, "redirection": { "redirect_url": "https://www.google.com", "declined_redirect_url": "https://www.googledecline.com" }, "sender_message": { "subject": "", "message": "" }, "forward": null } } ``` ### Expired A `SIGN_DOCUMENT_EXPIRED` `event_type` is produced when a sign request expired with incomplete signatures. The`additional_details` payload will provide the following details: ``` "additional_details": { "sign_request": { "sign_request_id": "123e4567-e89b-12d3-a456-426614174000", "sign_request_short_id": "426614174000", "status": "expired", "signer_ip_address": null, "requestor_ip_address": "", "files": [ { "id": "1234567890", "type": "file", "name": "example_doc.pdf", "parent": { "id": "987654321", "type": "folder" } } ], "expires": null, "requestor": { "id": "13579246", "type": "user", "name": "John Doe", "login": "johndoe@box.com" }, "signer": null, "template": null, "batch_send": null, "redirection": { "redirect_url": "https://www.google.com", "declined_redirect_url": "https://www.googledecline.com" }, "sender_message": { "subject": "", "message": "" }, "forward": null } } ``` ## Signer Events ### Assigned A `SIGN_DOCUMENT_ASSIGNED` `event_type` is produced when a sign request is successfully sent to a signer. The`additional_details` payload will provide the following details: ``` "additional_details": { "sign_request": { "sign_request_id": "123e4567-e89b-12d3-a456-426614174000", "sign_request_short_id": "426614174000", "status": "sent", "signer_ip_address": "", "requestor_ip_address": "", "files": [ { "id": "1234567890", "type": "file", "name": "example_doc.pdf", "parent": { "id": "987654321", "type": "folder" } } ], "expires": null, "requestor": { "id": "13579246", "type": "user", "name": "John Doe", "login": "johndoe@box.com" }, "signer": { "id": "246813579", "type": "user", "name": "Jane Doe", "login": "janedoe@example.com" }, "template": null, "batch_send": null, "redirection": { "redirect_url": "https://www.google.com", "declined_redirect_url": "https://www.googledecline.com" }, "ready_sign_link": { "id": "aaae45bb-e89b-12d3-a456-426614174000" }, "sender_message": { "subject": "", "message": "" }, "forward": null } } ``` ### Viewed by Signer A `SIGN_DOCUMENT_VIEWED_BY_SIGNER` `event_type` is produced when a sign request signer clicks on **Review Document** in the signing email or visits the signing URL. The`additional_details` payload will provide the following details: ``` "additional_details": { "sign_request": { "sign_request_id": "123e4567-e89b-12d3-a456-426614174000", "sign_request_short_id": "426614174000", "status": "viewed", "signer_ip_address": "", "requestor_ip_address": "", "files": [ { "id": "1234567890", "type": "file", "name": "example_doc.pdf", "parent": { "id": "987654321", "type": "folder" } } ], "expires": null, "requestor": { "id": "13579246", "type": "user", "name": "John Doe", "login": "johndoe@box.com" }, "signer": { "id": "246813579", "type": "user", "name": "Jane Doe", "login": "janedoe@example.com" }, "template": null, "batch_send": null, "redirection": { "redirect_url": "https://www.google.com", "declined_redirect_url": "https://www.googledecline.com" }, "ready_sign_link": { "id": "aaae45bb-e89b-12d3-a456-426614174000" }, "sender_message": { "subject": "", "message": "" }, "forward": null } } ``` ### Downloaded A `SIGNER_DOWNLOADED` `event_type` is produced when a signer downloads the signing document. ``` "additional_details": { "sign_request": { "sign_request_id": "123e4567-e89b-12d3-a456-426614174000", "sign_request_short_id": "426614174000", "status": "viewed", "signer_ip_address": "", "requestor_ip_address": "", "files": [ { "id": "1234567890", "type": "file", "name": "example_doc.pdf", "parent": { "id": "987654321", "type": "folder" } } ], "expires": null, "requestor": { "id": "13579246", "type": "user", "name": "John Doe", "login": "johndoe@box.com" }, "signer": { "id": "246813579", "type": "user", "name": "Jane Doe", "login": "janedoe@example.com" }, "template": null, "batch_send": null, "redirection": { "redirect_url": "https://www.google.com", "declined_redirect_url": "https://www.googledecline.com" }, "ready_sign_link": { "id": "aaae45bb-e89b-12d3-a456-426614174000" }, "sender_message": { "subject": "", "message": "" }, "forward": { "forwarded_to_email": "janedoe@box.com", "forwarded_reason": "Please sign", "forwarded_at": "2022-03-03T12:04:20-10:00" } } } ``` ### Forwarded A `SIGNER_FORWARDED` `event_type` is produced when a signer downloads the signing document. ``` "additional_details": { "sign_request": { "sign_request_id": "123e4567-e89b-12d3-a456-426614174000", "sign_request_short_id": "426614174000", "status": "viewed", "signer_ip_address": "", "requestor_ip_address": "", "files": [ { "id": "1234567890", "type": "file", "name": "example_doc.pdf", "parent": { "id": "987654321", "type": "folder" } } ], "expires": null, "requestor": { "id": "13579246", "type": "user", "name": "John Doe", "login": "johndoe@box.com" }, "signer": { "id": "246813579", "type": "user", "name": "Jane Doe", "login": "janedoe@example.com" }, "template": null, "batch_send": null, "redirection": { "redirect_url": "https://www.google.com", "declined_redirect_url": "https://www.googledecline.com" }, "ready_sign_link": { "id": "aaae45bb-e89b-12d3-a456-426614174000" }, "sender_message": { "subject": "Can you please sign this document?", "message": "This document shows the terms agreed to on the phone." }, "forward": { "forwarded_to_email": "somename@box.com", "forwarded_reason": "I need to forward to my business partner.", "forwarded_at": "2022-02-03T10:04:52-08:00", }, } } ``` ### Signed A `SIGN_DOCUMENT_SIGNED` `event_type` is produced when a signer completes the sign request. The`additional_details` payload will provide the following details: ``` "additional_details": { "sign_request": { "sign_request_id": "123e4567-e89b-12d3-a456-426614174000", "sign_request_short_id": "426614174000", "status": "signed", "signer_ip_address": "", "requestor_ip_address": "", "files": [ { "id": "1234567890", "type": "file", "name": "example_doc.pdf", "parent": { "id": "987654321", "type": "folder" } } ], "expires": null, "requestor": { "id": "13579246", "type": "user", "name": "John Doe", "login": "johndoe@box.com" }, "signer": { "id": "246813579", "type": "user", "name": "Jane Doe", "login": "janedoe@example.com" }, "template": null, "batch_send": null, "redirection": { "redirect_url": "https://www.google.com", "declined_redirect_url": "https://www.googledecline.com" }, "ready_sign_link": { "id": "aaae45bb-e89b-12d3-a456-426614174000" }, "sender_message": { "subject": "Can you please sign this document?", "message": "This document shows the terms agreed to on the phone." }, "forward": null } } ``` ### Declined A `SIGN_DOCUMENT_DECLINED` `event_type` is produced when a sign request signer declines the request. The`additional_details` payload will provide the following details: ``` "additional_details": { "sign_request": { "sign_request_id": "123e4567-e89b-12d3-a456-426614174000", "sign_request_short_id": "426614174000", "status": "declined", "signer_ip_address": "", "requestor_ip_address": "", "files": [ { "id": "1234567890", "type": "file", "name": "example_doc.pdf", "parent": { "id": "987654321", "type": "folder" } } ], "expires": null, "requestor": { "id": "13579246", "type": "user", "name": "John Doe", "login": "johndoe@box.com" }, "signer": { "id": "246813579", "type": "user", "name": "Jane Doe", "login": "janedoe@example.com" }, "template": null, "batch_send": null, "redirection": { "redirect_url": "https://www.google.com", "declined_redirect_url": "https://www.googledecline.com" }, "ready_sign_link": { "id": "aaae45bb-e89b-12d3-a456-426614174000" }, "sender_message": { "subject": "", "message": "" }, "forward": null } } ``` **Reference:** https://developer.box.com/guides/events/event-triggers/sign-events/ --- ## Untitled *Type: guide | Category: Events * Event Stream Parameters The GET /events endpoint can take in several parameters. stream_type and stream_position are described in more… # Event Stream Parameters The [`GET /events`](e://get_events) endpoint can take in several parameters. `stream_type` and `stream_position` are described in more detail in this section's guides. **Reference:** https://developer.box.com/guides/events/parameters/ --- ## Untitled *Type: guide | Category: Events * Stream Types Stream Types Scope Purpose Description Retention Access Pattern admin_logs A single enterprise (for authorized admins… # Stream Types | Stream Types | Scope | Purpose | Description | Retention | Access Pattern | | --- | --- | --- | --- | --- | --- | | admin_logs | A single enterprise (for authorized admins) | Historical Queries | Enables querying historical events up to one year | 365 Days | Filter by time frame, then paginate through the response by stream_position | | admin_logs_streaming | A single enterprise (for authorized admins) | Near real time subscriptions | Enables subscribing to live events in near real time | 14 Days | Poll using the stream_position | | all (default) | A single user (for any user) | Near real time subscriptions | Returns everything for a user | 21 Days | Poll or long-poll using the stream_position | | changes | A single user (for any user) | Near real time subscriptions | Returns events that may cause file tree changes such as file updates or collaborations | 21 Days | Poll or long-poll using the stream_position | | sync | A single user (for any user) | Near real time subscriptions | Is similar to changes but only applies to synced folders | 31 Days | Poll or long-poll using the stream_position | **Reference:** https://developer.box.com/guides/events/parameters/stream-types/ --- ## Untitled *Type: guide | Category: Events * Stream Position Pagination Paginating the event stream works through the use of the stream_position parameter. First, send a request to the… # Stream Position Pagination Paginating the event stream works through the use of the `stream_position` parameter. First, send a request to the [`GET /events`](e://get_events) API without a `stream_position` query parameter. ``` curl https://api.box.com/2.0/events \ -H "authorization: Bearer ACCESS_TOKEN" ``` The API will return all available events beginning with the oldest. The response will also include a `next_stream_position` value that can be used to make the next API call for the next place in the stream. ``` curl https://api.box.com/2.0/events?stream_position=388720462721 \ -H "authorization: Bearer ACCESS_TOKEN" ``` The `stream_position` can also be set to `now` to return the most recent stream position. ``` curl https://api.box.com/2.0/events?stream_position=now \ -H "authorization: Bearer ACCESS_TOKEN" ``` In this case, the API returns an empty list and a `next_stream_position` that can be used for the next call. **Reference:** https://developer.box.com/guides/events/parameters/pagination/ --- ## Untitled *Type: guide | Category: Events * User Events User Events provides a low latency stream of events relevant to the currently authenticated user. This event stream is how Box… # User Events User Events provides a low latency stream of events relevant to the currently authenticated user. This event stream is how Box keeps Box Drive up to date, but is also made available for developer use. **Reference:** https://developer.box.com/guides/events/user-events/ --- ## Untitled *Type: guide | Category: Events * Get User Events To get a user's events, authenticate as any user and make a call to the GET /events API. The events returned will only be… # Get User Events To get a user's events, authenticate as any user and make a call to the [`GET /events`](e://get_events) API. The events returned will only be for the user who's access token the API was made with. To get the event feed for a different user either use the `as-user` header or an actual access token for that user. ## Stream Types The user event stream support 3 types of stream. | Stream Type | | | --- | --- | | all | Returns everything for a user (default) | | changes | Returns events that may cause file tree changes such as file updates or collaborations. | | sync | Is similar to changes but only applies to synced folders | ## Anonymous Users In some cases, the event feed might list a user with an ID of `2`. This is Box's internal identifier for anonymous users. An anonymous user is a user that is not logged in. This can happen any time a user interacts with content and they aren't asked to log in first. An example would be when a user downloads a file through an open shared link. ## Limitations Box does not store events indefinitely. User events are stored for between two weeks and two months, after which the user events are removed. Enterprise events are accessible for one year via the API and seven years via exported reports in the Box Admin Console. The emphasis of this feed is to return the complete results quickly, which means that Box may return events more than once or out of order. Duplicate events can be identified by their event IDs. ## Long Polling The user event stream supports long-polling [through the `OPTIONS /events` API](g://events/user-events/polling). ## Event Types The following events can be triggered for a user. This list is not exhaustive, so it is possible events appear that are not listed. The following events are available in all feeds. | Event name | Description | | --- | --- | | ITEM_CREATE | A folder or File was created | | ITEM_UPLOAD | A folder or File was uploaded | | ITEM_MOVE | A file or folder was moved | | ITEM_COPY | A file or folder was copied | | LOCK_CREATE | A file was locked | | LOCK_DESTROY | A file was unlocked. If a locked file is deleted, the source file will be null. | | ITEM_TRASH | A file or folder was marked as deleted | | ITEM_UNDELETE_VIA_TRASH | A file or folder was recovered out of the trash | | COLLAB_ADD_COLLABORATOR | A collaborator was added to a folder | | COLLAB_ROLE_CHANGE | A collaborator had their role changed | | COLLAB_INVITE_COLLABORATOR | A collaborator was invited on a folder | | COLLAB_REMOVE_COLLABORATOR | A collaborator was removed from a folder | | ITEM_SYNC | A folder was marked for sync | | ITEM_UNSYNC | A folder was unmarked for sync | | ITEM_RENAME | A file or folder was renamed | | ITEM_MAKE_CURRENT_VERSION | A previous version of a file was promoted to the current version | | GROUP_ADD_USER | Added user to group | | GROUP_REMOVE_USER | Removed user from group | The following events are only available in the `all` feed. | Event name | Description | | --- | --- | | COMMENT_CREATE | A comment was created on a folder, file, or other comment | | COMMENT_DELETE | A comment was deleted on folder, file, or other comment | | ITEM_DOWNLOAD | A file or folder was downloaded | | ITEM_PREVIEW | A file was previewed | | TASK_ASSIGNMENT_CREATE | A task was assigned | | TASK_CREATE | A task was created | | ITEM_SHARED_CREATE | A file or folder was enabled for sharing | | ITEM_SHARED_UNSHARE | A file or folder was disabled for sharing | | ITEM_SHARED | A folder was shared | | TAG_ITEM_CREATE | A Tag was added to a file or folder | | ENABLE_TWO_FACTOR_AUTH | 2 factor authentication enabled by user | | MASTER_INVITE_ACCEPT | Free user accepts invitation to become a managed user | | MASTER_INVITE_REJECT | Free user rejects invitation to become a managed user | | ACCESS_GRANTED | Granted Box access to account | | ACCESS_REVOKED | Revoke Box access to account | ## Event notifications To reduce the noise of the event stream and optimize event consumption, the event types listed in the table trigger a limited number of notifications. | Event type | Notification behavior | | --- | --- | | COLLAB_ADD_COLLABORATOR, COLLAB_REMOVE_COLLABORATOR, COLLAB_INVITE_COLLABORATOR, COLLAB_ROLE_CHANGE | When these events take place, the content owner on the changes event stream is notified. Collaborators see an additional event that matches what they see on the all stream. | | ITEM_DOWNLOAD | When an item is downloaded, only the content owner gets the notification. Collaborators are not notified. | | ITEM_PREVIEW | When an item is previewed, only the content owner gets the notification. Collaborators are not notified. | **Reference:** https://developer.box.com/guides/events/user-events/for-user/ --- ## Untitled *Type: guide | Category: Events * Long-Poll Events To get real-time notification of activity in a Box account you can use the long poll feature of the OPTIONS /events API… # Long-Poll Events To get real-time notification of activity in a Box account you can use the long poll feature of the [`OPTIONS /events`](e://options_events) API. Long polling is only available for user events. Enterprise events do not support long polling. ## Long Polling Long polling involves opening an HTTP request and keeping it open until the server sends a response, then repeating the process over and over to receive updated responses. The SDKs have built-in support for turning the event feeds into an event stream by long polling for new events. ### Long Poll URL To use long polling, first send an request to the [`OPTIONS /events`](e://options_events) API to retrieve the long poll URL. ``` curl -X OPTIONS https://api.box.com/2.0/events \ -H "authorization: Bearer ACCESS_TOKEN" ``` ``` { "chunk_size": 1, "entries": [ { "type": "realtime_server", "url": "http://2.realtime.services.box.net/subscribe?channel=cc807c9c4869ffb1c81a&stream_type=all", "ttl": 10, "max_retries": 10, "retry_timeout": 610 } ] } ``` ### Real-Time Servers Next, make a `GET` request to the provided URL to begin listening for events. If an event occurs in an account that is being monitored the application will receive a response with the value `new_change`. The response contains no other details. This single response serves as a prompt to take further action such as sending a request to the `GET /events` endpoint with the last known `stream_position`. ### Disconnect & Reconnect After the server sends this response it closes the connection. The application must now repeat the long poll process to begin listening for events again. If no events occur for a while after the application connects to the real-time server the connection will close with a `reconnect` value. When this happens the application should make a new call to `OPTIONS /events` to restart the process. ### Timeouts & Retries If the application receive no events in `retry_timeout` seconds then the application can reconnect to the real-time server. This might be necessary in due to network errors. If the application receive a `max_retries` error when making a `GET` request to the real-time server then it must restart the process by making an `OPTIONS` call to the `/events` API. **Reference:** https://developer.box.com/guides/events/user-events/polling/ --- ## Untitled *Type: guide | Category: Embed Box * Best Practices Rotate App Tokens See our guide on rotating App Tokens. Use Downscope Tokens App Tokens are a set of two tokens (Primary and… # Best Practices ## Rotate App Tokens See our guide on [rotating App Tokens](g://authentication/app-token/rollover). ## Use Downscope Tokens App Tokens are a set of two tokens (Primary and Secondary) that hold elevated privileges over the content managed by your application. App Tokens can be used by your application to upload, download, preview and modify any Box content. We strongly recommend that you use a [downscoped](g://authentication/tokens/downscope) version of the original App Token if you need to make the token available on the client side for any reason. This is because of the elevated privileges granted to these tokens, if you need. An example of when this would be needed is if you are using the Box Preview UI Element. **Reference:** https://developer.box.com/guides/embed/box-view/best-practices/ --- ## Untitled *Type: guide | Category: Embed Box * Create File Preview Once a file is uploaded to an App Token application, it can be previewed using two different methods: Direct embed: A… # Create File Preview Once a file is uploaded to an App Token application, it can be previewed using two different methods: - Direct embed: A standard HTML `<iframe>` component with a custom embed link. - Customized previewer: A fully customized preview widget using Box [UI Elements](g://embed/ui-elements). ## Direct Embed (iframe) The direct embed link provides limited options to customize the preview experience in your application. Due to the light-weight nature of the method, the API doesn't give you options to respond to client-side actions such as scrolling in the case of documents, play/pause interactivity for videos, rotating an image, etc. There are two steps to create a direct `<iframe>` embed for Box View: 1. Generate an embed URL for the file 2. Add the embed URL to an `<iframe>` ### Generate an embed URL for the file To create a public file preview URL for a file that was uploaded to the App Token application, you may either use a direct SDK method or make the request directly to the APIs. When generating the embed URL directly from the APIs, use the [Get File Information endpoint](e://get_files_id) and request `expiring_embed_link` via the `fields` parameter. ``` curl https://api.box.com/2.0/files/FILE_ID?fields=expiring_embed_link \ -H "authorization: Bearer [APP_TOKEN]" ``` ``` String fileId = "12345678"; Uri embedUri = await client.FilesManager.GetPreviewLinkAsync(id: fileId); ``` ``` String fileID = "12345678"; BoxFile file = new BoxFile(api, fileID); URL embedLink = file.getPreviewLink(); ``` ``` file_id = '12345678' embed_url = client.file(file_id).get_embed_url() ``` ``` const fileId = '12345678'; client.files.getEmbedLink(fileId).then(embedURL => { // ... }); ``` Within the response object will be a public embed URL like the following: ``` https://app.box.com/preview/expiring_embed/gvoct6FE!YT_X1LauQ8ulDTad96hTl9xLCRYJ 5iU6O61KxiduxFIgX9HSWMcCWf7zju1XkEsf6-Ul2qtKXeaFeKPT4SysQJQdxrc144KgTIBuoI3bWMf4 cfhp3jdLYrK5hnr6KMq5H6r-AW31AcFtDJi1lnT0M4b3bvvZUaE2RRJGGINMauvS6MAT2luae5PvbFSx Ctqqx6XlN6QrqbhfJc0UeJF9qwMv3-O8q5fWn0qr8OTY4lkeYidtTs3Ux... ``` For security reasons, the generated embed link will expire after 1 minute and should be immediately embedded in the app once generated. ### Add the embed URL to an <iframe> Within the HTML of your application, create an `iframe` elements with the `src` attribute set to generated embed URL. ``` <iframe src="https://app.box.com/preview/expiring_embed/gvoct6FE!ixgtCKQAziW 9tHPm-jueq4cmS4GnL9RTJRcVEsK_3W8xcxtVo_v6gKpoXY45odgG1QrcjBVYZMrriUyGvcoSM SX8s-smpaFFYQik0R-PCKFtwvbv0lonid6ZfYNbuNFl2j9hOIqBccvHrdVor7i6WvOm6zELzTY 4EWshcyYYBhDbJmYMrq61RtU_kvBe5P..."></iframe> ``` ## Customized Previewer (UI Elements) To leverage advanced preview customization and event handling capabilities, use the Box [UI Preview Element](guide://embed/ui-elements/preview). To set up the Preview Element, start by installing the required components. Install Box Elements and Preview The basic code will resemble the below when adding the JavaScript code to display a new previewer. ``` var preview = new Box.Preview(); preview.show("FILE_ID", "ACCESS_TOKEN", { container: ".preview-container", showDownload: true }); ``` Replace the placeholders in the code sample with the following: - `FILE_ID`: The ID of the file uploaded to the App Token application, which can be obtained from the object returned when uploading the file. - `ACCESS_TOKEN`: The primary Access Token set up when configuring the application or a downscoped version of the token. Due to the elevated privileges of the primary access token it's highly recommended that you use a downscoped version of the token in the Javascript code. See [best practices for downscoping](guide://embed/box-view/best-practices#use-downscoped-tokens). **Reference:** https://developer.box.com/guides/embed/box-view/create-preview/ --- ## Untitled *Type: guide | Category: Embed Box * Setup Before uploading files to Box and previewing them using Box View, a Box application must be created and an access token be generated… # Setup Before uploading files to Box and previewing them using Box View, a Box application must be created and an access token be generated for that application. ## Create App Token App Box View uses an authentication mechanism called **App Token Auth** to store files directly within the account of the application as opposed to a specific Box user. This permits the previewing of files without having to associate the content with a user. The first step is to create the application and authorize it through your admin to begin making API requests to Box. Setup and authorize App Token app App Token apps need to be authorized by the Box admin, otherwise you will receive permission errors when making API requests. To go through app authorization, follow [this guide](guide://authorization/custom-app-approval). ## Generate Access Token With your application loaded, click on the **Configuration** option in the left navigation menu. The configuration page for your application should look like the following. Click on the **Generate Key** button within the **Primary Access Token** section. If you don't have 2FA setup for your developer account, you will be prompted to set up 2FA before you can successfully generate App Tokens. Follow the prompt to set up 2FA. Choose the expiration time for your token, either 30 days, 60 days, or set the token to never expire. Once the app token is generated, copy and store it securely. It won’t be displayed once the page is reloaded since Box stores a one-way hash of the tokens instead of the actual token and cannot retrieve the original token again. **Reference:** https://developer.box.com/guides/embed/box-view/setup/ --- ## Untitled *Type: guide | Category: Embed Box * FAQ How does Box View work? To get started with the New Box View, follow our guide here. Which Preview method, Embed Link or UI Element, is… # FAQ **How does Box View work?** To get started with the New Box View, follow our guide [here](guide://embed/box-view/setup). **Which Preview method, Embed Link or UI Element, is right for me?** Please follow our guide [here](guide://embed/box-view/create-preview) to choose the best method for your use case. **What file types are supported by Box View?** Supported [file types](https://support.box.com/hc/en-us/articles/360043695794-Viewing-Different-File-Types-Supported-in-Box-Content-Preview) can be found in our support article. **Which file types are not supported on mobile with Box View?** - All documents supported on web preview are supported on mobile browsers (Safari for iOS and Chrome). - Full annotations support is available for mobile via the [Content Preview UI Element](guide://embed/ui-elements/preview), which leverages Box Annotations. - Mobile SDKs (for iOS and Android) do not support 360 Videos/Images, and 3D. - Mobile SDKs (for iOS and Android) do not support annotations (both read and write). **What are annotations?** [Annotations](g://embed/ui-elements/annotations) are markup notes on a file rendering and allow developers to provide collaboration capabilities right from within the embedded Box preview in their application. **After uploading a file, how can my application get file representations?** [Box Representations](guide://representations) let you get the digital assets created for files stored in Box. You can use these endpoints to get PDF, text, image, and thumbnail representations for a file. **Can I use Box View with storage providers other than Box?** Currently, Box View is only compatible with files that are stored in Box. You can delete the files from Box once you no longer need to display them. However, you would need to re-upload them again in order to generate the preview. For this reason, we recommend keeping the files stored in Box for at least as long as you want to be able to display them. **How do I fix the CORS error Box gives me when I embed a Box UI Element?** To fix the [CORS](g://security/cors) error, add each domain you wish to allow to make CORS requests via the application's configuration page. Wildcards are supported for the subdomain (`https://*.domain.com`). See the [CORS guide](g://security/cors) for more information. **How can I replace the Box logo that shows up on preview?** See this guide for information on customizing the logo within a [Preview UI Element](g://embed/ui-elements/logo). **Reference:** https://developer.box.com/guides/embed/box-view/faq/ --- ## Untitled *Type: guide | Category: Embed Box * Box View Box View is an embeddable service that allows developers to upload, convert, and display files in their web and mobile apps via a… # Box View Box View is an embeddable service that allows developers to upload, convert, and display files in their web and mobile apps via a high-fidelity, interactive file viewer. ## Features ### View any file Embed documents, images, videos, 360-degree videos and images, 3D models, and dozens of other files in any web or mobile app using a standard `<iframe>`. ### Ease of use Upload files to Box via a secure API and receive an embeddable HTML5 snippet to place into your application's UI. ### Collaboration in context Allow end users to collaborate and mark up documents, presentations, and images with annotations. Users can highlight text or comment on specific areas of a file rendering. ## How does Box View work 1. Upload files into Box using our [secure upload API](e://post-files-content). All content is stored in Box's secure cloud storage infrastructure with virus scanning and 256 bit encryption. 2. Upon upload, files are converted into HTML5 compatible assets, which are designed to render crisply and responsively. 3. An embeddable URL is requested to access the file rendering. Previews can be embedded directly in your application using an `<iframe>` URL. ## Customize your experience with Box Content Preview [Box Content Preview](g://embed/ui-elements/preview) may be used to create customized client-side experiences with the converted files. ## Use cases - Convert and display PDF resumes in a recruiter application - Convert and display course materials, including documents, videos, and PDFs in a student portal - Convert and display HD video in a media room - Convert and display sales collateral, including documents, videos, PDFs, and 3D models in a field sales enablement application **Reference:** https://developer.box.com/guides/embed/box-view/ --- ## Untitled *Type: guide | Category: Embed Box * Upload Files To generate a preview embed link for a file, the file must be uploaded to Box. For most file types, conversion is triggered… # Upload Files To generate a preview embed link for a file, the file must be uploaded to Box. For most file types, conversion is triggered automatically upon upload. However, for video and 3D files, it is triggered upon first preview. No explicit action is required by the user to begin conversion. In either case, conversion is triggered only once per file, and the generated assets are available for as long as the original file is stored in Box. ## Uploading a File To upload a file using the [Box SDKs](pages://sdks-and-tools) or directly with the APIs, you need to use the client ID and App Token that are generated during [application setup](guide://embed/box-view/setup) to authenticate your application. Authenticate your application Once an authenticated client has been created, it may be used to upload files directly to the App Token application for conversion. Upload files to Box **Reference:** https://developer.box.com/guides/embed/box-view/upload-file/ --- ## Untitled *Type: guide | Category: Embed Box * Content Explorer - metadata view With Content Explorer you can also display files based on their metadata. The metadata view uses metadata… # Content Explorer - metadata view With Content Explorer you can also display files based on their metadata. The metadata view uses [metadata template](r://get-metadata-templates-id) and [metadata query](g://metadata/queries) to find the data you want to display. ## Prerequisites - Read the [Content Explorer](g:///embed/ui-elements/explorer) guide. - Check the [metadata terminology](g://metadata/#metadata-terminology). - Check the information on [metadata queries](g://metadata/queries). ## Create and configure an app 1. [Create a Box app](g:///applications/app-types). Add the local development address in the CORS Domains: 2. Generate a [developer token](g://authentication/tokens/developer-tokens). ## Create a metadata template The next step is to create a metadata template. 1. Use [Metadata API](g:///metadata/templates/create) or [Admin Console](https://support.box.com/hc/en-us/articles/360044194033-Customizing-Metadata-Templates) to create the template. 2. Apply an already created template to a Box folder. Make sure you enable the cascade policy. For detailed instructions, see [instructions on customizing and applying templates](https://support.box.com/hc/en-us/articles/360044196173-Using-Metadata). You can also apply a metadata template to a file. ### Display name and key parameters - The `displayName` parameter is the display name of the template visible in the Admin Console. - The `templateKey` parameter is a unique identifier of the template. It needs to be unique across the enterprise for which you create the metadata template. If you don't provide the `templateKey` parameter, API creates a unique one based on the value in `displayName`. - The `[fields].displayName` parameter is the display name of the field as it is shown to the user in the web and mobile apps. - The `[fields].key` parameter is a unique identifier for a specific field in the template. The identifier must be unique within the template to which it belongs. ## Display metadata view Proceed to fill in the necessary properties passed to the Content Explorer. To make things easier, you can use a [sample project](https://github.com/box-community/content-explorer-metadata/tree/main) based on a basic React app to launch metadata view. 1. Clone the metadata sample project. 2. Update the placeholders in [`App.js`](https://github.com/box-community/content-explorer-metadata/blob/main/src/App.js) with actual values: | Parameter | Description | | --- | --- | | DEVELOPER_TOKEN | Developer token generated in the the Developer Console. | | ENTERPRISE_ID | Enterprise ID copied from the General Settings tab of your Box application. | | METADATA_TEMPLATE_NAME | templateKey of your already created metadata template. Note: To make sure you provided the proper name, use the metadata API to retrieve the name, or copy it from the URL in the Admin Console. If you decide to change the template name in the UI, you change the display name only. The name to use in the component is always the one you provided at the beginning. | | METADATA_SOURCE | Source of your metadata. It's a string that combines the scope, enterprise ID, and metadata key. | | ROOTFOLDER_ID | ID of a Box folder to which you want to apply the metadata query and display filtered files. | The `defaultView`, `fieldsToShow`, and `metadataQuery` parameters are already defined in the sample project. Examples of these parameters are available in the sample project. | Parameter | Description | | --- | --- | | defaultView | A required prop to paint the metadata view. If it's not provided, you get the regular file view. | | fieldsToShow | Add or hide specific metadata columns to display in the Content Explorer. | | metadataQuery | Provides a way to find files by searching for the metadata attached to them. For additional information on metadata queries, see this guide. | 1. Pass the required parameters to the Content Explorer component. A sample code for a React component including the Content Explorer metadata view would look as follows: ``` function App() { const token = "<DEVELOPER_TOKEN>"; const rootFolderID = "<ROOTFOLDER_ID>"; const EID = "<ENTERPRISE_ID>"; const templateName = "<METADATA_TEMPLATE_NAME>"; const metadataSource = `enterprise_${EID}.${templateName}`; const metadataSourceFieldName = `metadata.${metadataSource}`; const metadataQuery = { from: metadataSource, query: "key = :arg1", query_params: { arg1: "value" }, ancestor_folder_id: 0, fields: [ `${metadataSourceFieldName}.name`, `${metadataSourceFieldName}.last_contacted_at`, `${metadataSourceFieldName}.industry`, `${metadataSourceFieldName}.role`, ], }; const fieldsToShow = [ // canEdit propetry determines if the user can edit the metadata directly from Content Explorer component { key: `${metadataSourceFieldName}.name`, canEdit: false }, // displayName alows to change the label on metadata column { key: `${metadataSourceFieldName}.industry`, canEdit: false, displayName: "alias" }, { key: `${metadataSourceFieldName}.last_contacted_at`, canEdit: true }, { key: `${metadataSourceFieldName}.role`, canEdit: true }, ]; const defaultView = "metadata"; return ( <IntlProvider locale="en"> <div className="App"> <header className="App-header"> <h2>Metadata view in Content Explorer</h2> </header> <section> <div className="metadata-based-view"> <ContentExplorer rootFolderId={rootFolderID} token={token} metadataQuery={metadataQuery} fieldsToShow={fieldsToShow} defaultView={defaultView} /> </div> </section> </div> </IntlProvider> ); } export default App; ``` ## Metadata keys To decide which fields to show, the Content Explorer uses metadata [field keys](e://post-metadata-templates-schema/#param-fields-key), not the [display names](e://post-metadata-templates-schema/#param-fields-displayName). You can see the display names in the Admin Console and user view, but you can obtain the field keys through the API. The field keys are not changing, even if you change the metadata display name. This ensures that the functionality works properly, despite any changes to the metadata in UI view. ### Metadata keys sanitization [Keys](e://post-metadata-templates-schema/#param-fields-key) are restricted to alphanumeric characters only: - No hyphens `-` or underscores `_` are permitted. - Only letters (`a-z, A-Z`) and numbers (`0-9`) are allowed. **Non-Latin characters:** If keys contain characters from non-Latin alphabets (such as Cyrillic, Arabic, Chinese, etc.) they are automatically renamed to generic identifiers: - `field` for the first occurrence - `field1`, `field2`, and so on for subsequent occurrences Keys are based on the display names. **TIP**: For a detailed flow, see [Metadata view blog post](https://medium.com/box-developer-blog/metadata-view-in-box-content-explorer-4978e47e97e9). **Reference:** https://developer.box.com/guides/embed/ui-elements/explorer-metadata/ --- ## Untitled *Type: guide | Category: Embed Box * Box Edit Custom Domains In order to integrate a third party web application with Box Tools, you will need to explicitly add your application… # Box Edit Custom Domains In order to integrate a third party web application with Box Tools, you will need to explicitly add your application's URL. On Windows, this is achieved by adding a registry key. On MacOS, you will need to run few terminal commands to add and remove domains. Adding domains to a safelist was added in **Box Tools version 4.5.0**. ## Safelisting on Windows Before starting the domain safelisting installation, please download the required scripts from the provided `.rar` [here](https://cloud.box.com/s/kvc9cysgq1y2yldpvciwlpt7093ho78l). ### Per User Installation Open `Add_OpenWith_WhiteListed_Domain.reg` in a text editor. Additional entries can be added on a new line. Close the file (save changes), open a command prompt with admin privileges, and navigate to the .reg file and type the following command. ``` reg import "Add_OpenWith_WhiteListed_Domain.reg" ``` ### Per Machine Installation Open the following file in a text editor: - `x64`: `Per Machine\64 bit\Add_OpenWith_WhiteListed_Domain.reg` - `x86`: `Per Machine\32 bit\Add_OpenWith_WhiteListed_Domain.reg` Replace the placeholder domain with the domain you would like allow. Additional entries may be added on a new line. Close the file (save changes), open a  command prompt with admin privileges, and navigate the .reg file and type the following command: ``` reg import "Add_OpenWith_WhiteListed_Domain.reg" ``` Restart the Windows service: **Box Local Com Service** (`Box Edit.exe`) using one of the following methods. #### Via the command prompt In a command prompt, enter: - `net stop "Box Local Com Service"` - `net start "Box Local Com Service"` #### Via the UI Press `Windows + R` and type `services.msc`. Restart `Box Edit.exe` by finding `Box Edit` in the system tray. Right click and select **Quit**. Open `%programfiles%\Box\Box Edit` and run `Box Edit.exe`. ### Removing - Run `Remove_ALL_OpenWith_WhiteListed_Domain.reg` to remove all domains at once - Run `Remove_OpenWith_WhiteListed_Domain.reg` to remove specific domains. Use the instructions above to add domains to this .reg, which will remove domains. ## Safelisting on MacOS ### Steps Download the bash script [here](https://cloud.box.com/s/z5qhc7rts6mzrhzfx6cpxeb5ed4ve5u6). Open the Terminal, go to the folder where the bash script was downloaded, and add the right permissions by running this command: ``` chmod u+rx OpenWith.sh ``` To **add** domains, run the following command in a terminal: ``` ./OpenWith.sh -a domain1 domain2 ... ``` To **remove** domains, run the following command in a terminal: ``` ./OpenWith.sh -r domain1 domain2 ... ``` To **clear all** domains, run the following command in a terminal: ``` ./OpenWith.sh -c ``` To **list** all domains, run the following command in a terminal: ``` ./OpenWith.sh -l ``` ### Notes - When safelisting domains ensure you are entering the domains without the HTTP protocol (for example without `https://`) or any trailing path like `yourdomain.com/page/3`. - All requests most come from a secure origin via HTTPS. - Safelisting subdomains and ports is possible as the wildcard `*` is supported. For example, all subdomains could be safelisted by adding `*.yourdomain.com` to your list. ## Uninstalling When Box Tools is uninstalled, all domains will be removed. **Reference:** https://developer.box.com/guides/embed/ui-elements/custom-domains/ --- ## Untitled *Type: guide | Category: Embed Box * Browser Support Box UI elements are supported in the following browsers. Chrome, Firefox, Safari, and Edge (latest 2 versions) Mobile Chrome… # Browser Support Box UI elements are supported in the following browsers. - Chrome, Firefox, Safari, and Edge (latest 2 versions) - Mobile Chrome and Safari **Reference:** https://developer.box.com/guides/embed/ui-elements/browser/ --- ## Untitled *Type: guide | Category: Embed Box * Annotations Box Annotations allow developers to provide collaboration capabilities right from within the embedded Box preview in their… # Annotations Box Annotations allow developers to provide collaboration capabilities right from within the embedded Box preview in their application. Box Annotations fit a wide range of use cases and can be used to draw the previewer's attention and/or provide feedback on specific parts of a document or images. Learn more about Content Preview Box Content Preview currently supports four annotation types - highlight comment, highlight only, draw, and point annotation. Box Annotations are today supported on documents and image previews only. You can find the full list of supported file types for Box Content Preview [here](https://support.box.com/hc/en-us/articles/360043695794-Viewing-Different-File-Types-Supported-in-Box-Content-Preview). ## Browser support [Learn more about browser support](g://embed/ui-elements/browser) for Box UI element. ## Usage Box Annotations can be used by pulling from our NPM package. ## Initialization ``` /* global BoxAnnotations */ const boxAnnotations = new BoxAnnotations(); const annotatorConf = boxAnnotations.determineAnnotator( options, disabledAnnotationTypes ); // Construct and init annotator const annotator = new annotatorConf.CONSTRUCTOR(options); annotator.init(scale); ``` Where `disabledAnnotationTypes` is a string of valid annotation types to disable. See below for more details on viewer specific annotation configurations. ## Authentication The UI Elements are designed in an authentication agnostic way so whether you are using UI Elements for users who have Box accounts (Managed Users) or non-Box accounts (App Users), UI Elements should work out of the box. The reason for this is that UI Elements only expect a "token" to be passed in for authentication, and Box provides two different ways to generate tokens - OAuth and JWT. Learn about selecting an authentication method If your application requires the end user to only be able to access a subset of the Annotations functionality, you can use [Downscoping](g://authentication/tokens/downscope) to appropriately downscope your Access Token to a resulting token that has the desired set of permissions, and can thus, be securely passed to the end user client initializing Annotations. Below are a set of new Annotation-specific scopes to go alongside Downscoping. These allow developers to enable/disable functionality on Box Annotations by configuring the appropriate scopes on the downscoped token. To learn more, see Dedicated scopes for the Box UI Elements. ## Parameters & Options ``` const annotator = new annotatorConf.CONSTRUCTOR({ annotator, apiHost, token, container, file: { id, file_version: { id }, permissions }, isMobile, hasTouch, locale, modeButtons: { // Annotation mode buttons, i.e. point, draw, etc point: { // Accessibilty message for the point annotation mode button title: "Point annotation mode", // CSS selector for the point annotation mode button selector: ".bp-btn-annotate-point" } } }); ``` | Parameter | Default | Description | | --- | --- | --- | | annotator | | Viewer-specific annotator configurations object | | apiHost | | Host for Box API calls for example https://app.box.com/api | | fileId | | Box file ID | | token | | A string auth token, see below for details on how to generate annotator tokens with appropriate scopes | | container | | DOM node or selector where Preview should be placed | | file | | File metadata object | | file.id | | String Box_File id | | file.file_version.id | | String Box_File_Version id | | file.permissions | | File permissions object, see below on how to scope permissions | | Option | Default | Description | | --- | --- | --- | | modeButtons | | Object containing a CSS selector and accessibility message for the annotation mode button, see parameters and options example above | | isMobile | false | Whether the user's browser is on a mobile device | | hasTouch | false | Whether the mobile browser has touch enabled | | locale | en-US | Shared link URL | ### Base Scope | Scope Name | What permissions does it grant? | | --- | --- | | base_preview | Allows preview access to a file or files in a folder based on user/file/token permissions | ### Feature Scopes | Scope Name | What permissions does it grant? | | --- | --- | | item_download | Allows files/folders contents to be downloaded | | annotation_view_self | Allows user to view their own annotations | | annotation_view_all | Allows user to view all annotations on the file | | annotation_edit | Allows user to edit their own annotations (includes annotation_view_self) | ### Sample Scenarios | Scenario | Scope Combinations | | --- | --- | | User wants basic preview functionality + ability to edit own annotations | base_preview + annotation_edit | | User wants basic preview functionality + ability to edit own annotations + ability to select text on documents | base_preview + annotation_edit + item_download | | User wants basic preview functionality + ability to view all annotations + ability to edit own annotations | base_preview + annotation_view_all + annotation_edit | | User wants basic preview functionality + ability to view only their own annotations | base_preview + annotation_view_self | ## Enabling/Disabling Annotations and Annotation Types Annotation types can be selectively turned off through preview options. Viewer options override global `showAnnotations` value, for that viewer. See Box Content Preview for more details on how to set up the Preview instances that are used with Box Annotations here. ``` preview.show(..., { showAnnotations: Boolean }); ``` Combined with the following: ``` preview.show(..., { viewers: { VIEWER_NAME: { annotations: { enabled: Boolean, enabledTypes: String[] | null } } } }); ``` This turns the annotations on if `enabled` is set to `true`. This respects the `showAnnotations` value if empty. The `enabledTypes` value is a list of annotation types to enable for this viewer. If empty, will respect default types for that annotator. ### Example Enable all annotations, turn off for Image Viewers, and enable only point annotations on Document viewer: ``` preview.show(fileId, token, { showAnnotations: true, viewers: { Image: { annotations: { enabled: false } }, Document: { annotations: { enabled: true, enabledTypes: ["point"] } } } }); ``` ## Annotators The name of an annotator can be one of the following `DocAnnotator` or `ImageAnnotator`. Call `boxAnnotations.getAnnotators()` to get the list of possible annotators. ## Additional Methods - `annotator.init()` initializes the annotator. - `annotator.isModeAnnotatable(/* String */ type)` returns whether or not the current annotation mode is enabled for the current viewer/annotator. - `annotator.showModeAnnotateButton(/* String */ currentMode)` shows the annotate button for the specified annotation mode. - `annotator.getAnnotateButton(/* String */ annotatorSelector)` gets the annotation button element. - `annotator.showAnnotations()` fetches and shows saved annotations. - `annotator.hideAnnotations()` hides annotations. - `annotator.hideAnnotationsOnPage(/* number */ pageNum)` hides annotations on a specified page. - `annotator.setScale()` sets the zoom scale. - `annotator.toggleAnnotationHandler()` toggles annotation modes on and off. When an annotation mode is on, annotation threads will be created at that location. - `annotator.disableAnnotationMode(/* String */ mode, /* HTMLElement */ buttonEl)` disables the specified annotation mode. - `annotator.enableAnnotationMode(/* String */ mode, /* HTMLElement */ buttonEl)` enables the specified annotation mode. - `annotator.getAnnotatedEl(/* HTMLElement */ containerEl)` determines the annotated element in the viewer. - `annotator.createAnnotationThread(/* Annotation[] */ annotations, /* Object */ location, /* String */ [annotation type])` creates the proper type of annotation thread, adds it to the in-memory map, and returns it. ## Events Events can be bound to the annotator object with `addListener` and removed with `removeListener`. Event listeners should be bound before `showAnnotations()` is called, otherwise events can be missed. ``` /* global BoxAnnotations */ const boxAnnotations = new BoxAnnotations(); const annotatorConf = boxAnnotations.determineAnnotator( options, disabledAnnotationTypes ); // Construct and init annotator const annotator = new annotatorConf.CONSTRUCTOR(options); var listener = value => { // Do something with value }; // Attach listener before calling show otherwise events can be missed annotator.addListener(EVENTNAME, listener); // Initialize a annotator annotator.showAnnotations(); // Remove listener when appropriate annotator.removeListener(EVENTNAME, listener); ``` `EVENTNAME` can be one of the following - `annotator` event will be triggered when we have the annotator instance first available. Box Annotations trigger this event before `load` so that clients can attach their listeners before the `load` event is triggered from Box Content Preview. - `annotationsfetched` event will be triggered when annotations have been fetched from the Box API. - `annotationmodeenter` event will be triggered on when an annotation mode is entered. The event data will contain: ``` { // Annotation mode that was entered mode: 'point', // Optional CSS selector for the container's header headerSelector: '.bp-preview-header', } ``` `annotationmodeexit` event will be triggered on when an annotation mode is exited. The event data will contain: ``` { // Annotation mode that was exited mode: 'point', // Optional CSS selector for the container's header headerSelector: '.bp-preview-header', } ``` `annotationerror` event will be triggered when an annotation error has occurred. The event data will contain: ``` { message: 'message', // Error message to show } ``` `annotationpending` event will be triggered when an annotation thread was created but has not yet been saved on the server. The event data will contain: ``` { data: { type: 'point', // Annotation type threadID: '123abc', userId: '456def', threadNumber: '1' // Thread number from Annotations API } } ``` `annotationthreadsaved` event will be triggered when an annotation thread was saved on the server. The event data will contain: ``` { data: { type: 'point', // Annotation type threadID: '123abc', userId: '456def', threadNumber: '1' // Thread number from Annotations API } } ``` `annotationthreaddeleted` event will be triggered when an annotation thread was deleted on the server. The event data will contain: ``` { data: { type: 'point', // Annotation type threadID: '123abc', userId: '456def', threadNumber: '1' // Thread number from Annotations API } } ``` `annotationsaved` event will be triggered when an annotation is added and saved to an existing annotation thread on the server. The event data will contain: ``` { data: { type: 'point', // Annotation type threadID: '123abc', userId: '456def', threadNumber: '1' // Thread number from Annotations API } } ``` `annotationdeleted` event will be triggered when an annotation is deleted from an existing thread on the server. The entire annotation thread is not deleted. The event data will contain: ``` { data: { type: 'point', // Annotation type threadID: '123abc', userId: '456def', threadNumber: '1' // Thread number from Annotations API } } ``` `annotationcanceled` event will be triggered when an annotation is canceled from posting on either a new or existing thread. The event data will contain: ``` { data: { type: 'point', // Annotation type threadID: '123abc', userId: '456def', threadNumber: '1' // Thread number from Annotations API } } ``` `annotationdeleteerror` event will be triggered when an error occurs while deleting an annotation on either a new or existing thread. The event data will contain: ``` { data: { type: 'point', // Annotation type threadID: '123abc', userId: '456def', threadNumber: '1' // Thread number from Annotations API } } ``` `annotationcreateerror` event will be triggered when an error occurs while posting an annotation on either a new or existing thread. The event data will contain: ``` { data: { type: 'point', // Annotation type threadID: '123abc', userId: '456def', threadNumber: '1' // Thread number from Annotations API } } ``` `annotatorevent` Each annotator will trigger its own sets of events. For example, the Image Annotator will trigger `rotate` or `resize`, etc. while other annotator may trigger another set of events. The Annotator wrapper will also re-emit events at the Preview level in Box Content Preview, with event data containing: ``` { event: EVENTNAME, // Event name data: DATA, // Event data object annotatorName: ANNOTATORNAME, // Name of the annotator fileVersionId: fileVersionId // The file version id fileId: fileId // The file id } ``` ### Example event usage ``` preview.addListener("annotator", viewer => { annotator.addListener("annotationsfetched", () => { // Do something when annotations are fetched on a preview }); }); // Event listeners can be attached to viewers preview.addListener("load", data => { var viewer = data.viewer; viewer.addListener("annotationsfetched", () => { // Do something when annotations are fetched on a preview }); }); // Event listeners can be attached to annotators preview.addListener("load", data => { var annotator = data.viewer.annotator; annotator.addListener("annotationsfetched", () => { // Do something when annotations are fetched on a preview }); }); preview.addListener("annotatorevent", data => { if (data.event === "annotationsfetched") { // Do something when annotations are fetched on a preview } else if (data.event === "annotationerror") { // Do something different when an annotation error has occurred } else { } }); preview.addListener("annotatorevent", data => { if (data.viewerName === "Image") { if (data.event === "annotationsfetched") { // Do something when annotations are fetched on an image preview } } else if (data.viewerName === "MultiImage") { if (data.event === "annotationsfetched") { // Do something different when annotations are fetched on a multi-page image } } else { } }); preview.addListener("annotationsfetched", data => { if (data.viewerName === "Image") { // Do something when annotations are fetched on an image preview } else if (data.viewerName === "MultiImage") { // Do something different when annotations are fetched on a multi-page image } else { } }); ``` ## Annotation Thread ### Thread Methods The following methods are available for the annotation threads. | Method Name | Explanation | Method Parameters | | --- | --- | --- | | createDialog | Creates the dialog for the thread | | | show | Shows the annotation indicator | | | hide | Hides the annotation indicator | | | reset | Resets thread state to 'inactive' | | | showDialog | Shows the appropriate dialog for this thread | | | hideDialog | Hides the appropriate indicator for this thread | | | saveAnnotation | Saves an annotation locally and on the server | {string} annotation type, {text} text of annotation to save | | deleteAnnotation | Deletes an annotation | {string} annotation ID, {boolean} whether or not to delete on server, default true | ### Thread Events All annotation threads trigger the following events. The event data will contain: ``` { data: { type: 'point', // Annotation type threadID: '123abc', userId: '456def', threadNumber: '1' // Thread number from Annotations API } } ``` | Event Name | Explanation | | --- | --- | | annotationpending | An annotation thread was created but has not yet been saved on the server. | | annotationthreadsaved | An annotation thread was saved on the server. | | annotationthreaddeleted | An annotation thread was deleted on the server. | | annotationsaved | An annotation thread was added and saved to an existing annotation thread on the server | | annotationdeleted | An annotation thread was deleted from an existing thread on the server. The entire annotation thread is not deleted. | | annotationcanceled | An annotation thread was canceled from posting on either a new or existing thread. | | annotationdeleteerror | An error occurs while deleting an annotation on either a new or existing thread. | | annotationcreateerror | An error occurs while posting an annotation on either a new or existing thread. | See the **Events** section above for example event usage. ## Annotation Dialog ### Dialog Methods The following methods are available for the annotation dialog. | Method Name | Explanation | Method Parameters | | --- | --- | --- | | show | Positions and shows the dialog | | | hide | Hides the dialog | | | hideMobileDialog | Hides and resets the shared mobile dialog | | | addAnnotation | Adds an annotation to the dialog | {Annotation} annotation to add | | removeAnnotation | Removes an annotation from the dialog | {string} annotation ID | | postAnnotation | Posts an annotation in the dialog | {string} annotation text to post | | position | Positions the dialog | | ## Supported Annotation Types Point annotations are supported on both document and image formats. Highlight comment, highlight only, and draw annotations are only supported on document formats. Supported document file extensions: `pdf`, `doc`, `docx`, `ppt`, `pptx`, `xlsx`, `xls`, `xlsm`. Supported image file extensions: `ai`, `bmp`, `dcm`, `eps`, `gif`, `idml`, `indd`, `indt`, `inx``png`, `ps`, `psd`, `svs`, `tga`, `tif`, `tiff`. **Reference:** https://developer.box.com/guides/embed/ui-elements/annotations/ --- ## Untitled *Type: guide | Category: Embed Box * Customize Access Motivation Box UI Elements are initialized on the client and make API calls directly to Box. Thus, a valid access token… # Customize Access ## Motivation Box UI Elements are initialized on the client and make API calls directly to Box. Thus, a valid access token must live on the client because all Box API requests need to be authenticated. [Token Exchange](g://authentication/tokens/downscope) is a mechanism to exchange a "parent token" (a token for your managed or app user, service account, or application) to a "child token" which is scoped down to the minimum set of required permissions so it can be securely sent down to the client without elevating client privileges. The Box UI Elements are designed to adapt to the permissions on the token, so an additional advantage of using Token Exchange with them is that front-end developers won't have to manually toggle UI controls on/off as long as the correct set of permissions are present on the client token. This behavior mimics the experience in the Box application as well. For example, if a user with "Preview only" permissions to a folder does not see the "Download" button present in the UI. This blueprint covers how to use Token Exchange with UI Elements in your application and example use cases. - **Making API calls from client more secure:** As a general security practice, we recommend generating and sending downscoped tokens per every action performed by the user to limit exposure on the client. For example, even if a user should have preview and share access to a file, instead of sending the user token to the client which would give them complete user-level access, we suggest downscoping the token using Token Exchange and sending them a "preview only" token when they wish to preview a file and a "share" token when they want to share a file. - **Creating a custom permissions model:** If you are building an application using UI Elements and the default Box access levels don't fit your permission model, you can start out with a fully scoped token for all your users and trim them down using Token Exchange on the fly to map to your custom permissions model. - **Using Box services transactionally (no Box user accounts):** If you are not creating users in Box and are using Box services transactionally, you won't have access to user level tokens. In such cases, you can use Token Exchange to scope down the Service Account token to an appropriately scoped token. - **Creating a user only to give them access to another user's content:** If you need to provide an end user "one time" access to another app/managed user's content, Token Exchange can be used to downscope the app/managed user token to the specific permissions and file/folders you want to give the end user access to, and pass it down to UI Elements to give them access to that content. For example, using Token Exchange, you can downscope a user token to have read-only access to content and pass it down to another user. ## Implementation The solution for all of the above is using Token Exchange to exchange a parent token for a downscoped token and initializing the UI Element using that token. Token Exchange takes in the "parent token", "list of scopes" and optionally a file/folder ID as the input arguments, and returns a token that is scoped down to those exact set of scopes and the respective file/folder ID, if present in the input arguments. # Dedicated scopes designed for UI Elements We have designed a set of scopes that work seamlessly with the UI Elements. While token exchange works with all Box scopes, we recommend using these scopes along with UI Elements since they include the exact set of permissions required for base functionality as well as incremental permissions for added functionality. ## Developer Flow Now that you understand the different scopes, let's walk through a scenario for using Token Exchange with a UI Element. **Scenario:** Provide a user with the ability to browse a folder tree using the Box Content Explorer UI Element and allow them to preview and download files. Sharing should be turned off. **Steps:** Create a Managed User, App User or Service Account depending on your use case. You will use this user or application's token as the parent token for Token Exchange. To generate the user token follow: - Authentication with OAuth guide if you would like to create a Managed User - Authentication with JWT guide if you would like to create an App User or Service Account Add the user you created above as a Collaborator to the content using the Create Collaborator API as shown below. This step gives the user access to the content if not already under the user's account. If the file/folder was created under this user's account, then the user by default has "Owner" access to the folder so you can skip the collaboration step. ``` curl https://api.box.com/2.0/collaborations \ -H "authorization: Bearer [ACCESS_TOKEN]" \ -d '{"item": { "id": "123456", "type": "folder"}, "accessible_by": { "id": "USER_ID", "type": "user" }, "role": "editor"}' \ -X POST ``` # Access Levels in Box There are [7 different access levels](https://community.box.com/t5/How-To-Guides-for-Sharing/What-Are-The-Different-Access-Levels-For-Collaborators/ta-p/144) that a Box user can be collaborated as on a file/folder in Box. If the file/folder was created under this user's account, then the user has "Owner" access to the folder by default so you can skip the collaboration step. - Use the Token Exchange API to exchange the parent token for a child token that contains the base scope for Content Explorer (`base_explorer`), `item_download` and `item_preview` scopes enabled for the specific `folder_id` `123456`. We strongly recommend performing this step on the application server. ### Request ``` curl https://api.box.com/oauth2/token \ -d 'subject_token=ACCESS_TOKEN' \ -d 'subject_token_type=urn:ietf:params:oauth:token-type:access_token' \ -d 'scope=item_upload item_preview base_explorer' \ -d 'resource=https://api.box.com/2.0/folders/123456' \ -d 'grant_type=urn:ietf:params:oauth:grant-type:token-exchange' \ -X POST ``` ### Response ``` { "access_token": "CHILD_TOKEN", "expires_in": 4247, "token_type": "bearer", "restricted_to": [ { "scope": "base_explorer", "object": { "type": "folder", "id": "123456", "sequence_id": "0", "etag": "0", "name": "FOLDER_NAME" } }, { "scope": "item_download", "object": { "type": "folder", "id": "123456", "sequence_id": "0", "etag": "0", "name": "FOLDER_NAME" } }, { "scope": "item_preview", "object": { "type": "folder", "id": "123456", "sequence_id": "0", "etag": "0", "name": "FOLDER_NAME" } } ], "issued_token_type": "urn:ietf:params:oauth:token-type:access_token" } ``` The `CHILD_TOKEN` obtained above is scoped down to only include download and preview permissions for folder id `123456` and its children. Use this `CHILD_TOKEN` in your Content Explorer UI Element. To see a quick demo, you can use our Content Explorer UI Element CodePen Sample and replace the access token and folder id values in the JS Tab. Click "Run". You should see the Content Explorer initialized at the folder you specified. Also, notice that your Content Explorer no longer shows the "Share" button as it would have with the parent (user) token. ## When NOT to use Token Exchange **Does not replace Users or Groups in Box:** We recommend not using Token Exchange as a replacement to creating users in Box. One way to determine whether you should create Box users, is by doing an assessment to determine if it makes logical sense for every end user of your application to have a copy of their own content. Here are some benefits of maintaining user level accounts in Box, which you wouldn't get by using Token Exchange alone. - **Content isolation/security:** It's better to have user level accounts since in the case of the parent token accidentally leaking, you would only be compromising the content of a single user vs. all users of your enterprise. - **Performance:** Creating users/groups in Box is also useful as your application does not have to determine the appropriate permissions at the time of accessing content since that is likely to affect the performance of your application. - **User level tracking/auditing:** Several Box features such as auditing, access stats, retention, etc. leverage Box's user model. If it is essential for you to use those features for you then you would have to create user-level accounts. **Does not replace Collaborations in Box:** Collaborations is a more standard and easier to scale way to provide Box users with access to content. Also, managing content access through collaborations in Box requires your application to manage lesser code/data on which user should have access to what content. If you are using Token Exchange as a replacement to Collaborations, you will need to keep a mapping of every user -> every file and folder that they should have access to and that could get out of control pretty quickly. **Caching the downscoped tokens:** If performance is critical to your application, you should pre-cache downscoped tokens on the server side. If you are pre-caching tokens, we recommend implementing retries as the tokens expire within 1 hour. # Token Lifespan The lifespan of parent and child tokens are not mutually dependent, for example when generating a child token doesn't render the parent token inactive. Similarly, generating more child tokens doesn't impact the previous child tokens in any way. ## Example Scenarios ### Scenario #1: More secure client API calls An equity-investment firm is building out a partner/investor portal to broadcast information (view-only access) to investors published by the employees. They create app users for each of their external customers and employees have provisioned Box accounts which they used to publish content. All app users are collaborated on published content as Viewer Uploader, so they can both preview as well as upload content in their account as needed. Once an investor/partner signs in they are taken to a portal where they can view as well as upload information. Since both preview and upload require the API calls to be made via the client, instead of passing down a “Viewer Uploader” token, the application uses Token Exchange to downscope the token appropriately based on the action that the user is trying to perform. This ensures that if the token is compromised, data leakage is minimized thereby improving the overall security posture of the application. ### Scenario #2: A custom permissions model A large fin-tech company is building a secure client vault to manage investments for their clients. They are also using Box UI Elements to build out the content management front end of their application. They create app users as usual for each one of their clients and app advisors. When they wanted to share content across app users, they collaborate the app user(s) as "Editor" role (Box's role). This guarantees that every app user had full access to every other user’s content. To enable a user to access content via the UI Elements, the application does not provide the app user token to the client directly as the app user may be collaborated as an "Editor" into other user's content (highly privileged). Instead, it uses Token Exchange using the app user token to generate a downscoped token that limits: - the scope of what the token can be used for (view, upload, download, browse, share, etc.) and/or - the specific files that the user should have access to ### Scenario #3: Process Flows A not-for-profit union bank is developing a loan processing application using Box's secure content layer to facilitate the sharing of documents between loan applicants and internal users (loan processors and underwriters). The basic process is as follows: - When customers apply for a loan they must submit documents through a custom-built web portal as a part of the process (proof of income, identity etc.) - Box is used as the intermediary for sending/receiving docs - Internal employees need to upload files for customers - Internal employees can access documents through a custom web portal as well as Box Web app The application developers used Token Exchange along with UI Elements to build out a loan process management solution, where the application server downscopes the loan application's app user token based on which operations the client needs to perform, and passes it down to the clients (customers and loan officers). Using these downscoped tokens, the UI Elements are able to show the right controls/buttons to indicate to the user which actions they can perform (example: upload button is grayed out from the content explorer UI Elements if the downscoped token passed doesn't contain upload permissions). ### Scenario #4: Transactional Flows A Learning Management System provider is using Box Platform to power preview in their application. All users and associated permissions are managed by the application outside Box. From Box's perspective, all API calls made by the application are on its own behalf (any not any individual user), and all content stored and previewed belongs to the application and not to a single user. In this case, the application developer is using a sliver of Box's overall capabilities (for example Preview) transactionally and none of the other secure content sharing and collaboration capabilities. In fact, this is a key use case for many customers. Consider a straightforward use case of the application - the end user needs to upload a file and then preview it in the application itself. To do this the application is using the Content Uploader and Content Preview UI Elements, respectively. Since both preview + upload are bandwidth heavy operations, the application should not proxy the traffic and instead allow the client to perform these operations directly against Box. To make calls against Box, all UI Elements require a valid access token to be available on the client. But the application should never pass down the privileged "service account" token to the client which would give the client access to all content ever uploaded through the application. Instead, the application uses Token Exchange to down-scope it's service account token to an upload-only token to allow the client to upload files via the Upload UI Element, and a separate preview-only token to let the client preview via the Preview UI Element. ## Anti-patterns These anti-patterns have been included here to help you identify patterns that we do not recommend as they will either make your app development more challenging, your application less secure or performing or both. If you find yourself implementing any of these patterns on your app, please reach out to us via Box support so we can help you out. ### Passing privileged tokens to client Please NEVER DO THIS. This can potentially compromise the security of content in your Box enterprise. Always use token exchange to provide end users with the exact set of permissions. ### Proxying/filtering API responses If you are proxying/filtering API responses from Box only to limit data/content exposure to the client, you should try to see if using Token Exchange allows you to limit the exposure. This is especially true for high-bandwidth operations such as preview, download, and upload which are bandwidth intensive and so we recommend that the client performs those operations directly with Box. **Reference:** https://developer.box.com/guides/embed/ui-elements/access/ --- ## Untitled *Type: guide | Category: Embed Box * UI Elements Box UI Elements are pre-built UI components that allow developers to add elements of the main Box web application into their own… # UI Elements Box UI Elements are pre-built UI components that allow developers to add elements of the main Box web application into their own applications. They can be used to navigate through, upload, preview, and select content stored on Box and are available both as React components and framework-agnostic JavaScript libraries. ## Available Elements Box offers several UI Elements to create common user experiences with files and folders in an application. The available UI Elements are: - [Content Explorer](g://embed/ui-elements/explorer)- Allow users to search and browse through files and folders - [Content Open With](g://embed/ui-elements/open-with) - Allow users to open content stored in box with a partner application using an embedded dropdown We no longer support the `OpenWith` UI element for any new customers as of December 21, 2021. - [Content Picker](g://embed/ui-elements/picker) - Allow users to select files or folders from their Box account - [Content Preview](g://embed/ui-elements/preview) - Display interactive viewers for documents, images, audio, video, and more - [Content Sidebar](g://embed/ui-elements/sidebar) - Display a sidebar for file metadata and activity feed information - [Content Uploader](g://embed/ui-elements/uploader) - Allow users to upload files by selecting or using drag-and-drop UI Elements can be used in isolation or joined together to construct common user flows with content, such as uploading and then viewing a file. **Reference:** https://developer.box.com/guides/embed/ui-elements/ --- ## Untitled *Type: guide | Category: Embed Box * Content Explorer The Box Content Explorer UI Element allows developers to embed a folder view of content stored on Box in their desktop or… # Content Explorer The Box Content Explorer UI Element allows developers to embed a folder view of content stored on Box in their desktop or mobile web application. The library fetches information about a specified folder through the Box API and then renders the content in a folder view, similar to the main Box web application. Users can then navigate through the folder hierarchy and perform file operations like rename, delete, and share. Content Explorer comes with a metadata view that uses metadata query to find files and folders based on their metadata. The data is then displayed in the embedded view. ## Installation [Learn how to install](g://embed/ui-elements/installation) Box UI elements either through NPM or the Box CDN. ## Authentication The UI Elements are designed in an authentication agnostic way so whether you are using UI Elements for users who have Box accounts (Managed Users) or non-Box accounts (App Users), UI Elements should work out of the box. The reason for this is that UI Elements only expect a "token" to be passed in for authentication, and Box provides two different ways to generate tokens - OAuth and JWT. Learn about selecting an authentication method ## Sample HTML ``` <!DOCTYPE html> <html lang="en-US"> <head> <meta charset="utf-8" /> <title>Box Content Explorer Demo</title> <!-- Latest version of the explorer css for your locale --> <link rel="stylesheet" href="https://cdn01.boxcdn.net/platform/elements/{VERSION}/en-US/explorer.css" /> </head> <body> <div class="container" style="height:600px"></div> <!-- Latest version of the explorer js for your locale --> <script src="https://cdn01.boxcdn.net/platform/elements/{VERSION}/en-US/explorer.js"></script> <script> var folderId = "123"; var accessToken = "abc"; var contentExplorer = new Box.ContentExplorer(); contentExplorer.show(folderId, accessToken, { container: ".container", }); </script> </body> </html> ``` ## Demo ## API ``` const { ContentExplorer } = Box; const contentExplorer = new ContentExplorer(); /** * Shows the content explorer. * * @param {string} folderId - The root folder id * @param {string} accessToken - Box API access token * @param {Object} [options] - Options * @return {void} */ contentExplorer.show(folderId, accessToken, options); /** * Hides the content explorer, removes all event listeners, and clears out the * HTML. * * @return {void} */ contentExplorer.hide(); /** * Clears out the internal in-memory * cache for the content explorer forcing * re-load of items via the API. * * @public * @return {void} */ contentExplorer.clearCache(); /** * Adds an event listener to the content explorer. Listeners should be added * before calling show() so no events are missed. * * @param {string} eventName - Name of the event * @param {Function} listener - Callback function * @return {void} */ contentExplorer.addListener(eventName, listener); /** * Removes an event listener from the content explorer. * * @param {string} eventName - Name of the event * @param {Function} listener - Callback function * @return {void} */ contentExplorer.removeListener(eventName, listener); /** * Removes all event listeners from the content explorer. * * @return {void} */ contentExplorer.removeAllListeners(); ``` ### Parameters | Parameter | Type | Description | | --- | --- | --- | | folderId | String | Box Folder ID. This will be the ID of the folder you want to navigate. If you want to use the Box All Files folder, then use 0 as the folderId. | | accessToken | String | Box API access token to use. This should have read/write access to the folder above. The value passed in for the token is assumed to never expire while the explorer is visible. | | options | Object | Optional options. See below for details. For example: contentExplorer.show(FOLDER_ID, TOKEN, {canDelete: false}) would be used to hide the delete option. | ### Options | Parameter | Type | Default | Description | | --- | --- | --- | --- | | container | String | document.body | CSS selector of the container in which the content explorer should be placed. Calling hide() will clear out this container. | | sortBy | String | name | The initial sort by option for the content list. Value can be either id, name, date or size. | | sortDirection | String | ASC | The initial sort direction option for the content list. Value should be either ASC or DESC. | | logoUrl | String | | URL of custom logo to show in header. If this value is the string box then the box logo will show. | | canPreview | Boolean | true | If this option is set to true AND can_preview permission on the file is true, files on the content explorer will be clickable. Clicking on a file will launch preview of that file. This option has no effect when the file permission can_preview is set to false. This is only applicable to files that can be previewed. | | canDownload | Boolean | true | Visually hides the download option if this is set to false. Hiding the option alone will not prevent downloading unless the file permissions also set can_download to false. This option has no effect when the file permission can_download is set to false. This is only applicable to files. | | canDelete | Boolean | true | Visually hides the delete option if this is set to false. Hiding the option alone will not prevent deleting unless the item permissions also set can_delete to false. This option has no effect when the item permission can_delete is set to false. | | canRename | Boolean | true | Visually hides the rename option if this is set to false. Hiding the option alone will not prevent renaming unless the item permissions also set can_rename to false. | | canUpload | Boolean | true | Visually hides the upload option if this is set to false. Hiding the option alone will not prevent uploading unless the current folder permissions also set can_upload to false. This option has no effect when the folder permission can_upload is set to false. | | canCreateNewFolder | Boolean | true | Visually hides the create new folder option. Hiding the option alone will not prevent creating a new folder unless the folder item permissions also set can_upload to false. This option has no effect when the folder item permission can_upload is set to false. | | canShare | Boolean | true | Visually hides the share button if set to false. Hiding the button alone will not prevent sharing unless the item permissions also set can_share to false. This option has no effect when the item permission can_share is set to false. | | canSetShareAccess | Boolean | true | Visually hides the sharing drop down select that allows changing share access, if set to false. Hiding the select drop down alone will not prevent changing the share access unless the item permissions also set can_set_share_access to false. This option has no effect when the item permission can_set_share_access is set to false. | | sharedLink | String | | Shared link URL, required if folder is shared and the access token doesn't belong to an owner or collaborator of the file. | | sharedLinkPassword | String | | Shared link password, required if shared link has a password. | | size | String | undefined | Indicates to the content explorer to fit within a small or large width container. Value can be absent or one of small or large. If absent the UI Element will adapt to its container and automatically switch between small width or large width mode. | | isTouch | Boolean | Defaults to the browser and device's default touch support | Indicates to the Content Explorer that it's is being rendered on a touch enabled device. | | autoFocus | Boolean | false | When set to true, the item grid will get focus on initial load. | | defaultView | String | files | Value can be either be files, recents or metadata. When set to recents, by default you will see recent items instead of the regular file/folder structure. metadata is required to display the metadata view in Content Explorer. If not provided, you'll get a regular folder view. | | requestInterceptor | Function | | Function to intercept requests. For an example see this CodePen. Our underlying XHR library is axios.js and we follow a similar approach for interceptors. | | responseInterceptor | Function | | Function to intercept responses. For an example see this CodePen. Our underlying XHR library is axios.js and we follow a similar approach for interceptors. | | ContentOpenWithProps | Object | { show: false } | Allows you to show the Open With Element when previewing via the explorer. | | token | String | | Developer token generated in the Developer Console. | | metadataQuery | Object | | Metadata query used to get the information for the metadata view. | | rootFolderID | String | | Folder ID with a metadata template applied. metadataQuery will apply to this folder. | | fieldsToShow | Object | | The metadata fields/columns to view - must be valid field names from the metadata template. | ### Events | Event Name | Event Data | Description | | --- | --- | --- | | select | Array<File | Web Link | Folder> | Will be fired when item rows are selected. | | rename | File | Web Link | Folder | Will be fired when an item is renamed. | | preview | File | Will be fired when a file is previewed. | | download | Array<File> | Will be fired when items are downloaded. | | delete | Array<File> | Will be fired when items are deleted. | | upload | Array<File> | Will be fired when items are uploaded. | | navigate | Folder | Will be fired when navigating into folders. | | create | Folder | Will be fired when a new folder is created | ## Keyboard Shortcuts When the item grid has focus, either manually by clicking on it or programmatically via javascript or via the above mentioned `autoFocus` prop, the following keyboard shortcuts will work if their corresponding operations are applicable and allowed. | Key | Action | | --- | --- | | Arrow Up | Previous item row | | Arrow Down | Next item row | | Ctrl/Cmd + Arrow Up | First item row | | Ctrl/Cmd + Arrow Down | Last item row | | / | Search | | Shift + X | Select an item row | | Delete | Delete the selected item | | Enter | Open the selected item | | Shift + R | Rename the selected item | | Shift + S | Share the selected item | | Shift + D | Download the selected item | | g then f | Navigates to the root folder | | g then u | Upload to the current folder | | g then b | Focuses the root folder breadcrumb | | g then r | Recent items | ## Scopes If your application requires the end user to only be able to access a subset of the Content Explorer functionality, you can use [Downscoping](guide://authentication/tokens/downscope) to appropriately downscope the Access Token to a resulting token that has the desired set of permissions, and can thus, be securely passed to the end user client initializing the Content Explorer. Below are a set of UI Element-specific scopes to go alongside Downscoping. These allow developers to enable/disable UI controls on the Content Explorer by configuring the appropriate scopes on the downscoped token. To learn more, see [Dedicated Scopes for Box UI Elements](guide://api-calls/permissions-and-errors/scopes). ### Base Scope | Scope Name | Permissions granted | | --- | --- | | base_explorer | Allows access to content in the folder tree based on user/file/token permissions. | ### Feature Scopes | Scope Name | Permissions granted | | --- | --- | | item_preview | Automatically enables preview of the file, upon user click (requires Preview UI Element to be referenced) | | item_download | Allows files/folders contents to be downloaded | | item_rename | Allows files/folders to be renamed | | item_share | Allows sharing of resource specified under "resource" of the downscope request. | | item_delete | Allows file/folders to be deleted | ### Sample Scenarios | Scenario | Scopes | | --- | --- | | User want to navigate folder structure (basic functionality) | base_explorer | | User want basic functionality + preview | base_explorer + item_preview | | User want basic functionality + preview + download | base_explorer + item_preview + item_download | | User want basic functionality + preview + download + rename file/folder names | base_explorer + item_preview + item_download + item_rename | | User wants all functionality (basic, preview, download, rename, share, upload and delete) | base_explorer + item_preview + item_download + item_rename + item_delete + item_share + item_upload | ## Custom actions You can expand the actions in the **More Options** menu for files and folders in Content Explorer and Content Picker. Your custom options show when user clicks the ellipsis button. To customize the **More Options** menu, pass an array of custom actions to `itemActions`. ``` contentExplorer.show(configData.FOLDER_ID, configData.ACCESS_TOKEN, { container: ".container", itemActions: customActions, }); ``` The array can include multiple actions. The action object should include the `label` and `onAction` callback functions. You can filter the custom actions to appear only on a specific item `type`, by passing the `file` or `folder` value. The `filter` value is used for advanced filtering, for example by a specific file extension: ``` const customActions = [ { label: "Preview in New Window", onAction: (item) => alert('action ' + item), type: 'file', }, { label: "Open in Box.com", onAction: (item) => window.open("https://app.box.com"), }, { label: "Export", onAction: (item) => console.log('action ' + item), filter: (item) => item.extension?.toLowerCase() === 'pdf', } ]; ``` See the implemented examples in CodePen: **Reference:** https://developer.box.com/guides/embed/ui-elements/explorer/ --- ## Untitled *Type: guide | Category: Embed Box * Customize Logo Each of the Box UI Elements allow for specifying a custom logo to place in the top-left corner of the embedded container. By… # Customize Logo Each of the Box UI Elements allow for specifying a custom logo to place in the top-left corner of the embedded container. By default, each of the UI Elements uses a generic placeholder as a logo. This is meant to be replaced by either a Box logo or a custom logo, such as a company's logo. To add a logo to a UI Element, supply a URL for a logo as an option in in the Javascript setup code. The following is an example of how to do this with the Content Preview UI Element. ``` var preview = new Box.Preview(); preview.show(fileId, accessToken, { container: '.preview-container', sharedLink: 'https://app.box.com/v/foo', sharedLinkPassword: 'bar', collection: [FILE_ID, '123', '234', ...], header: 'light', logoUrl: 'http://i.imgur.com/xh8j3E2.png', showAnnotations: true, showDownload: true }); ``` # Box Logo To present the Box logo in a UI Element, specify the string `box` as the `logoURL` option. ## Image size Images files will be fitted to a maximum height of 32 pixels and a maximum width of 80 pixels. Larger images will be shrunk to fit these dimensions. **Reference:** https://developer.box.com/guides/embed/ui-elements/logo/ --- ## Untitled *Type: guide | Category: Embed Box * Installation UI Elements can be used either by downloading the Javascript libraries directly from the Box CDN or by installing our NPM… # Installation UI Elements can be used either by downloading the Javascript libraries directly from the Box CDN or by installing our [NPM package](https://www.npmjs.com/package/box-ui-elements). All UI Elements require their corresponding CSS stylesheet to render properly. ## Prerequisites To use Box UI Elements, you need the following Node and React versions: - Node version: `>=18.18.2 <20.11.0` - React version `>=18.0.0` The available versions of UI elements are listed in the [manual installation](#manual-installation) section. ### Migration to React 18 For existing projects based on React 17 or below, check the [React 18 migration guide](https://react.dev/blog/2022/03/08/react-18-upgrade-guide). Once you upgrade React in your project, update the `box-ui-elements` package with `npm` or `yarn`: ``` npm install box-ui-elements --legacy-peer-deps yarn add box-ui-elements ``` This should result in updated version in the `package.json` file: ``` "box-ui-elements": "^23.0.0" ``` No additional changes related to Box UI elements are required in your code. For more information, check the blog post about React 18 upgrade for UI elements. ## NPM installation Use this when you are building a React based app and would like to import the components directly into your app at build time. **Note** The `--legacy-peer-deps` flag is necessary when installing UI elements with `npm` due to library dependencies. ``` npm install box-ui-elements --legacy-peer-deps yarn add box-ui-elements ``` Learn more on the NPM website ## Content Preview Use the Content Preview SDK if you need a specific version of the preview library. Otherwise, use Content Preview UI Element wrapper. ## Manual installation All the UI elements are also available directly from the Box CDN. | Element | Version | File | | --- | --- | --- | | Content Explorer | 23.0.0 | CSS https://cdn01.boxcdn.net/platform/elements/23.0.0/en-US/explorer.css | | | | JS with React https://cdn01.boxcdn.net/platform/elements/23.0.0/en-US/explorer.js | | | | JS without React https://cdn01.boxcdn.net/platform/elements/23.0.0/en-US/explorer.no.react.js | | Content Open With | 23.0.0 | CSS https://cdn01.boxcdn.net/platform/elements/23.0.0/en-US/openwith.css | | | | JS with React https://cdn01.boxcdn.net/platform/elements/23.0.0/en-US/openwith.js | | | | JS without React https://cdn01.boxcdn.net/platform/elements/23.0.0/en-US/openwith.no.react.js | | Content Picker | 23.0.0 | CSS https://cdn01.boxcdn.net/platform/elements/23.0.0/en-US/picker.css | | | | JS with React https://cdn01.boxcdn.net/platform/elements/23.0.0/en-US/picker.js | | | | JS without React https://cdn01.boxcdn.net/platform/elements/23.0.0/en-US/picker.no.react.js | | Content Sidebar | 23.0.0 | CSS https://cdn01.boxcdn.net/platform/elements/23.0.0/en-US/sidebar.css | | | | JS with React https://cdn01.boxcdn.net/platform/elements/23.0.0/en-US/sidebar.js | | | | JS without React https://cdn01.boxcdn.net/platform/elements/23.0.0/en-US/sidebar.no.react.js | | Content Uploader | 23.0.0 | CSS https://cdn01.boxcdn.net/platform/elements/23.0.0/en-US/uploader.css | | | | JS with React https://cdn01.boxcdn.net/platform/elements/23.0.0/en-US/uploader.js | | | | JS without React https://cdn01.boxcdn.net/platform/elements/23.0.0/en-US/uploader.no.react.js | | Content Preview UI Element | 23.0.0 | CSS https://cdn01.boxcdn.net/platform/elements/23.0.0/en-US/preview.css | | | 23.0.0 | JS https://cdn01.boxcdn.net/platform/elements/23.0.0/en-US/preview.js | | Content Preview SDK | 2.106.0 | CSS https://cdn01.boxcdn.net/platform/preview/2.106.0/en-US/preview.css | | | | JS https://cdn01.boxcdn.net/platform/preview/2.106.0/en-US/preview.js | Use these links to either download the elements code to your application's code, or embed them straight into your page from the CDN. ``` <!DOCTYPE html> <html lang="en-US"> <head> <!-- Latest version of the explorer css for your locale --> <link rel="stylesheet" href="https://cdn01.boxcdn.net/platform/elements/{VERSION}/en-US/explorer.css" /> </head> <body> <!-- Latest version of the explorer js for your locale --> <script src="https://cdn01.boxcdn.net/platform/elements/{VERSION}/en-US/explorer.js"></script> ... </body> </html> ``` For Box Preview, the sample would look slightly different. ``` <!DOCTYPE html> <html lang="en-US"> <head> <meta charset="utf-8" /> <title>Box Content Preview Demo</title> <!-- Latest version of Box Content Preview for en-US locale --> <script src="https://cdn01.boxcdn.net/platform/preview/{VERSION}/en-US/preview.js"></script> <link rel="stylesheet" href="https://cdn01.boxcdn.net/platform/preview/{VERSION}/en-US/preview.css" /> </head> <body> <div class="preview-container" style="height:400px; width:100%;"></div> <script> var preview = new Box.Preview(); preview.show("93392244621", "EqFyi1Yq1tD9mxY8F38sxDfp73pFd7FP", { container: ".preview-container", showDownload: true, }); </script> </body> </html> ``` ### Versions Most of the UI elements have 2 distinct versions. A standard `*.js` file (e.g. `explore.js`) that includes React and ReactDOM in the bundle. - Use this when you are not building a React based app or you don't want to include the components as part of your app's build process. - It includes React and ReactDOM libraries. - The file size of this asset will be larger than the one below. A smaller `*.no.react.js` file (e.g. `explore.no.react.js`) that does not get bundled with React and ReactDOM. - Use this when both React and ReactDOM libraries are already loaded on the application. - These libraries expect a React and ReactDOM `>= 16.6` and `< 18`. Only one of the two `js` files and the additional `css` file need to be added to a project. ### Supported Locales The above asset URLs use `en-US`. If you want to use another locale, then replace `en-US` in the URLs above with any of the following: `en-AU`, `en-CA`, `en-GB`, `da-DK`, `de-DE`, `es-ES`, `fi-FI`, `fr-CA`, `fr-FR`, `it-IT`, `ja-JP,`, `ko-KR`, `nb-NO`, `nl-NL`, `pl-PL`, `pt-BR`, `ru-RU`, `sv-SE`, `tr-TR`, `zh-CN`, `zh-TW` ## Self-hosting Content Preview To serve the Box Content Preview library from your own server, follow these steps. ### 1. Download release Either fork the repository and check out the version you want to serve or download the specific version as a zip. - Check out a specific version with `git checkout v2.106.0`. - Download a specific version as a zip from the [releases](https://github.com/box/box-content-preview/releases) page. ### 2. Install dependencies Install the dependencies and build the library with the following command. ``` yarn install && yarn build:i18n && yarn build:prod ``` ### 3. Serve files Self-serve everything except for the `dev` folder from the `/dist` folder. You must not alter the folder structure and `third-party` needs to be in the same folder as `2.106.0`. For example, if you self-host using a `box-assets` directory, these URLs must be accessible: - `https://cdn.YOUR_SITE.com/box-assets/2.106.0/en-US/preview.js` - `https://cdn.YOUR_SITE.com/box-assets/third-party/text/2.65.0/papaparse.min.js` - `https://cdn.YOUR_SITE.com/box-assets/third-party/model3d/1.12.0/three.min.js` ## Authentication In order to initialize any of the UI Elements, an application will need to provide a valid Access Token. Learn how to authenticate an application It is also recommended to [downscope](g://authentication/tokens/downscope) an Access Token before passing it into an insecure environment (the user's web browser). UI Elements only expect any Access Token to be passed in for authentication, and can therefore be used for any type of authentication available from Box Platform. For testing purposes, a [Developer Token](g://authentication/tokens/developer-tokens) can be used. ## CORS To use UI Elements, an application needs to allow the domain the widget is used on for Cross Origin Resource sharing. See the [CORS guide](g://security/cors) for more details. ## Source Code & Releases Source code for Box UI Elements is [hosted on GitHub](https://github.com/box/box-ui-elements). The repository contains detailed documentation for usage and development. Please file any bugs you encounter under the "Issues" tab with clear steps to reproduce. This repository also holds a list of [releases](https://github.com/box/box-ui-elements/releases). **Reference:** https://developer.box.com/guides/embed/ui-elements/installation/ --- ## Untitled *Type: guide | Category: Embed Box * Content Open With We no longer support the OpenWith UI element for any new customers as of December 21, 2021. The Box Content Open With UI… # Content Open With We no longer support the `OpenWith` UI element for any new customers as of December 21, 2021. The Box Content Open With UI Element allows developers to embed a dropdown to open content stored in box with a partner application, or locally on the desktop. The Element fetches information about enabled integrations using the Box API, and calls out to partner services. Users can then take action in these services, and the edited content will be automatically saved back to Box. The integrations included in the Open With Element are Google Suite, and Box Edit. Additional information on the Google Suite integration can be found on the [Box Community site](https://support.box.com/hc/en-us/articles/360043696994-Introducing-Box-for-G-Suite). Currently, the element only supports [App Users](page://platform/user-types) for authentication. ## Installation [Learn how to install](g://embed/ui-elements/installation) Box UI elements either through NPM or the Box CDN. ## Box Edit Box Edit requires additional setup in order to be integrated into a custom application. Box Edit uses the desktop application [Box Tools](https://support.box.com/hc/en-us/categories/360003188014-Box-Tools) in order to open content locally. - Requests must use a secure connection (from an `https` domain) - The application's domain must be allowed by Box Tools. Instructions can be found [here](g://embed/ui-elements/custom-domains). The ideal workflow is to bundle these steps within an installer that also installs Box Tools. - Safari requires a browser extension to connect to box tools. More details can be found [here](https://support.box.com/hc/en-us/articles/360043697334-Installing-Box-Tools). ## G Suite A valid G Suite account is required in order to use the Box for G Suite integration. To connect a user's G Suite and Box account, the `external_app_user_id` of the app user must be updated to be the email address associated with the user's G Suite account. The `external_app_user_id` of an app user can be updated via the [`PUT /users/:id`](e://put-users-id) endpoint. ## Setup The Open With UI Element is intended to be used after allowing your application and enabling integrations for app users using Box API endpoints. ### Activate application The 'Open With' UI Element is available to any developer building with the Box Platform. To activate it for your instance, add the "Enable integrations" scope to your application in the developer console. Once your application has been activated for API calls it will need to be reauthorized in your enterprise. The steps for performing these actions are available [here](g://authorization/custom-app-approval). ## List available integrations The first step to assigning an app integration to a user is to get the list of integrations that are available. This `GET` request can be made with the following `cURL` request. ``` curl -X GET https://api.box.com/2.0/app_integrations \ -H 'Authorization: Bearer [ACCESS_TOKEN]' ``` ``` { "next_marker": null, "entries": [ { "type": "app_integration", "id": "10897" }, { "type": "app_integration", "id": "1338" }, { "type": "app_integration", "id": "13418" }, { "type": "app_integration", "id": "3282" } ], "limit": 100 } ``` The app integration IDs are used to assign an integration to a given user. ## Get a specific integration To obtain additional information about a specific integration, based on ID, the following GET request may be made. ``` curl -X GET \ https://api.box.com/2.0/app_integrations/[APP_INTEGRATION_ID] \ -H 'Authorization: Bearer [ACCESS_TOKEN]' ``` ``` { "type": "app_integration", "id": "10897", "app": { "type": "app", "id": "336417" }, "name": "Edit with G Suite", "description": "Securely manage your Google Docs, Sheets and Slides in Box", "executable_item_types": [ "file" ], "restricted_extensions": [ "docx", "gdoc", "xlsx", "gsheet", "pptx", "gslides", "gslide" ], "scoped_to": "parent" } ``` ## Add integration to user To add an app integration to a valid app user, three pieces of information are required: - A valid [Service Account](page://platform/user-types/#service-account) Access Token. - The ID of the app user to be assigned the integration - The ID of the app integration to assign to the user While the two previous requests to get app integration information can be done with any valid token including a valid developer token, adding and removing app integrations requires a valid service account's access token. Using a developer token will produce a `404 Not Found` error. The following `POST` request can be made to assign an app integration to an app user: ``` curl -X POST https://api.box.com/2.0/app_integration_assignments \ -H 'Authorization: Bearer [SERVICE_ACCOUNT_TOKEN]' \ -d '{ "assignee": { "type": "user", "id": "[APP_USER_ID]" }, "app_integration": { "type": "app_integration", "id": "[APP_INTEGRATION_ID]" } }' ``` ``` { "type": "app_integration_assignment", "id": "72048301", "assignee": { "type": "user", "id": "6084519920" }, "app_integration": { "type": "app_integration", "id": "3282" } } ``` The ID in the JSON response can be used to manage app integrations after assignment, and should be stored by the application. ## Remove integration from user To remove an app integration from an app user, the following request may be made with a valid service access token and the app integration assignment ID from the previous step. ``` curl -X DELETE https://api.box.com/2.0/app_integration_assignments/[APP_INTEGRATION_ASSIGNMENT_ID] \ -H 'Authorization: Bearer [SERVICE_ACCOUNT_TOKEN]' ``` ``` 204 No Content ``` ## Sample HTML ``` <!DOCTYPE html> <html lang="en-US"> <head> <meta charset="utf-8" /> <title>Box Content Open With Demo</title> <!-- Latest version of the open with css for your locale --> <link rel="stylesheet" href="https://cdn01.boxcdn.net/platform/elements/{VERSION}/en-US/openwith.css" /> </head> <body> <div class="container" style="height:600px"></div> <!-- Latest version of the open with js for your locale --> <script src="https://cdn01.boxcdn.net/platform/elements/{VERSION}/en-US/openwith.js"></script> <script> var fileId = "123"; var accessToken = "abc"; var contentOpenWith = new Box.ContentOpenWith(); contentOpenWith.show(fileId, accessToken, { container: ".container" }); </script> </body> </html> ``` ## Demo ### Open With Example ### Content Explorer + Open With Example # Access Token These demos may not fully function until you provide a valid access token under the JS tab of the demo. For the Open With element, you must provide a valid [Service Account](page://platform/user-types/#service-account) Access Token. ## Authentication The UI Elements are designed in an authentication agnostic way so whether you are using UI Elements for users who have Box accounts (Managed Users) or non-Box accounts (App Users), UI Elements should work out of the box. The reason for this is that UI Elements only expect a "token" to be passed in for authentication, and Box provides two different ways to generate tokens - OAuth and JWT. Learn about selecting an authentication method ## Scopes To run integrations with downscoped tokens, you must include the `item_execute_integration` scope as well as the scope required by the specific integration you would like to use. - **Google**: `item_readwrite` on the parent folder - **Adobe**: `root_readwrite` - **Box Edit**: `item_readwrite` on the parent folder. - **Box Edit Single File Collaboration**: `item_readwrite` on the file. More information on scopes can be found [here](guide://api-calls/permissions-and-errors/scopes). ## API ``` const { ContentOpenWith } = Box; const contentOpenWith = new ContentOpenWith(); /** * Shows the content open with element. * * @param {string} fileId - The root file id * @param {string} accessToken - Box API access token * @param {Object} [options] - Options * @return {void} */ contentOpenWith.show(fileId, accessToken, options); /** * Hides the content open with element, removes all event listeners, * and clears out the * HTML. * * @return {void} */ openWith.hide(); /** * Adds an event listener to the content open with element. Listeners should be added * before calling show() so no events are missed. * * @param {string} eventName - Name of the event * @param {Function} listener - Callback function * @return {void} */ contentOpenWith.addListener(eventName, listener); /** * Removes an event listener from the content open with element. * * @param {string} eventName - Name of the event * @param {Function} listener - Callback function * @return {void} */ contentOpenWith.removeListener(eventName, listener); /** * Removes all event listeners from the content open with element. * * @return {void} */ contentOpenWith.removeAllListeners(); ``` ### Parameters | Parameter | Type | Description | | --- | --- | --- | | fileId | String | Box File ID. This will be the ID of the file for which you would like to execute integrations. | | accessToken | String | Box API access token to use. This should have read/write access to the folder above. The value passed in for the token is assumed to never expire while the explorer is visible. | | options | Object | Optional options. See below for details. For example: contentExplorer.show(FOLDER_ID, TOKEN, {canDelete: false}) would be used to hide the delete option. | ### Options | Parameter | Type | Description | | --- | --- | --- | | dropdownAlignment | `left | right` | | boxToolsName | String | This string will replace the name of Box Tools in the "Install Box Tools to open this file on your desktop" message. | | boxToolsInstallUrl | String | This URL will be used instead of the default Box installation instructions which are linked in the "Install Box Tools to open this file on your desktop" message. | | onExecute | Function | A callback that executes when an integration invocation is attempted. | | onError | Function | A callback that executes when an error occurs. | | requestInterceptor | Function | Function to intercept requests. For an example see this CodePen. Our underlying XHR library is axios.js and we follow a similar approach for interceptors. | | responseInterceptor | Function | Function to intercept responses. For an example see this CodePen. Our underlying XHR library is axios.js and we follow a similar approach for interceptors. | Currently the `onError` and `onExecute` events are subject to a known bug. We recommend using `openWith.addListener('execute', callback)` and `openWith.addListener('error', callback)` as a temporary workaround. ### Events | Event Name | Event Data | Description | | --- | --- | --- | | execute | Integration ID | Will be fired when an integration invocation is executed. | | error | Error | Will be fired when an error occurs. | **Reference:** https://developer.box.com/guides/embed/ui-elements/open-with/ --- ## Untitled *Type: guide | Category: Embed Box * Theming & styling for Box UI Elements With theming & styling for Box UI Elements you can customize the look of the embedded Box component to… # Theming & styling for Box UI Elements With theming & styling for Box UI Elements you can customize the look of the embedded Box component to your enterprise's requirements. As of now, [theming & styling](https://medium.com/@stefaniuk.olga/b4a86518d5ca) is available for Content Explorer and Content Uploader. ## How to start Add the selected Box component to your application. The default Box theme is applied. ## Customization Box uses [design tokens](g://embed/ui-elements/ui-elements-design-tokens) for the customization purposes. Design tokens are named entities that store specific visual attributes, such as colors, spacing, typography, scale, and many more. They are used instead of hard-coded values to simplify sharing design properties across platforms, tools, and components. You can change the following elements in your embedded Box component. ### Colors Customize: - global color palette - select the primary, secondary, and accent colors - state colors - select color for states such as hover, focus, active, disabled - component-specific colors - for example, you can select different colors for your buttons depending on their type - gradients - choose and adjust a gradient as your background or other components - opacity - adjust the opacity of the overlay effects for Box Modals and side panels ### Typography Customize: - text decoration - underline, strikethrough, letter spacing - line heights and paragraph spacing ### Border, radius Choose: - border styling - width, styles, and colors - border radius ### Spacing Choose: - global spacing - component-specific spacing - component-specific alignment and distribution, for example justify-content ### Shadows and elevation Define: - shadow presets - custom shadows ### Interactive states Customize: - hover, active, focus, disabled, and error states - visual effect such as changed background color - transitions and animations ### Component-level overrides Customize text input, drop down, and checkboxes with individual color ### Icons Style interactive icons such as buttons or drop downs. To replace non-interactive icons such as file or folder icons with your custom ones, use inline SVGs in CSS. To change non-interactive icons color, height, and width, use CSS. See the implemented examples of custom icons in CodePen: ### Miscellaneous Style: - help text and labels - tool tips ## Demo **Reference:** https://developer.box.com/guides/embed/ui-elements/theming-styling/ --- ## Untitled *Type: guide | Category: Embed Box * Content Sidebar The Box Content Sidebar UI Element allows developers to add support for viewing file related metadata (incl. Box Skills) and… # Content Sidebar The Box Content Sidebar UI Element allows developers to add support for viewing file related metadata (incl. Box Skills) and Activity Feed (incl. versions, comments and tasks) in their desktop or mobile web application. ## Installation [Learn how to install](g://embed/ui-elements/installation) Box UI elements either through NPM or the Box CDN. ## Authentication The UI Elements are designed in an authentication agnostic way so whether you are using UI Elements for users who have Box accounts (Managed Users) or non-Box accounts (App Users), UI Elements should work out of the box. The reason for this is that UI Elements only expect a "token" to be passed in for authentication, and Box provides two different ways to generate tokens - OAuth and JWT. Learn about selecting an authentication method ## Sample HTML ``` <!DOCTYPE html> <html lang="en-US"> <head> <meta charset="utf-8" /> <title>Box Content Sidebar</title> <!-- Latest version of the sidebar css for your locale --> <link rel="stylesheet" href="https://cdn01.boxcdn.net/platform/elements/{VERSION}/en-US/sidebar.css" /> </head> <body> <div class="container" style="height:600px"></div> <!-- Latest version of the sidebar js for your locale --> <script src="https://cdn01.boxcdn.net/platform/elements/{VERSION}/en-US/sidebar.js"></script> <script> var fileId = "123"; var accessToken = "abc"; // At least one of the sub-sidebars (details, activity feed, skills, metadata) need to be turned on for something to render. Details is iteself further divided into notices, properties, access stats and versions, one of which need to be enabled for the details sidebar to render. var sidebar = new Box.ContentSidebar(); sidebar.show(fileId, accessToken, { container: ".container", detailsSidebarProps: { hasNotices: true, hasProperties: true, hasAccessStats: true, hasVersions: true }, hasActivityFeed: true, hasSkills: true, hasMetadata: true }); </script> </body> </html> ``` ## Demo ### Stand-alone Sidebar ### Sidebar with Content Preview ### Sidebar with Content Explorer ## API ``` const { ContentSidebar } = Box; const sidebar = new ContentSidebar(); /** * Shows the file selection. * * @public * @param {String} fileId - The file id. * @param {String} token - The API access token. * @param {Object|void} [options] - Optional options. * @return {void} */ sidebar.show(fileId, token, options); /** * Hides the sidebar, removes all event listeners, and clears out the HTML. * * @public * @return {void} */ sidebar.hide(); /** * Clears out the internal in-memory cache for the sidebar. This forces * items to be reloaded from the API. * * @public * @return {void} */ sidebar.clearCache(); /** * Adds an event listener to the sidebar. Listeners should be added before * calling show() so no events are missed. * * @public * @param {String} eventName - Name of the event. * @param {Function} listener - Callback function. * @return {void} */ sidebar.addListener(eventName, listener); /** * Removes an event listener from the sidebar. * * @public * @param {String} eventName - Name of the event. * @param {Function} listener - Callback function. * @return {void} */ sidebar.removeListener(eventName, listener); /** * Removes all event listeners from the sidebar. * * @public * @return {void} */ sidebar.removeAllListeners(); ``` ### Parameters | Parameter | Type | Description | | --- | --- | --- | | fileId | String | Box File ID. This will be the ID of the file for which you want the sidebar. | | token | String | Box API access token to use. It can have read/write access to the file above. The value passed in for the token is assumed to never expire while the sidebar is visible. | | options | Object | Additional options. For example, sidebar.show(FILE_ID, TOKEN, {hasProperties: true}) would be used to show file properties in the sidebar. | ### Options | Parameter | Type | Default | Description | | --- | --- | --- | --- | | container | String | document.body | CSS selector of the container in which the content sidebar should be placed. Calling hide() will clear out this container. | | hasActivityFeed | Boolean | false | Set to true to show the activity feed that includes versions, comments and tasks. | | hasMetadata | Boolean | false | Set to true to show box metadata for the file. | | hasSkills | Boolean | false | Set to true to show the file skills data. | | detailsSidebarProps | Object | {} | See section below. | | requestInterceptor | Function | | Function to intercept requests. For an example see this CodePen. Our underlying XHR library is axios.js and we follow a similar approach for interceptors. | | responseInterceptor | Function | | Function to intercept responses. For an example see this CodePen. Our underlying XHR library is axios.js and we follow a similar approach for interceptors. | ### detailsSidebarProps | Parameter | Type | Default | Description | | --- | --- | --- | --- | | hasProperties | Boolean | false | Set to true to show file properties in the details sidebar. | | hasAccessStats | Boolean | false | Set to true to show file access stats in the details sidebar. | | hasVersions | Boolean | false | Set to true to show file versions in the details sidebar. | | hasNotices | Boolean | false | Set to true to show file related notices in the details sidebar. | ## Scopes If your application requires the end user to only be able to access a subset of the Content Sidebar's functionality, you can use [Downscoping](guide://authentication/tokens/downscope) to appropriately downscope the Access Token to a resulting token that has the desired set of permissions, and can thus, be securely passed to the end user client initializing the Content Sidebar. Below are a set of UI Element-specific scopes to go alongside Downscoping. These allow developers to enable/disable UI controls on the Content Sidebar by configuring the appropriate scopes on the downscoped token. To learn more, see [Dedicated Scopes for Box UI Elements](guide://api-calls/permissions-and-errors/scopes). ### Base Scope | Scope Name | Permissions granted | | --- | --- | | base_sidebar | Allows the user to get basic file info needed for the sidebar. | ### Feature Scopes | Scope Name | Permissions granted | | --- | --- | | item_comment | Allows adding and editing comments. | | item_rename | Allows editing of file description. | | item_upload | Allows editing of file metadata. | | item_task | Allows creating and resolving of tasks. | **Reference:** https://developer.box.com/guides/embed/ui-elements/sidebar/ --- ## Untitled *Type: guide | Category: Embed Box * Content Picker The Box Content Picker UI Element allows developers to add support for selecting files and folders from Box in their desktop… # Content Picker The Box Content Picker UI Element allows developers to add support for selecting files and folders from Box in their desktop or mobile web application. The library fetches information about a specified folder through the Box API and renders the content in a folder view. Users can select files or folders and this content information is then passed to another part of the application. ## Installation [Learn how to install](g://embed/ui-elements/installation) Box UI elements either through NPM or the Box CDN. ## Authentication The UI Elements are designed in an authentication agnostic way so whether you are using UI Elements for users who have Box accounts (Managed Users) or non-Box accounts (App Users), UI Elements should work out of the box. The reason for this is that UI Elements only expect a "token" to be passed in for authentication, and Box provides two different ways to generate tokens - OAuth and JWT. Learn about selecting an authentication method ## Sample HTML ``` <!DOCTYPE html> <html lang="en-US"> <head> <meta charset="utf-8" /> <title>Box File Selection</title> <!-- Latest version of the picker css for your locale --> <link rel="stylesheet" href="https://cdn01.boxcdn.net/platform/elements/{VERSION}/en-US/picker.css" /> </head> <body> <div class="container" style="height:600px"></div> <!-- Latest version of the picker js for your locale --> <script src="https://cdn01.boxcdn.net/platform/elements/{VERSION}/en-US/picker.js"></script> <script> var folderId = "123"; var accessToken = "abc"; var filePicker = new Box.FilePicker(); filePicker.show(folderId, accessToken, { container: ".container" }); </script> </body> </html> ``` ## Demo ### File Selection Demo ### Folder Selection Demo ### File Selection + Preview Demo ### File Selection as a popup # Access Token These demos may not fully function until you provide a valid access token. For testing purposes, you can use your temporary developer token. This will need to be updated under the JS tab in the demo. ## API ``` const { FilePicker } = Box; const filePicker = new FilePicker(); /** * Shows the file selection. * * @public * @param {String} folderId - The root folder id. * @param {String} accessToken - The API access token. * @param {Object|void} [options] - Optional options. * @return {void} */ filePicker.show(folderId, accessToken, options); /** * Hides the file picker, removes all event listeners, and clears out the HTML. * * @public * @return {void} */ filePicker.hide(); /** * Clears out the internal in-memory cache for the file picker. This forces * items to be reloaded from the API. * * @public * @return {void} */ filePicker.clearCache(); /** * Adds an event listener to the file picker. Listeners should be added before * calling show() so no events are missed. * * @public * @param {String} eventName - Name of the event. * @param {Function} listener - Callback function. * @return {void} */ filePicker.addListener(eventName, listener); /** * Removes an event listener from the file picker. * * @public * @param {String} eventName - Name of the event. * @param {Function} listener - Callback function. * @return {void} */ filePicker.removeListener(eventName, listener); /** * Removes all event listeners from the file picker. * * @public * @return {void} */ filePicker.removeAllListeners(); ``` ### Parameters | Parameter | Type | Description | | --- | --- | --- | | folderId | String | Box Folder ID. This will be the ID of the folder from which you want the content to be picked. If you want to use the Box All Files folder, then use 0 as the folderId. | | accessToken | String | Box API access token to use. This should have read/write access to the folder above. The value passed in for the token is assumed to never expire while the explorer is visible. | | options | Object | Optional options. See below for details. For example: contentExplorer.show(FOLDER_ID, TOKEN, {canDelete: false}) would be used to hide the delete option. | ### Options | Parameter | Type | Default | Description | | --- | --- | --- | --- | | container | String | document.body | CSS selector of the container in which the content picker should be placed. Calling hide() will clear out this container. | | sortBy | String | name | The initial sort by option for the content list. Value should be either name or date. | | sortDirection | String | ASC | The initial sort direction option for the content list. Value should be either ASC or DESC. | | logoUrl | String | | URL of custom logo to show in header. If this value is the string box then the box logo will show. | | extensions | Array<string> | [] | Array of file extensions to be allowed for selection. for example ['doc', 'ppt']. Applicable only to the File Picker and not the Folder Picker. If any extensions are provided, only those will have the ability to be selected. An empty array signifies all the file extensions are allowed for selection. | | maxSelectable | Number | Infinity | Number of files or folders that can be selected. Specify 1 if you want only 1 file or folder selected. | | canUpload | Boolean | true | Visually hides the upload option if this is set to false. Hiding the option alone will not prevent uploading unless the current folder permissions also set can_upload to false. This option has no effect when the folder permission can_upload is set to false. | | canSetShareAccess | Boolean | true | Visually hides the sharing drop down select if set to false. Hiding the select drop down alone will not prevent changing the share access unless the folder item permissions also set can_set_share_access to false. This option has no effect when the folder item permission can_set_share_access is set to false. | | canCreateNewFolder | Boolean | true | Visually hides the create new folder option. Hiding the option alone will not prevent creating a new folder unless the folder item permissions also set can_upload to false. This option has no effect when the folder item permission can_upload is set to false. | | sharedLink | String | | Shared link URL, required if folder is shared and the access token doesn't belong to an owner or collaborator of the file. | | sharedLinkPassword | String | | Shared link password, required if shared link has a password. | | modal | Object | | When the modal attribute is specified, then the content pickers will not be created in-place. Instead a button will be created in the container and clicking this button will launch the content picker in a modal popup. | | size | String | undefined | Indicates to the content picker to fit within a small or large width container. Value can be absent or one of small or large. If absent the UI Element will adapt to its container and automatically switch between small width or large width mode. | | isTouch | Boolean | Defaults to the browser and device's default touch support | Indicates to the content explorer that it's is being rendered on a touch enabled device. | | autoFocus | Boolean | false | When set to true, the item grid will get focus on initial load. | | defaultView | String | files | Value should either be files or recents. When set to recents, by default you will see recent items instead of the regular file/folder structure. | | chooseButtonLabel | String | | String to re-label the Choose button | | cancelButtonLabel | String | | String to re-label the Cancel button | | requestInterceptor | Function | | Function to intercept requests. For an example see this CodePen. Our underlying XHR library is axios.js and we follow a similar approach for interceptors. | | responseInterceptor | Function | | Function to intercept responses. For an example see this CodePen. Our underlying XHR library is axios.js and we follow a similar approach for interceptors. | ### Modal Options | Parameter | Type | Default | Description | | --- | --- | --- | --- | | buttonLabel | String | | Label for the button | | buttonClassName | String | Box Blue Button | CSS class to decorate the button | | modalClassName | String | | CSS class to decorate the modal popup content | | overlayClassName | String | | CSS class to decorate the modal popup overlay | ### Events | Event Name | Event Data | Description | | --- | --- | --- | | choose | Array<File | Web Link | Folder> | Will be fired when the Choose button is pressed. Event data will be an array of Folder Object or File Object or Web Link object depending upon whether it was a file selection or folder selection. | | cancel | | Will be fired when the Cancel button is pressed | ## Keyboard Shortcuts When the item grid has focus, either manually by clicking on it or programmatically via javascript or via the above mentioned `autoFocus` prop, the following keyboard shortcuts will work if their corresponding operations are applicable and allowed. | Key | Action | | --- | --- | | Arrow Up | Previous item row | | Arrow Down | Next item row | | Ctrl/Cmd + Arrow Up | First item row | | Ctrl/Cmd + Arrow Down | Last item row | | / | Search | | Shift + X | Select an item row | | Enter | Open the selected item | | g then f | Navigates to the root folder | | g then u | Upload to the current folder | | g then b | Focuses the root folder breadcrumb | | g then s | Shows the selected items | | g then x | Cancel | | g then c | Choose | | g then r | Recent items | ## Scopes If your application requires the end user to only be able to access a subset of the Content Picker functionality, you can use [Downscoping](guide://authentication/tokens/downscope) to appropriately downscope the Access Token to a resulting token that has the desired set of permissions, and can thus, be securely passed to the end user client initializing the Content Picker. Below are a set of UI Element-specific scopes to go alongside Downscoping. These allow developers to enable/disable UI controls on the Content Picker by configuring the appropriate scopes on the downscoped token. To learn more, see [Dedicated Scopes for Box UI Elements](guide://api-calls/permissions-and-errors/scopes). | Scope Name | Permissions granted | | --- | --- | | base_picker | Allows access to content in the folder tree based on user/file/token permissions | ### Feature Scopes | Scope Name | Permissions granted | | --- | --- | | item_share | Allows sharing of resource specified under "resource" of the Token Exchange request. | | item_upload | Allows upload in the content picker | ### Sample Scenarios | Scenario | Scopes | | --- | --- | | User wants to only navigate a folder structure and pick a file / folder | base_picker | | User wants to navigate a folder structure, pick a file / folder and also set access level | base_picker + item_share | | User wants to navigate a folder structure, pick a file / folder and also upload a file / folder | base_picker + item_upload | | User should be able to navigate a folder structure and pick a file / folder, upload a file / folder, and also set access level for a file/folder | base_picker + item_share + item_upload | ## Custom actions You can expand the actions in the **More Options** menu for files and folders in Content Explorer and Content Picker. Your custom options show when user clicks the ellipsis button. To customize the **More Options** menu, pass an array of custom actions to `itemActions`. ``` contentExplorer.show(configData.FOLDER_ID, configData.ACCESS_TOKEN, { container: ".container", itemActions: customActions, }); ``` The array can include multiple actions. The action object should include the `label`, and the `onAction` callback function. You can filter the custom actions to appear only on a specific item `type`, by passing the `file` or `folder` value. The `filter` value is used for advanced filtering, for example by a specific file extension: ``` const customActions = [ { label: "Preview in New Window", onAction: (item) => alert('action ' + item), type: 'file', }, { label: "Open in Box.com", onAction: (item) => window.open("https://app.box.com"), }, { label: "Export", onAction: (item) => console.log('action ' + item), filter: (item) => item.extension?.toLowerCase() === 'pdf', }, ]; ``` See the implemented examples in CodePen: **Reference:** https://developer.box.com/guides/embed/ui-elements/picker/ --- ## Untitled *Type: guide | Category: Embed Box * Dedicated Scopes When working with Box UI Elements many developers have shown interest in being able to implement their own permission model… # Dedicated Scopes When working with Box UI Elements many developers have shown interest in being able to implement their own permission model, different from Access Levels defined by Box. [Downscoping](g://authentication/tokens/downscope), also known as Token Exchange, is our mechanism by which developers can restrict the permissions on the Access Token to a more granular level, allowing them to build out their own permissions model on Box Platform. ## Scopes & UI Elements To facilitate this process we've created a new set of API scopes that allow developers to control which UI controls are available to end users in applications leveraging UI Element. The Box UI Element has been designed to respect the permission enforced via these scopes, so UI controls are turned on or off automatically depending on whether or not the token allows the corresponding functionality or not. Another advantage of these new scopes is that since the tokens are scoped to the precise set of operations that the application wants the end user to have access to, a savvy end user won't have any more access directly through the API using the token. This allows you to make your application more secure. ## Scope principles The new scopes have been designed keeping the following fundamentals in mind: - **All scopes should be modular and strictly additive:** So a developer can combine multiple scopes in a token exchange request to generate a token with the desired set of functionality. Also to avoid confusion, two scopes should never have the same permission. - **Scopes should directly map to a specific action in the UI Element:** So adding this scope to the token enables the specific action. Since some actions can only be performed by specific UI Elements, some scopes may only be meaningfully applicable to those UI Elements. - **Scope should contain the minimum set of permissions required to perform the corresponding action:** So even if some end-user of your application uses the token directly against the API, they can't gain access beyond what you intended them to provide through the UI Element. - **Every UI Element should have a "base scope" that encapsulates all required permissions for the UI Element to functionally work:** Any fewer permissions on the token and the basic set of operations on the UI Element won't work. This scope should always be included in the Token Exchange request. With that said, for every UI Element, we are adding two types of scopes: ### Base Scope Every UI Element has a "base scope", which provides the least set of permissions that are necessary for the UI Element to functionally work. For this reason, this "base scope" must always be present in the token exchange request. Since the base set of permissions for every UI Element to functionally work may not be the same (Content Uploader UI Element doesn't require preview permissions, for example), UI Elements may have their own respective base scopes. ### Feature Scopes In addition to the base scope, we also introduced feature-level scopes. Upon pairing with base scopes, feature scopes enable additional capabilities within your UI Element, and provide the user with permissions to perform the actions prescribed by the feature scope(s) added to their down-scoped token. As with base scopes, feature scopes too are mostly different for every UI Element. Since feature scopes are strictly additive, you can assume (unless noted otherwise in the documentation) that providing a user with access to a scope only gives them permission to perform the corresponding action and nothing else. Now that you understand what Base Scopes and Feature Scopes are, refer to the documentation for each of the UI Elements to learn more about their dedicated scopes. **Reference:** https://developer.box.com/guides/embed/ui-elements/scopes/ --- ## Untitled *Type: guide | Category: Embed Box * Content Preview The Box Content Preview UI Element allows developers to embed high quality and interactive previews of Box files in their… # Content Preview The Box Content Preview UI Element allows developers to embed high quality and interactive previews of Box files in their desktop or mobile web application. ## Content Preview Element vs Content Preview Library The Content Preview UI Element works differently from the other UI Elements, as the React component is a wrapper for the [Box Content Preview library](https://github.com/box/box-content-preview). It also requires passing a language (defaults to `en-US`) since the preview library bundles are localized. ``` var ContentPreview = require("./ContentPreview").default; <IntlProvider locale="en"> <ContentPreview contentSidebarProps={{ detailsSidebarProps: { hasAccessStats: true, hasClassification: true, hasNotices: true, hasProperties: true, hasRetentionPolicy: true, hasVersions: true, }, features: FEATURES, hasActivityFeed: true, hasMetadata: true, hasSkills: true, hasVersions: true, }} hasHeader={true} features={FEATURES} fileId={FILE_ID} token={TOKEN} {...PROPS} /> </IntlProvider>; ``` The Content Preview Library fetches information about the file and its converted representations through the Box API, chooses the appropriate viewer for the file type, dynamically loads the necessary static assets and file representations, and finally renders the file. This UI Element also allows loading previews of multiple files in the same container and exposes arrows to navigate between those files. It powers Preview in the main Box web application as well as the [expiring embed link object](r://file--full/#param-expiring_embed_link). ## Installation [Learn how to install](g://embed/ui-elements/installation) Box UI elements either through NPM or the Box CDN. ## Authentication UI Elements are designed in an authentication-agnostic way so whether they will work for Managed Box Users and non-Box users (App Users). The reason for this is that UI Elements only expect a [token](g://authentication/tokens/developer-tokens) for authentication, and Box provides two different ways to generate tokens - OAuth and JWT. Learn about selecting an authentication method ## Supported File Types Box Content Preview supports 120+ file types, including most document and image formats, HD video, 3D models, 360-degree images, and 360-degree videos. You can find the full list of supported file types [here](https://support.box.com/hc/en-us/articles/360043695794-Viewing-Different-File-Types-Supported-in-Box-Content-Preview). Note that where supported file types contain references to other objects, for example `DWG` files, those references are not supported in the Box preview. A notice will be displayed to all end users viewing a DWG file that contains unsupported references letting them know to take alternate steps to complete their workflow. ## Demo Use the navigation arrows to preview different file types. ## API ``` const { Preview } = Box; const preview = new Preview(); /** * Shows a preview. * * @public * @param {string} fileId - File ID to preview * @param {string} accessToken - Box API access token * @param {Object} [options] - Options * @return {void} */ preview.show(fileId, accessToken, options); /** * Hides the preview. * * @return {void} */ preview.hide(); /** * Prints the current file, if printable. * * @return {void} */ preview.print(); /** * Downloads the current file. * * @return {void} */ preview.download(); /** * Resizes the current preview, if applicable. This function only needs to * be called when preview's viewport has changed while the window has not. * If the window is resizing, then preview will automatically resize itself. * * @return {void} */ preview.resize(); /** * Adds an event listener to the preview. Listeners should be added * before calling show() so no events are missed. * * @param {string} eventName - Name of the event * @param {Function} listener - Callback function * @return {void} */ preview.addListener(); /** * Removes an event listener from the preview. * * @param {string} eventName - Name of the event * @param {Function} listener - Callback function * @return {void} */ preview.removeListener(eventName, listener); /** * Removes all event listeners from the preview. * * @return {void} */ preview.removeAllListeners(); ``` ### Parameters | Parameter | Type | Description | | --- | --- | --- | | fileId | String | Box File ID. | | accessToken | String | Box API access token to use. This should have read/write access to the folder above. The value passed in for the token is assumed to never expire while the explorer is visible. | | options | Object | Optional options. See below for details. For example: contentExplorer.show(FOLDER_ID, TOKEN, {canDelete: false}) would be used to hide the delete option. | ### Options | Parameter | Type | Default | Description | | --- | --- | --- | --- | | container | String | document.body | CSS selector of the container in which Preview should be placed | | sharedLink | String | | Shared link URL, required if file is shared and the access token doesn't belong to an owner or collaborator of the file | | sharedLinkPassword | String | | Shared link password, required if shared link has a password | | collection | Array | | List of file IDs to preview over. When used, this will show previews of multiple files within the same container and expose arrows to navigate between those files. Note that FILE_ID needs to be included in this list and that the SDK doesn't support collections with files that require a shared link or password. | | header | String | light | Values that control header visibility and background color. Use none for no header, light for a light header and background, and dark for a dark header and background | | logoUrl | String | | URL of custom logo to show in header. If this value is the string box then the box logo will show | | showAnnotations | Boolean | false | Whether annotation button in header and annotations on content are shown | | showDownload | Boolean | false | Whether download button is shown in header. Will also control print button visibility in viewers that support print. Note that this option will not override download permissions on the access token. | ## Token Generator Function The Preview library optionally takes a token generator function instead of a string token. Using a token generator function allows you to dynamically determine what tokens Preview should use. For example, you can pass in different access tokens for each file or make sure your token is refreshed and valid before showing a preview. The token generator function should return a promise that resolves in either a single string token that applies to all of the files being previewed or a map of typed file IDs to access token for those files. ``` // Example token generator function that resolves to a single access token var singleTokenGenerator = function () { return someApi.getToken().then(function (data) { return data.token; }); }; // Example token generator function that resolves to a map of tokens var mapTokenGenerator = function () { return Promise.resolve({ file_1234: "some_token_abcd", file_2345: "some_token_bcde", }); }; ``` ## Events The preview object exposes `addListener` and `removeListener` for binding to events. Event listeners should be bound before `show()` is called, otherwise events can be missed. ``` const listener = (value) => { // Do something with value }; // Attach listener before calling show otherwise events can be missed var preview = new Box.Preview(); preview.addListener(EVENTNAME, listener); // Show a preview preview.show(...); // Remove listener when needed preview.removeListener(EVENTNAME, listener); ``` `EVENTNAME` can be one of the following: `viewer` event will be triggered when we have the viewer instance first available. This will be the same object that is also a property included in the `load` event. Preview triggers this event before `load` so that clients can attach their listeners before the `load` event is triggered. `load` event will be triggered on every preview load when `show()` is called or if inter-preview navigation occurs. The event data will contain: ``` error: 'message', // Error message if any error occurred while loading viewer: {...}, // Instance of the current viewer object if no error occurred metrics: {...}, // Performance metrics file: {...} // Box file object with properties defined in file.js ``` `navigate` event will be triggered when navigation happens. The event includes the file ID of the file being navigated to, and this event will trigger before `load`. `notification` event will be triggered when either the preview wrapper or one of the viewers wants to notify something like a warning or non-fatal error. The event data will contain: ``` message: 'message', // Message to show type: 'warning' // 'warning', 'notice', or 'error' ``` `viewerevent` Each viewer will trigger its own sets of events. For example, the Image viewer will trigger `rotate` or `resize`, etc. while other viewers may trigger another set of events. The preview wrapper will also re-emit events at the preview level, with event data containing: ``` event: EVENTNAME, // Event name data: DATA, // Event data object viewerName: VIEWERNAME, // Name of the viewer. See VIEWERNAME above fileId: fileId // The file ID ``` ### Example event usage ``` var preview = new Box.Preview(); preview.addListener("viewer", (viewer) => { viewer.addListener("rotate", () => { // Do something when a viewer rotates a preview }); }); preview.addListener("load", (data) => { const viewer = data.viewer; viewer.addListener("rotate", () => { // Do something when a viewer rotates a preview }); }); preview.addListener("viewerevent", (data) => { if (data.viewerName === "Image") { if (data.event === "rotate") { // Do something when an image preview is rotated } } else if (data.viewerName === "Image360") { if (data.event === "rotate") { // Do something different when a 360-degree image is rotated } } else { } }); preview.addListener("rotate", (data) => { if (data.viewerName === "Image") { // Do something when an image preview is rotated } else if (data.viewerName === "Image360") { // Do something different when a 360-degree image is rotated } else { } }); ``` ## Annotations You can enable V4 [annotations](g://embed/ui-elements/annotations.md) in content preview. New annotations will sync in real time. To add V4 annotations to preview: 1. Run `npm i box-annotations@latest`to install [box annotations](https://github.com/box/box-annotations). Box annotations version should be at least major version 4 and up. 1. Run `npm i box-ui-elements@16.0.0` to install [BUIE](https://github.com/box/box-ui-elements/releases/tag/v16.0.0) version with annotation related change. Box UI elements should be the lowest working version that contains fully working V4 annotations. Import content preview and box annotations into your application: ``` import boxAnnotations from "https://cdn.skypack.dev/box-annotations@latest"; var file_id = "YOUR FILE ID"; var accessToken = "YOUR ACCESS TOKEN"; /* Enable annotations in sidebar */ var contentSidebarProps = { hasActivityFeed: true, features: { activityFeed: { annotations: { enabled: true, }, }, }, }; var options = { container: ".previewer", contentSidebarProps: contentSidebarProps, /* Enable annotations in preview */ enableAnnotationsDiscoverability: true, enableAnnotationsImageDiscoverability: true, showAnnotations: true, showAnnotationsControls: true, showAnnotationsDrawingCreate: true, }; /* BoxAnnotations */ var annotations = new BoxAnnotations(); /* Box Preview */ var contentPreviewer = new Box.ContentPreview(); /* Set annotation into previewer */ options["boxAnnotations"] = annotations; /* Show previewer */ contentPreviewer.show(file_id, accessToken, options); ``` The property `features: { activityFeed: { annotations: { enabled: true } } } } ` is subject to change in the future. ``` <link href="https://cdn01.boxcdn.net/platform/elements/16.0.0/en-US/preview.css" rel="stylesheet" type="text/css"></link> <script src="https://cdn01.boxcdn.net/platform/elements/16.0.0/en-US/preview.js"></script> <style> .previewer { border: 1px solid #eee; height: 500px; width: 100%; } </style> <div class="previewer"></div> <script type="module" src="./script.js"></script> ``` ## Box AI for UI Elements Box AI for UI Elements enhances the Content Preview UI Element with additional features, allowing the developers to add the Box Q&A AI functionality to their platform app. Enriched with Box AI features, the Preview UI element brings the following functionality: - Q&A and document summaries. - A **clear conversation** button that resets the conversation with Box AI. - Citations that appear below the answer if included in the answer. - Formatting support that allows requesting Markdown-formatted response, including bullet points or tables. - Question history that allows referencing previous context to achieve the best response possible. The question history is kept only during the current session. - Suggested questions that appear at the top of the chat by default to assist with the conversation. ### Enable Box AI for UI Elements To enable the Box AI modal in content preview header, follow these steps: Make sure your Node and React versions are `18.x` or higher. Download the [npm package that contains Box AI for UI Elements](https://www.npmjs.com/package/box-ui-elements/v/22.0.0) or directly from [Box CDN](g://embed/ui-elements/installation). Install the peer dependencies: - [`box-ai-content-answers`](https://www.npmjs.com/package/@box/box-ai-content-answers) - [`blueprint-web`](https://www.npmjs.com/package/@box/blueprint-web) - [`blueprint-web-assets`](https://www.npmjs.com/package/@box/blueprint-web-assets) To do so, run the following command: ``` npx install-peerdeps box-ui-elements@^22.0.0 ``` ### Using JavaScript To enable Box AI features, pass the following: - `hasHeader` prop set to `true`, - `contentAnswersProps` prop. The fields `show`, `isCitationsEnabled`, `isMarkdownEnabled`, `isResetChatEnabled` and `suggestedQuestions` are included by default. ``` const preview = new Box.Preview(); const suggestedQuestions = [ { label: 'What are the key takeaways?', prompt: 'What are the key takeaways?', id: '1234', }, { label: 'Summarize this document', prompt: 'Summarize this document', id: '5678', }, ]; preview.show(<FILE_ID>, <TOKEN>, { container: '.preview-container', contentAnswersProps={ show: true, isCitationsEnabled: true, isMarkdownEnabled: true, isResetChatEnabled: true, suggestedQuestions } hasHeader: true, }); ``` ### Using React component You can also add Box AI element to a header in a React component. To do so, add: - `hasHeader` prop set to `true`, - `contentAnswersProps` prop. The fields `show`, `isCitationsEnabled`, `isMarkdownEnabled`, `isResetChatEnabled` and `suggestedQuestions` are included by default. To localize the `suggestedQuestions` properly, make sure that the prompts are translated. The optional `label` property is for screen readers, while the `prompt` property is the text displayed to the user in the AI modal. ``` import ContentPreview from 'box-ui-elements/es/elements/content-preview'; import { IntlProvider } from "react-intl"; const suggestedQuestions = [ { label: 'Key takeaways', prompt: 'What are the key takeaways from this document?', id: '1234', }, { label: 'Summarize', prompt: 'Summarize this document', id: '5678', }, ]; export default () => { // Storing variables in the front end is not secure. // You will want to grab this value from a database for production const TOKEN = process.env.REACT_APP_BOX_DEVELOPER_TOKEN const FILE_ID = process.env.REACT_APP_BOX_PREVIEW_FILE_ID return ( <IntlProvider locale="en"> <ContentPreview contentAnswersProps={{ show: true, isCitationsEnabled: true, isMarkdownEnabled: true, isResetChatEnabled: true, suggestedQuestions }} fileId={FILE_ID} token={TOKEN} hasHeader=true /> </IntlProvider> ); }; ``` You can further customize your apps using the following event listeners for Content Preview available with this release: `onAsk`, `onClearConversations`, and `onRequestClose`. ## Scopes If your application requires the end user to only be able to access a subset of the Content Preview functionality, you can use [Downscoping](guide://authentication/tokens/downscope) to appropriately downscope the Access Token to a resulting token that has the desired set of permissions, and can thus, be securely passed to the end user client initializing the Content Preview. Below is a set of UI Element-specific scopes to go alongside Downscoping. These allow developers to enable/disable UI controls on the Content Preview by configuring the appropriate scopes on the downscoped token. To learn more, see [Dedicated Scopes for Box UI Elements](g://api-calls/permissions-and-errors/scopes). ### Base Scope | Scope Name | Permissions granted | | --- | --- | | base_preview | Allows the user to preview the file, nothing else | ### Feature Scopes | Scope Name | Permissions granted | | --- | --- | | item_download | Allows downloading/printing the content from the generated preview | | annotation_edit | Allows users to edit annotations (delete). Note: For highlight annotations to work, the text layer on the document needs to be enabled for the user. Text layer is disabled for all users that don't have download permissions on the file. To enable highlight annotations for a user, please ensure they have download permissions on the file. | | annotation_view_all | Allows users to view all users' annotations. | | annotation_view_self | Allows users to view their own annotations only. | # Enable highlight annotations with scopes The highlight scope is not included with `annotation_edit` and `annotation_view_all` scopes. The downscoped access token will need to include the `item_download` scope to enable highlighting. ### Sample Scenarios | Scenario | Scopes | | --- | --- | | User should only be able to preview (not download/print, annotate) | base_preview | | User should be able to preview, download and print | base_preview + item_download | | User should be able to preview and view all annotations (not download, print or create annotations) | base_preview + annotation_view_all | | User should be able to preview, and create annotations but only view their own. | base_preview + annotation_view_self + annotation_edit | | User should be able to preview, edit annotations and view all annotations | base_preview + annotation_view_all + annotation_edit | | User should be able to preview and only view their own annotations but not add/delete (ex: after review period has expired, all documents need to be stored in read only mode) | base_preview + annotation_view_self | **Reference:** https://developer.box.com/guides/embed/ui-elements/preview/ --- ## Untitled *Type: guide | Category: Embed Box * Content Uploader The Box Content Uploader UI Element allows developers to embed an upload widget in their desktop or mobile web application… # Content Uploader The Box Content Uploader UI Element allows developers to embed an upload widget in their desktop or mobile web application. Users can select files or use drag and drop to upload. Large files will be uploaded with the Chunked Upload API. ## Installation [Learn how to install](g://embed/ui-elements/installation) Box UI elements either through NPM or the Box CDN. ## Authentication The UI Elements are designed in an authentication agnostic way so whether you are using UI Elements for users who have Box accounts (Managed Users) or non-Box accounts (App Users), UI Elements should work out of the box. The reason for this is that UI Elements only expect a "token" to be passed in for authentication, and Box provides two different ways to generate tokens - OAuth and JWT. Learn about selecting an authentication method ## Demo ### Uploader ### Uploader as popup # Access Token These demos may not fully function until you provide a valid access token. For testing purposes, you can use your temporary developer token. This will need to be updated under the JS tab in the demo. ## API ``` const { ContentUploader } = Box; const uploader = new ContentUploader(); /** * Shows the content uploader. * * @public * @param {String} folderId - Folder ID to upload to. * @param {String} accessToken - Box API access token. * @param {Object|void} [options] - Optional options. * @return {void} */ uploader.show(folderId, accessToken, options); /** * Hides and clears HTML for the uploader. * * @public * @return {void} */ uploader.hide(); /** * Adds an event listener to the content uploader. * Listeners should be added before calling show() * so no events are missed. * * @public * @param {String} eventName Name of the event. * @param {Function} listener Callback function. * @return {void} */ uploader.addListener(eventName, listener); /** * Removes an event listener from the content uploader. * * @public * @param {String} eventName Name of the event. * @param {Function} listener Callback function. * @return {void} */ uploader.removeListener(eventName, listener); /** * Removes all event listeners from the content uploader. * * @public * @return {void} */ uploader.removeAllListeners(); ``` ### Parameters | Parameter | Type | Description | | --- | --- | --- | | folderId | String | Box Folder ID. This will be the ID of the folder from which you want files to be uploaded to. If you want to use the Box All Files folder, then use 0 as the folderId. | | accessToken | String | Box API access token to use. This should have upload access to the folder above. | | options | Object | Optional options. See below for details. | ### Options | Parameter | Type | Default | Description | | --- | --- | --- | --- | | container | String | document.body | CSS selector of the container in which the content uploader should be placed. Calling hide() will clear out this container. | | sharedLink | String | | Shared link URL, required if folder is shared and the access token doesn't belong to an owner or collaborator of the file. | | sharedLinkPassword | String | | Shared link password, required if shared link has a password. | | onClose | Function | | Callback function for 'Close' button, which will appear when there are no files to upload or when all uploads are complete. If this option is set to null the button will not appear. | | modal | Object | | When the modal attribute is specified, then the content uploader will not be created in-place. Instead a button will be created in the container and clicking this button will launch the content uploader in a modal popup. See below for the modal options. | | size | String | undefined | Indicates to the content uploader to fit within a small or large width container. Value can be absent or one of small or large. If absent the UI Element will adapt to its container and automatically switch between small width or large width mode. | | isTouch | Boolean | Defaults to the browser and device's default touch support | Indicates to the content explorer that it's is being rendered on a touch enabled device. | | fileLimit | Number | 100 | The maximum number of files that can be uploaded at once. If more than fileLimit files are selected for upload, any beyond the first fileLimit files will not be included for uploaded. A warning message will be shown in the footer when this happens. | | requestInterceptor | Function | | Function to intercept requests. For an example see this CodePen. Our underlying XHR library is axios.js and we follow a similar approach for interceptors. | | responseInterceptor | Function | | Function to intercept responses. For an example see this CodePen. Our underlying XHR library is axios.js and we follow a similar approach for interceptors. | ### Modal Options | Parameter | Type | Default | Description | | --- | --- | --- | --- | | buttonLabel | String | | Label for the button | | buttonClassName | String | Box Blue Button | CSS class to decorate the button | | modalClassName | String | | CSS class to decorate the modal popup content | | overlayClassName | String | | CSS class to decorate the modal popup overlay | ### Events | Event Name | Event Data | Description | | --- | --- | --- | | close | | Will be fired when 'Close' button is clicked. | | complete | Array<File> | Will be fired when all uploads in the current view are complete. Event data will be an array of File Object. | | upload | File | Fired when a single file is successfully uploaded. Event data will be a File Object. | | error | Object | Fired when a single file has an upload error. Event data will be an object with properties file from the File Web API and the error object error. | ## Scopes If your application requires the end user to only be able to access a subset of the Content Uploader functionality, you can use [Downscoping](guide://authentication/tokens/downscope) to appropriately downscope the Access Token to a resulting token that has the desired set of permissions, and can thus, be securely passed to the end user client initializing the Content Uploader. Below are a set of UI Element-specific scopes to go alongside Downscoping. These allow developers to enable/disable UI controls on the Content Uploader by configuring the appropriate scopes on the downscoped token. To learn more, see [Dedicated Scopes for Box UI Elements](guide://api-calls/permissions-and-errors/scopes). ### Base Scope | Scope Name | Permissions granted | | --- | --- | | base_upload | Allows upload into the folder specific under "resource" of the Token Exchange request. | ### Sample Scenarios | Scenario | Scopes | | --- | --- | | User wants to upload files to a Box folder | base_upload | **Reference:** https://developer.box.com/guides/embed/ui-elements/uploader/ --- ## Untitled *Type: guide | Category: Embed Box * UI Elements design tokens This table shows all the design tokens you can use for theming and styling purposes. Provided default values and… # UI Elements design tokens This table shows all the design tokens you can use for [theming and styling](g://embed/ui-elements/theming-styling) purposes. Provided default values and descriptions give clarity and guidance for implementation. ## Token structure There are two ways to provide tokens to the theme object in Content Explorer: flatten structure using full token names and nested structure using shortened names. The latter allows you to group tokens, which might improve the readability of your customization, but both produce the same results. ### Example Flatten structure: ``` const theme = { tokens: { "body-default-font-size": "14px", "body-default-font-weight": "400" "body-default-text-decoration": "none", "body-default-bold-line-height": "20px", } }; ``` Nested structure: ``` const theme = { tokens: { Body: { Default: { "font-size": "14px", "font-weight": "400", "text-decoration": "none", } "Default Bold": { "line-height": "20px", } } } }; ``` ## Border colors You can pass any CSS color value, like hexadecimal colors, RGB, cross-browser color names, and so on. | Token | Default value | Description | | --- | --- | --- | | border-checkbox-border | #6f6f6f | Default border color for checkboxes. | | border-checkbox-border-hover | #4e4e4e | Border color when hovering over checkboxes. | | border-checkbox-border-selected | #0061d5 | Border color for selected checkboxes. | | border-checkbox-border-selected-hover | #2079e3 | Border color when hovering over selected checkboxes. | | border-cta-border-outline | #000000 | Border for secondary buttons with transparent background. | | border-cta-border-outline-disabled | #646464 | Border for disabled outline buttons. | | border-cta-border-outline-hover | #000000 | Border for outline buttons on hover. | | border-cta-border-outline-pressed | #000000 | Border for outline buttons when pressed. | | border-cta-border-secondary | #bcbcbc | Border for secondary buttons. | | border-cta-border-secondary-disabled | #e8e8e8 | Border for disabled secondary buttons. | | border-cta-border-secondary-hover | #bcbcbc | Border for secondary buttons on hover. | | border-cta-border-secondary-pressed | #bcbcbc | Border for secondary buttons when pressed. | | border-divider-border | #e8e8e8 | Horizontal line separators. | | border-dropdown-border | #bcbcbc | Border for dropdown menus. | | border-gridthumbnail-border | #e8e8e8 | Border for item (file/folder) thumbnails in grid view. | | border-input-border | #909090 | Border for text inputs. | | border-input-border-error | #ed3757 | Border for text inputs with errors. | | border-input-border-focus | #2486fc | Border for focused text inputs. | | border-input-border-hover | #6f6f6f | Border for text inputs on hover. | | border-search-border | #f4f4f4 | Border for search inputs. | | border-search-border-hover | #6f6f6f | Border for search inputs on hover. | | border-switch-border | #bcbcbc | Border for toggle switches. | | border-switch-border-hover | #bcbcbc | Border for toggle switches on hover. | | border-tooltip-border-error | #f69bab | Border for error tool tips. | ## Icon colors You can only style interactive icons such as buttons or drop down. | Token | Default value | Description | | --- | --- | --- | | icon-cta-icon | #6f6f6f | Default color for icons in buttons. | | icon-cta-icon-hover | #222222 | Color for icons in buttons on hover. | | icon-cta-icon-pressed | #222222 | Color for icons in buttons when pressed. | ## Outline colors | Token | Default value | Description | | --- | --- | --- | | outline-focus-on-dark | #ffffff | Focus outline color on dark backgrounds. | | outline-focus-on-light | #2486fc | Focus outline color on light backgrounds. | ## Surface colors | Token | Default value | Description | | --- | --- | --- | | surface-checkbox-surface | #ffffff | Background color for checkboxes. | | surface-checkbox-surface-hover | #ffffff | Background color for checkboxes on hover. | | surface-checkbox-surface-selected | #0061d5 | Background color for selected checkboxes. | | surface-checkbox-surface-selected-hover | #2079e3 | Background color for selected checkboxes on hover. | | surface-cta-surface-icon | rgba(0, 0, 0, 0) | Background for icon buttons. | | surface-cta-surface-icon-disabled | rgba(0, 0, 0, 0) | Background for disabled icon buttons. | | surface-cta-surface-icon-hover | rgba(0, 0, 0, 0.04) | Background for icon buttons on hover. | | surface-cta-surface-icon-pressed | rgba(0, 0, 0, 0.08) | Background for icon buttons when pressed. | | surface-cta-surface-outline | rgba(0, 0, 0, 0) | Background for secondary buttons with transparent background. | | surface-cta-surface-outline-hover | rgba(0, 0, 0, 0.04) | Background for outline buttons on hover. | | surface-cta-surface-outline-pressed | rgba(0, 0, 0, 0.08) | Background for outline buttons when pressed. | | surface-cta-surface-secondary | #ffffff | Background for secondary buttons. | | surface-cta-surface-secondary-hover | #f4f4f4 | Background for secondary buttons on hover. | | surface-cta-surface-secondary-pressed | #e8e8e8 | Background for secondary buttons when pressed. | | surface-cta-surface-tertiary | #ffffff | Background for link styled buttons. | | surface-cta-surface-tertiary-hover | #f4f4f4 | Background for link styled buttons on hover. | | surface-cta-surface-tertiary-pressed | #e8e8e8 | Background for link styled buttons when pressed. | | surface-dropdown-surface | #ffffff | Background for dropdown menus. | | surface-dropdown-surface-error | #ffffff | Background for dropdown menus with errors. | | surface-dropdown-surface-focus | #ffffff | Background for focused dropdown menus. | | surface-dropdown-surface-hover | #ffffff | Background for dropdown menus on hover. | | surface-illustration-surface-box-neutral | #0061d5 | Color for illustrations (detailed icons). | | surface-input-surface | #ffffff | Background for text inputs. | | surface-input-surface-error | #ffffff | Background for text inputs with errors. | | surface-input-surface-focus | #ffffff | Background for focused text inputs. | | surface-input-surface-hover | #ffffff | Background for text inputs on hover. | | surface-menu-surface | #ffffff | Background for dropdown menu options. | | surface-menu-surface-focus | #f4f4f4 | Background for focused menu items. | | surface-menu-surface-hover | #f4f4f4 | Background for menu items on hover. | | surface-search-surface | #f4f4f4 | Background for search inputs. | | surface-search-surface-focused | #ffffff | Background for focused search inputs. | | surface-search-surface-hover | #fbfbfb | Background for search inputs on hover. | | surface-sliderthumb-surface | #0061d5 | Color for range slider thumbs. | | surface-sliderthumb-surface-hover | #2486fc | Color for range slider thumbs on hover. | | slidertrack-surface | #6f6f6f | Color for range slider tracks. | | surface-surface | #ffffff | General background color. | | surface-surface-brand | #0061d5 | Background for primary buttons. | | surface-surface-brand-disabled | #0061d5 | Background for disabled primary buttons. | | surface-surface-brand-hover | #006ae9 | Background for primary buttons on hover. | | surface-surface-brand-pressed | #004eac | Background for primary buttons when pressed. | | surface-switch-surface | #ffffff | Background for toggle switches. | | surface-switch-surface-off | #d3d3d3 | Background for toggle switches in off state. | | surface-switch-surface-on | #0061d5 | Background for toggle switches in on state. | | surface-tooltip-surface | #4e4e4e | Background for tool tips. | | surface-tooltip-surface-error | #fdebee | Background for error tool tips. | ## Text colors | Token | Default value | Description | | --- | --- | --- | | text-cta-link | #0061d5 | Color for hyperlinks. | | text-cta-link-disabled | #b2cff2 | Color for disabled hyperlinks. | | text-cta-link-hover | #1d6bca | Color for hyperlinks on hover. | | text-cta-link-pressed | #2486fc | Color for hyperlinks when pressed. | | text-text-error-on-light | #d5324e | Error text color on light backgrounds. | | text-text-on-dark | #ffffff | Text color on dark backgrounds. | | text-text-on-light | #222222 | Primary text color on light backgrounds. | | text-text-on-light-secondary | #6f6f6f | Secondary text color on light backgrounds. | | text-text-on-light-secondary-hover | #4e4e4e | Secondary text color on hover. | ## Typography tokens The comment in the table refers to pixels in calculation based on a root font-size of 16 px. | Token | Default value | Description | | --- | --- | --- | | body-default-font-family | Lato, "Helvetica Neue", sans-serif | Font family for body text. | | body-default-font-size | 0.875rem | Font size for body text. | | body-default-font-weight | 400 | Font weight for body text. | | body-default-letter-spacing | 0.01875rem | Letter spacing for body text. | | body-default-line-height | 1.25rem | Line height for body text. | | body-default-text-decoration | none | Text decoration for body text. | | body-default-bold-font-family | Lato, "Helvetica Neue", sans-serif | Font family for bold body text. | | body-default-bold-font-size | 0.875rem | Font size for bold body text. | | body-default-bold-font-weight | 700 | Font weight for bold body text. | | body-default-bold-letter-spacing | 0.01875rem | Letter spacing for bold body text. | | body-default-bold-line-height | 1.25rem | Line height for bold body text. | | body-default-bold-text-decoration | none | Text decoration for bold body text. | | body-default-semibold-font-family | Lato, "Helvetica Neue", sans-serif | Font family for semi-bold body text. | | body-default-semibold-font-size | 0.875rem | Font size for semi-bold body text. | | body-default-semibold-font-weight | 600 | Font weight for semi-bold body text. | | body-default-semibold-letter-spacing | 0.01875rem | Letter spacing for semi-bold body text. | | body-default-semibold-line-height | 1.25rem | Line height for semi-bold body text. | | body-default-semibold-text-decoration | none | Text decoration for semi-bold body text. | | body-large-font-family | Lato, "Helvetica Neue", sans-serif | Font family for large body text. | | body-large-font-size | 1rem | Font size for large body text. | | body-large-font-weight | 400 | Font weight for large body text. | | body-large-letter-spacing | 0.01875rem | Letter spacing for large body text. | | body-large-line-height | 1.5rem | Line height for large body text. | | body-large-text-decoration | none | Text decoration for large body text. | | body-large-bold-font-family | Lato, "Helvetica Neue", sans-serif | Font family for large bold body text. | | body-large-bold-font-size | 1rem | Font size for large bold body text. | | body-large-bold-font-weight | 700 | Font weight for large bold body text. | | body-large-bold-letter-spacing | 0.01875rem | Letter spacing for large bold body text. | | body-large-bold-line-height | 1.5rem | Line height for large bold body text. | | body-large-bold-text-decoration | none | Text decoration for large bold body text. | | caption-bold-font-family | Lato, "Helvetica Neue", sans-serif | Font family for bold captions. | | caption-bold-font-size | 0.75rem | Font size for bold captions. | | caption-bold-font-weight | 700 | Font weight for bold captions. | | caption-bold-letter-spacing | 0.01875rem | Letter spacing for bold captions. | | caption-bold-line-height | 0.875rem | Line height for bold captions. | | caption-bold-text-decoration | none | Text decoration for bold captions. | | caption-default-font-family | Lato, "Helvetica Neue", sans-serif | Font family for captions. | | caption-default-font-size | 0.75rem | Font size for captions. | | caption-default-font-weight | 400 | Font weight for captions. | | caption-default-letter-spacing | 0.01875rem | Letter spacing for captions. | | caption-default-line-height | 0.875rem | Line height for captions. | | caption-default-text-decoration | none | Text decoration for captions. | | label-bold-font-family | Lato, "Helvetica Neue", sans-serif | Font family for bold labels. | | label-bold-font-size | 0.625rem | Font size for bold labels. | | label-bold-font-weight | 700 | Font weight for bold labels. | | label-bold-letter-spacing | 0.0375rem | Letter spacing for bold labels. | | label-bold-line-height | 1rem | Line height for bold labels. | | label-bold-text-decoration | none | Text decoration for bold labels. | | label-default-font-family | Lato, "Helvetica Neue", sans-serif | Font family for labels. | | label-default-font-size | 0.625rem | Font size for labels. | | label-default-font-weight | 400 | Font weight for labels. | | label-default-letter-spacing | 0.0375rem | Letter spacing for labels. | | label-default-line-height | 1rem | Line height for labels. | | label-default-text-decoration | none | Text decoration for labels. | | link-default-font-family | Lato, "Helvetica Neue", sans-serif | Font family for hyperlinks. | | link-default-font-size | 0.875rem | Font size for hyperlinks. | | link-default-font-weight | 400 | Font weight for hyperlinks. | | link-default-letter-spacing | 0.01875rem | Letter spacing for hyperlinks. | | link-default-line-height | 1.25rem | Line height for hyperlinks. | | link-default-text-decoration | underline | Text decoration for hyperlinks. | | notification-default-font-family | Lato, "Helvetica Neue", sans-serif | Font family for notifications. | | notification-default-font-size | 0.5625rem | Font size for notifications. | | notification-default-font-weight | 700 | Font weight for notifications. | | notification-default-letter-spacing | 0.01875rem | Letter spacing for notifications. | | notification-default-line-height | 0.75rem | Line height for notifications. | | notification-default-text-decoration | none | Text decoration for notifications. | | title-large-font-family | Lato, "Helvetica Neue", sans-serif | Font family for large titles. | | title-large-font-size | 1.125rem | Font size for large titles. | | title-large-font-weight | 700 | Font weight for large titles. | | title-large-letter-spacing | 0.01875rem | Letter spacing for large titles. | | title-large-line-height | 1.5rem | Line height for large titles. | | title-large-text-decoration | none | Text decoration for large titles. | | title-medium-font-family | Lato, "Helvetica Neue", sans-serif | Font family for medium titles. | | title-medium-font-size | 1rem | Font size for medium titles. | | title-medium-font-weight | 700 | Font weight for medium titles. | | title-medium-letter-spacing | 0.01875rem | Letter spacing for medium titles. | | title-medium-line-height | 1.5rem | Line height for medium titles. | | title-medium-text-decoration | none | Text decoration for medium titles. | | title-small-font-family | Lato, "Helvetica Neue", sans-serif | Font family for small titles. | | title-small-font-size | 0.9375rem | Font size for small titles. | | title-small-font-weight | 700 | Font weight for small titles. | | title-small-letter-spacing | 0.01875rem | Letter spacing for small titles. | | title-small-line-height | 1.25rem | Line height for small titles. | | title-small-text-decoration | none | Text decoration for small titles. | | title-subtitle-font-family | Lato, "Helvetica Neue", sans-serif | Font family for subtitles. | | title-subtitle-font-size | 0.875rem | Font size for subtitles. | | title-subtitle-font-weight | 700 | Font weight for subtitles. | | title-subtitle-letter-spacing | 0.01875rem | Letter spacing for subtitles. | | title-subtitle-line-height | 1.25rem | Line height for subtitles. | | title-subtitle-text-decoration | none | Text decoration for subtitles. | | title-x-large-font-family | Lato, "Helvetica Neue", sans-serif | Font family for extra-large titles. | | title-x-large-font-size | 1.3125rem | Font size for extra-large titles. | | title-x-large-font-weight | 700 | Font weight for extra-large titles. | | title-x-large-letter-spacing | 0.01875rem | Letter spacing for extra-large titles. | | title-x-large-line-height | 2rem | Line height for extra-large titles. | | title-x-large-text-decoration | none | Text decoration for extra-large titles. | ## Spacing, sizing, and shape tokens The comment in the table refers to pixels in calculation based on a root font-size of 16 px. | Token | Default value | Description | | --- | --- | --- | | border-1 | 0.0625rem | Border width (1 px equivalent). | | border-2 | 0.125rem | Border width (2 px equivalent). | | border-3 | 0.1875rem | Border width (3 px equivalent). | | border-4 | 0.25rem | Border width (4 px equivalent). | | border-6 | 0.375rem | Border width (6 px equivalent). | | border-8 | 0.5rem | Border width (8 px equivalent). | | dropshadow-1 | 0 0 0.5rem 0 rgba(0, 0, 0, 0.05) | Subtle shadow effect. | | dropshadow-2 | 0 0.0625rem 0.25rem 0 rgba(0, 0, 0, 0.1) | Medium shadow effect. | | dropshadow-3 | 0 0.25rem 0.75rem 0 rgba(0, 0, 0, 0.1) | Pronounced shadow effect. | | radius-05 | 0.125rem | Border radius (2 px equivalent). | | radius-1 | 0.25rem | Border radius (4 px equivalent). | | radius-2 | 0.375rem | Border radius (6 px equivalent). | | radius-3 | 0.5rem | Border radius (8 px equivalent). | | radius-4 | 0.75rem | Border radius (12 px equivalent). | | radius-5 | 1rem | Border radius (16 px equivalent). | | radius-6 | 1.25rem | Border radius (20 px equivalent). | | radius-7 | 1.5rem | Border radius (24 px equivalent). | | radius-8 | 1.75rem | Border radius (28 px equivalent). | | radius-half | 2rem | Border radius (32 px equivalent). | | size-05 | 0.125rem | Size measurement (2 px equivalent). | | size-1 | 0.25rem | Size measurement (4 px equivalent). | | size-2 | 0.5rem | Size measurement (8 px equivalent). | | size-3 | 0.75rem | Size measurement (12 px equivalent). | | size-4 | 1rem | Size measurement (16 px equivalent). | | size-5 | 1.25rem | Size measurement (20 px equivalent). | | size-6 | 1.5rem | Size measurement (24 px equivalent). | | size-7 | 1.75rem | Size measurement (28 px equivalent). | | size-8 | 2rem | Size measurement (32 px equivalent). | | size-9 | 2.25rem | Size measurement (36 px equivalent). | | size-10 | 2.5rem | Size measurement (40 px equivalent). | | size-11 | 2.75rem | Size measurement (44 px equivalent). | | size-12 | 3rem | Size measurement (48 px equivalent). | | size-14 | 3.5rem | Size measurement (56 px equivalent). | | size-15 | 3.75rem | Size measurement (60 px equivalent). | | size-16 | 4rem | Size measurement (64 px equivalent). | | size-18 | 4.5rem | Size measurement (72 px equivalent). | | size-20 | 5rem | Size measurement (80 px equivalent). | | space-05 | 0.125rem | Spacing measurement (2 px equivalent). | | space-1 | 0.25rem | Spacing measurement (4 px equivalent). | | space-2 | 0.5rem | Spacing measurement (8 px equivalent). | | space-3 | 0.75rem | Spacing measurement (12 px equivalent). | | space-4 | 1rem | Spacing measurement (16 px equivalent). | | space-5 | 1.25rem | Spacing measurement (20 px equivalent). | | space-6 | 1.5rem | Spacing measurement (24 px equivalent). | | space-7 | 1.75rem | Spacing measurement (28 px equivalent). | | space-8 | 2rem | Spacing measurement (32 px equivalent). | | space-9 | 2.25rem | Spacing measurement (36 px equivalent). | | space-10 | 2.5rem | Spacing measurement (40 px equivalent). | | space-11 | 2.75rem | Spacing measurement (44 px equivalent). | | space-12 | 3rem | Spacing measurement (48 px equivalent). | | space-14 | 3.5rem | Spacing measurement (56 px equivalent). | | space-15 | 3.75rem | Spacing measurement (60 px equivalent). | | space-16 | 4rem | Spacing measurement (64 px equivalent). | | space-18 | 4.5rem | Spacing measurement (72 px equivalent). | | space-20 | 5rem | Spacing measurement (80 px equivalent). | **Reference:** https://developer.box.com/guides/embed/ui-elements/ui-elements-design-tokens/ --- ## Untitled *Type: guide | Category: Folders * Build Folder Tree The following examples provide an example on how to create a JSON representation of a folder tree. A folder tree is the… # Build Folder Tree The following examples provide an example on how to create a JSON representation of a folder tree. A folder tree is the name of a folder, and every subfolder within that folder. The sample below allows for specifying the starting **root** folder and the maximum depth the code should traverse. It also allows for configuring what user is authenticated by allowing the passing in of an initialized SDK client. ``` using System; using System.Collections.Generic; using System.Diagnostics; using System.Dynamic; using System.IO; using System.Linq; using System.Net; using System.Net.Sockets; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using Box.V2; using Box.V2.Auth; using Box.V2.Config; using Box.V2.Converter; using Box.V2.Exceptions; using Box.V2.JWTAuth; using Box.V2.Models; using Newtonsoft.Json; namespace BoxPlayground { public class Program4 { static void Main(string[] args) { ExecuteMainAsync().Wait(); } public class BoxFolderTreeBuilder { public BoxClient BoxClient { get; set; } public string RootFolderId { get; set; } public int MaxDepth { get; set; } public BoxFolderTreeBuilder(BoxClient boxClient, string rootFolderId = "0", int maxDepth = -1) { this.BoxClient = boxClient; this.RootFolderId = rootFolderId; this.MaxDepth = maxDepth; } public async Task<BoxFolderTree> BuildFolderTreeWithFlatLists() { var tree = new BoxFolderTree { RootId = this.RootFolderId, Files = new List<BoxFolderTreeItem>(), Folders = new List<BoxFolderTreeFolder>() }; var rootFolderItems = await this.BoxClient.FoldersManager.GetFolderItemsAsync(this.RootFolderId, limit: 1000, autoPaginate: true); var rootFolderChildren = new List<BoxFolderTreeFolder>(); foreach (var item in rootFolderItems.Entries) { var folderTreeItem = new BoxFolderTreeItem(item); folderTreeItem.Path = $"{this.RootFolderId}"; if (item.Type == "file") { tree.Files.Add(folderTreeItem); } else if (item.Type == "folder") { var childFolder = new BoxFolderTreeFolder(folderTreeItem); tree.Folders.Add(new BoxFolderTreeFolder(folderTreeItem)); rootFolderChildren.Add(childFolder); } } tree = await Dive(tree, rootFolderChildren, 1); return tree; } private async Task<BoxFolderTree> Dive(BoxFolderTree tree, List<BoxFolderTreeFolder> children, int currentDepth) { if (InTooDeep(currentDepth)) { return tree; } else { currentDepth++; var additionalChildren = new List<BoxFolderTreeFolder>(); foreach (var child in children) { var folderItems = await this.BoxClient.FoldersManager.GetFolderItemsAsync(child.Item.Id, limit: 1000, autoPaginate: true); var foundFolder = tree.Folders.FindIndex((f) => { return f.Item.Id == child.Item.Id; }); foreach (var item in folderItems.Entries) { if (foundFolder >= 0) { tree.Folders[foundFolder].Children.Add(item); } var folderTreeItem = new BoxFolderTreeItem(item); folderTreeItem.Path = $"{child.Path}/{child.Item.Id}"; if (item.Type == "file") { tree.Files.Add(folderTreeItem); } else if (item.Type == "folder") { var childFolder = new BoxFolderTreeFolder(folderTreeItem); tree.Folders.Add(new BoxFolderTreeFolder(folderTreeItem)); additionalChildren.Add(childFolder); } } } if (additionalChildren.Count == 0) { return tree; } else { return await Dive(tree, additionalChildren, currentDepth); } } } private bool InTooDeep(int depthCount) { if (this.MaxDepth < 0) { return false; } else { return (depthCount >= this.MaxDepth); } } public class BoxFolderTreeItem { [JsonProperty(PropertyName = "item")] public BoxItem Item { get; set; } [JsonProperty(PropertyName = "path")] public string Path { get; set; } public BoxFolderTreeItem(BoxItem item) { Item = item; } } public class BoxFolderTreeFolder : BoxFolderTreeItem { public BoxFolderTreeFolder(BoxFolderTreeItem item) : base(item.Item) { this.Path = item.Path; } [JsonProperty(PropertyName = "children")] public List<BoxItem> Children { get; set; } = new List<BoxItem>(); } public class BoxFolderTree { [JsonProperty(PropertyName = "rootId")] public string RootId { get; set; } [JsonProperty(PropertyName = "files")] public List<BoxFolderTreeItem> Files { get; set; } [JsonProperty(PropertyName = "folders")] public List<BoxFolderTreeFolder> Folders { get; set; } public string writeJSON() { var converter = new Box.V2.Converter.BoxJsonConverter(); return converter.Serialize<BoxFolderTreeBuilder.BoxFolderTree>(this); } } } private static async Task ExecuteMainAsync() { using (FileStream fs = new FileStream($"./config.json", FileMode.Open)) { var session = new BoxJWTAuth(BoxConfig.CreateFromJsonFile(fs)); var serviceAccountClient = session.AdminClient(session.AdminToken()); var folderTreeBuilder = new BoxFolderTreeBuilder(serviceAccountClient, rootFolderId: "37477903736"); var tree = await folderTreeBuilder.BuildFolderTreeWithFlatLists(); System.Console.WriteLine(tree.writeJSON()); } } } } ``` ``` package com.box; import java.io.BufferedReader; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import com.box.sdk.BoxConfig; import com.box.sdk.BoxDeveloperEditionAPIConnection; import com.box.sdk.BoxFile; import com.box.sdk.BoxFolder; import com.box.sdk.BoxItem; import com.eclipsesource.json.JsonArray; import com.eclipsesource.json.JsonObject; public class BoxFolderTreeBuilder { private BoxDeveloperEditionAPIConnection _boxClient; private String _rootFolderId; private int _maxDepth; private static final String DEFAULT_ROOT_FOLDER_ID = "0"; private static final int DEFAULT_MAX_DEPTH = -1; public BoxFolderTreeBuilder(BoxDeveloperEditionAPIConnection boxClient) { this(boxClient, DEFAULT_ROOT_FOLDER_ID, DEFAULT_MAX_DEPTH); } public BoxFolderTreeBuilder(BoxDeveloperEditionAPIConnection boxClient, String rootFolderId) { this(boxClient, rootFolderId, DEFAULT_MAX_DEPTH); } public BoxFolderTreeBuilder(BoxDeveloperEditionAPIConnection boxClient, int maxDepth) { this(boxClient, DEFAULT_ROOT_FOLDER_ID, maxDepth); } public BoxFolderTreeBuilder(BoxDeveloperEditionAPIConnection boxClient, String rootFolderId, int maxDepth) { this._boxClient = boxClient; this._rootFolderId = rootFolderId; this._maxDepth = maxDepth; } public BoxFolderTree buildFolderTreeWithFlatLists() { BoxFolderTree tree = new BoxFolderTree(); tree.setRootId(this._rootFolderId); ArrayList<BoxFolderTreeFolder> rootFolderChildren = new ArrayList<>(); BoxFolder folder = new BoxFolder(this._boxClient, this._rootFolderId); String path = this._rootFolderId; for (BoxItem.Info itemInfo : folder) { if (itemInfo instanceof BoxFile.Info) { BoxFile.Info fileInfo = (BoxFile.Info) itemInfo; BoxFolderTreeFile treeFile = new BoxFolderTreeFile(fileInfo, path); tree.files.add(treeFile); } else if (itemInfo instanceof BoxFolder.Info) { BoxFolder.Info folderInfo = (BoxFolder.Info) itemInfo; BoxFolderTreeFolder treeFolder = new BoxFolderTreeFolder(folderInfo, path); tree.folders.add(treeFolder); rootFolderChildren.add(treeFolder); } } tree = Dive(tree, rootFolderChildren, 1); return tree; } private BoxFolderTree Dive(BoxFolderTree tree, ArrayList<BoxFolderTreeFolder> children, int currentDepth) { if (inTooDeep(currentDepth)) { return tree; } else { currentDepth++; ArrayList<BoxFolderTreeFolder> additionalChildren = new ArrayList<>(); for (BoxFolderTreeFolder child : children) { BoxFolder folderItems = new BoxFolder(this._boxClient, child.getItem().getID()); int foundFolder = -1; for (int i = 0; i < tree.folders.size(); i++) { if (child.getItem().getID() == tree.folders.get(i).getItem().getID()) { foundFolder = i; } } String path = String.format("%s/%s", child.getPath(), child.getItem().getID()); for (BoxItem.Info item : folderItems.getChildren()) { if (foundFolder >= 0) { tree.folders.get(foundFolder).children.add(new BoxFolderTreeItem(item)); } if (item instanceof BoxFile.Info) { BoxFile.Info fileInfo = (BoxFile.Info) item; tree.files.add(new BoxFolderTreeFile(fileInfo, path)); } else if (item instanceof BoxFolder.Info) { BoxFolder.Info folderInfo = (BoxFolder.Info) item; BoxFolderTreeFolder nestedFolder = new BoxFolderTreeFolder(folderInfo, path); tree.folders.add(nestedFolder); additionalChildren.add(nestedFolder); } } } if (additionalChildren.size() == 0) { return tree; } else { return Dive(tree, additionalChildren, currentDepth); } } } private Boolean inTooDeep(int depthCount) { if (this._maxDepth < 0) { return false; } else { return (depthCount >= this._maxDepth); } } public class BoxFolderTree { private String rootId; private ArrayList<BoxFolderTreeFile> files = new ArrayList<>(); private ArrayList<BoxFolderTreeFolder> folders = new ArrayList<>(); public String getRootId() { return this.rootId; } public void setRootId(String id) { this.rootId = id; } public String writeJSON() { JsonObject requestJSON = new JsonObject(); JsonArray filesJSON = new JsonArray(); for (BoxFolderTreeFile file : files) { filesJSON.add(file.convertToJSON()); } JsonArray foldersJSON = new JsonArray(); for (BoxFolderTreeFolder folder : folders) { foldersJSON.add(folder.convertToJSON()); } requestJSON.add("rootId", this.rootId); requestJSON.add("files", filesJSON); requestJSON.add("folders", foldersJSON); return requestJSON.toString(); } } public class BoxFolderTreeItem { private BoxItem.Info item; public BoxFolderTreeItem(BoxItem.Info item) { this.item = item; } public JsonObject convertToJSON() { JsonObject itemJSON = new JsonObject(); itemJSON.add("id", this.item.getID()); itemJSON.add("name", this.item.getName()); String type = (item instanceof BoxFile.Info == true) ? "file" : "folder"; itemJSON.add("type", type); return itemJSON; } } public class BoxFolderTreeFile { private BoxFile.Info item; private String path; public BoxFolderTreeFile(BoxFile.Info item, String path) { this.item = item; this.path = path; } public BoxFile.Info getItem() { return this.item; } public void setItem(BoxFile.Info info) { this.item = info; } public String getPath() { return this.path; } public void setPath(String path) { this.path = path; } public JsonObject convertToJSON() { JsonObject itemJSON = new JsonObject(); JsonObject fileJSON = new JsonObject(); fileJSON.add("id", this.item.getID()); fileJSON.add("type", "file"); fileJSON.add("name", this.item.getName()); itemJSON.add("path", this.path); itemJSON.add("item", fileJSON); return itemJSON; } } public class BoxFolderTreeFolder { private BoxFolder.Info item; private String path; private ArrayList<BoxFolderTreeItem> children; public BoxFolderTreeFolder(BoxFolder.Info item, String path) { this.item = item; this.path = path; this.children = new ArrayList<>(); } public BoxFolder.Info getItem() { return this.item; } public void setItem(BoxFolder.Info info) { this.item = info; } public String getPath() { return this.path; } public void setPath(String path) { this.path = path; } public JsonObject convertFolderToJSON() { JsonObject itemJSON = new JsonObject(); JsonObject folderJSON = new JsonObject(); folderJSON.add("id", this.item.getID()); folderJSON.add("type", "folder"); folderJSON.add("name", this.item.getName()); itemJSON.add("path", this.path); itemJSON.add("item", folderJSON); return itemJSON; } public JsonObject convertToJSON() { JsonObject folderJSON = this.convertFolderToJSON(); JsonArray children = new JsonArray(); for (BoxFolderTreeItem item : this.children) { children.add(item.convertToJSON()); } folderJSON.add("children", children); return folderJSON; } } public static void main(String[] args) throws Exception { Path configPath = Paths.get("config.json"); try (BufferedReader reader = Files.newBufferedReader(configPath, Charset.forName("UTF-8"))) { BoxConfig boxConfig = BoxConfig.readFrom(reader); BoxDeveloperEditionAPIConnection serviceAccountClient = BoxDeveloperEditionAPIConnection .getAppEnterpriseConnection(boxConfig); BoxFolderTreeBuilder treeBuilder = new BoxFolderTreeBuilder(serviceAccountClient, "43491738095", 2); BoxFolderTree tree = treeBuilder.buildFolderTreeWithFlatLists(); System.out.println(tree.writeJSON()); } } } ``` ``` class BoxFolderTreeBuilder { constructor(boxClient, options) { options = options || {}; boxClient._useIterators = true; this.boxClient = boxClient; this.maxDepth = options.maxDepth || -1; this.rootFolderId = options.rootFolderId || "0"; } async buildFolderTreeWithFlatLists() { let tree = { rootId: this.rootFolderId, folders: [], files: [] } let folderItemsIterator = await this.boxClient.folders.getItems(this.rootFolderId); let collection = await BoxUtilities.autoPage(folderItemsIterator); let rootFolderChildren = []; const path = `${this.rootFolderId}`; collection.forEach((item) => { if (item.type === "file") { tree.files.push({ item, path }) } else if (item.type === "folder") { let folderTreeFolder = { item, path, children: [] } tree.folders.push(folderTreeFolder); rootFolderChildren.push(folderTreeFolder); } }); tree = await this.dive(tree, rootFolderChildren, 1); return tree; } async dive(tree, children, currentDepth) { if (this.inTooDeep(currentDepth)) { return tree; } else { currentDepth++; let additionalChildren = []; let childrenPromises = []; children.forEach((child) => { let foundFolder = -1; childrenPromises.push(this.boxClient.folders.getItems(child.item.id) .then((folderItemsIterator) => { return BoxUtilities.autoPage(folderItemsIterator) .then((collection) => { for (let i = 0; i < tree.folders.length; i++) { if (child.item.id === tree.folders[i].item.id) { foundFolder = i; } } const path = `${child.path}/${child.item.id}`; collection.forEach((item) => { if (foundFolder >= 0) { tree.folders[foundFolder].children.push(item); } if (item.type === "file") { tree.files.push({ item, path }) } else if (item.type === "folder") { let folderTreeFolder = { item, path, children: [] } tree.folders.push(folderTreeFolder); additionalChildren.push(folderTreeFolder); } }); return; }); })); }); await Promise.all(childrenPromises); if (additionalChildren.length === 0) { return tree; } else { return this.dive(tree, additionalChildren, currentDepth); } } } inTooDeep(depthCount) { if (this.maxDepth < 0) { return false; } else { return (depthCount >= this.maxDepth); } } } class BoxUtilities { static async autoPage(iterator, collection = []) { let moveToNextItem = async () => { let item = await iterator.next(); if (item.value) { collection.push(item.value); } if (item.done !== true) { return moveToNextItem(); } else { return collection; } } return moveToNextItem(); } } let folderTreeBuilder = new BoxFolderTreeBuilder(client); folderTreeBuilder.buildFolderTreeWithFlatLists() .then((tree) => { console.log(JSON.stringify(tree)); }) ``` **Reference:** https://developer.box.com/guides/folders/bulk/build-folder-tree/ --- ## Untitled *Type: guide | Category: Folders * Change Folder Owner To change the owner of a folder, first invite the user you wish to transfer the folder to as a collaborator on the… # Change Folder Owner To change the owner of a folder, first invite the user you wish to transfer the folder to as a collaborator on the folder. Then, update the created collaboration by changing the role of that invited user to `owner`. ``` curl -X PUT https://api.box.com/2.0/collaborations/1234 \ -H "authorization: Bearer <ACCESS_TOKEN>" \ -H "content-type: application/json" \ -d '{ "role": "owner" }' ``` ``` BoxCollaborationRequest requestParams = new BoxCollaborationRequest() { Id = "12345", Role = "owner" }; BoxCollaboration collab = await client.CollaborationsManager.EditCollaborationAsync(requestParams); ``` ``` Collection<BoxCollaboration.Info> pendingCollaborations = BoxCollaboration.getPendingCollaborations(api); for (BoxCollaboration.Info collabInfo : pendingCollaborations) { collabInfo.setRole(BoxCollaboration.Role.OWNER); collabInfo.getResource().updateInfo(collabInfo); } ``` ``` from boxsdk.object.collaboration import CollaborationRole, CollaborationStatus collaboration = client.collaboration(collab_id='12345') updated_collaboration = collaboration.update_info(CollaborationRole.OWNER) ``` ``` client.collaborations.update('12345', {role: client.collaborationRoles.OWNER}) .then(collaboration => { // ... }); ``` Depending on the settings, a user with access to a folder might be able to invite other users, yet in all cases only the current owner of a folder can transfer the ownership. **Reference:** https://developer.box.com/guides/folders/single/change-owner/ --- ## Untitled *Type: guide | Category: Embed Box * Preview - Viewers & Events This page describes the preview experience for each file type. It also lists the events you can listen for by… # Preview - Viewers & Events This page describes the preview experience for each file type. It also lists the events you can listen for by viewer type. ## Text Viewer The text viewer renders previews of text files. For code files like `py` or `rb`, it uses [`highlight.js`](https://github.com/isagalaev/highlight.js) to add syntax highlighting. ### Behavior The text viewer displays the first 192 KB of text in the file. Additional text is truncated and a notification and download button are appended to the bottom of the preview. Re-sizing the viewer window will reflow the text to fit the available space and the zoom in and out buttons will increase and decrease font size respectively. This viewer supports printing and will attempt to print with appropriate syntax highlighting when either `print()` is invoked or the print button is pressed. Note that printing large files may cause some browsers to freeze for a few seconds. ### Controls - Zoom In - Zoom Out - Fullscreen: can be exited with the escape key ### Supported File Extensions `as`, `as3as`, `asmas`, `batas`, `cas`, `ccas`, `cmakeas`, `cppas`, `csas`, `cssas`, `cxxas`, `diffas`, `erbas`, `groovyas`, `has`, `hamlas`, `hhas`, `htmas`, `htmlas`, `javaas`, `jsas`, `lessas`, `mas`, `makeas`, `mdas`, `mlas`, `mmas`, `phpas`, `plas`, `plistas`, `propertiesas`, `pyas`, `rbas`, `rstas`, `sassas`, `scalaas`, `scriptas`, `scmas`, `smlas`, `sqlas`, `shas`, `vias`, `vimas`, `webdocas`, `xhtmlas`, `yaml`, ### Events The text viewer triggers the following events. | Event Name | Explanation | Event Data | | --- | --- | --- | | destroy | The preview is intentionally destroyed | | | load | The preview loads | 1. {string} error (optional): error message | | | | 2. {object} file: current file | | | | 3. {object} metrics: information from the logger | | | | 4. {object} viewer: current viewer | | notification | A notification is displayed | | | navigate | The preview is shown for a given index | {object} file | | reload | The preview reloads | | | resize | The preview resizes | 1. {number} height: window height | | | | 2. {number} width: window width | | zoom | The preview zooms in or out | 1. {number} zoom: new zoom value | | | | 2. {boolean} canZoomIn: true if the viewer can zoom in more | | | | 3. {boolean} canZoomOut: true if the viewer can zoom out more | | printsuccess | An attempt to print triggered successfully | | ## 360 Video Viewer The 360 video viewer renders a preview of a video stored as an equirectangular projection (often recorded with a 360 camera). ### Behavior This viewer gives you an interactive view of the 360 degree video. ### Controls - Change the view direction with the left mouse button (single touch on touch-enabled device). ### VR button When using a browser that supports WebVR and a suitable VR device is attached to your computer, a VR button will be available to allow toggling in and out of VR mode. ### Limitations Currently, this previewer requires that the file be named with a `.360` preceding the file extension. This is so that Preview SDK knows to run this viewer rather than the standard video preview. ### Supported File Extensions `360.3g2`, `360.3gp`, `360.avi`, `360.m2v`, `360.m2ts`, `360.m4v`, `360.mkv`, `360.mov`, `360.mp4`, `360.mpeg`, `360.mpg`, `360.mts`, `360.qt`, `360.wmv` ### Events The 360 video viewer triggers the following events. | Event Name | Explanation | Event Data | | --- | --- | --- | | destroy | The preview is intentionally destroyed | | | load | The preview loads | 1. {string} error (optional): error message | | | | 2. {object} file: current file | | | | 3. {object} metrics: information from the logger | | | | 4. {object} viewer: current viewer | | notification | A notification is displayed | | | navigate | The preview is shown for a given index | {object} file | | reload | The preview reloads | | | resize | The preview resizes | 1. {number} height: window height | | | | 2. {number} width: window width | ## SWF Viewer The SWF viewer uses [`SWFObject`](https://github.com/swfobject/swfobject) to embed SWF files. ### Behavior If the user has the Adobe Flash Player plugin, `SWFObject` will embed the SWF file and allow the plugin to render relevant content. Note that all network requests made by the flash content will be blocked due to security constraints, so any content that requires network connectivity will not be rendered. ### Supported File Extensions - `swf` ### Events The SWF viewer triggers the following events. | Event Name | Explanation | Event Data | | --- | --- | --- | | destroy | The preview is intentionally destroyed | | | load | The preview loads | 1. {string} error (optional): error message | | | | 2. {object} file: current file | | | | 3. {object} metrics: information from the logger | | | | 4. {object} viewer: current viewer | | notification | A notification is displayed | | | navigate | The preview is shown for a given index | {object} file | | reload | The preview reloads | | | resize | The preview resizes | 1. {number} height: window height | | | | 2. {number} width: window width | ## Presentation viewer The presentation viewer renders previews of PowerPoint files. ### Behavior The presentation viewer remembers which slide you were viewing upon closing the preview. The next time that file is opened, you will immediately be brought to that slide. Scrolling the mouse up and down, or swiping up and down on mobile will transition between slides. Zooming in or out will increase or decrease the size of the slide respectively. If the zoom level causes the content to overflow, scrolling the mouse will allow you to scroll around the slide. To return to normal scrolling behavior, the user must zoom out until the overflow is removed. ### Controls - Zoom In - Zoom Out - Set Page: either with the up and down arrows, or by clicking the page number and entering text - Fullscreen: can be exited with the escape key ### Supported File Extensions `ppt`, `pptx`, `odp` ### Options | Option | Type | Description | | --- | --- | --- | | annotations | boolean | Optional. Whether annotations on content are shown. Defaults to false | ### Events The presentation viewer triggers the following events. | Event Name | Explanation | Event Data | | --- | --- | --- | | destroy | The preview is intentionally destroyed | | | load | The preview loads | 1. string error (optional): error message | | | | 2. object file: current file | | | | 3. object metrics: information from the logger | | | | 4. object viewer: current viewer | | notification | A notification is displayed | | | navigate | The preview is shown for a given index | object file | | reload | The preview reloads | | | resize | The preview resizes | 1. number height: window height | | | | 2. number width: window width | | zoom | The preview zooms in or out | 1. number zoom: new zoom value | | | | 2. boolean canZoomIn: true if the viewer can zoom in more | | | | 3. boolean canZoomOut: true if the viewer can zoom out more | | pagerender | A page renders | number page number of rendered page | | pagefocus | A page is visible | number page number of focused page | | scrollstart | The viewer starts to scroll | 1. number scrollTop: number of pixels scrolled from top of viewport | | | | 2. number scrollLeft: number of pixels scrolled from left of viewport | | scrollend | The viewer stops scrolling | 1. number scrollTop: number of pixels scrolled from top of viewport | | | | 2. number scrollLeft: number of pixels scrolled from left of viewport | ## MP4 Viewer The MP4 viewer renders previews for video files. ### Behavior The MP4 viewer uses a black background to create a better viewing experience. Volume can be muted or unmuted by clicking the volume icon, or changed by dragging the volume scrubber. The position of the video can be changed by clicking or dragging the playback scrubber. ### Controls - Play/Pause - Volume - Settings - Fullscreen (can be exited with the escape key) ### Settings Settings are available through the cog icon in the preview toolbar. - Video speed values: `0.25`, `0.5`, normal (`1`), `1.25`, `1.5`, `2.0` ### Supported File Extensions `3g2`, `3gp`, `avi`, `m2v`, `m2ts`, `m4v`, `mkv`, `mov`, `mp4`, `mpeg`, `mpg`, `ogg`, `mts`, `qt`, `wmv` ### Events The MP4 viewer triggers the following events. | Event Name | Explanation | Event Data | | --- | --- | --- | | destroy | The preview is intentionally destroyed | | | load | The preview loads | 1. string error (optional): error message | | | | 2. object file: current file | | | | 3. object metrics: information from the logger | | | | 4. object viewer: current viewer | | notification | A notification is displayed | | | navigate | The preview is shown for a given index | object file | | reload | The preview reloads | | | resize | The preview resizes | 1. number height: window height | | | | 2. number width: window width | | speedchange | Media speed changes | string playback speed | | play | The video plays | | | pause | The video pauses | | | seek | The video skips to a time | number time | ## MP3 Viewer The MP3 viewer displays previews for audio files. ### Behavior Volume can be muted or unmuted by clicking the volume icon, or changed by dragging the volume scrubber. The position of the audio can be changed by clicking or dragging the playback scrubber. ### Controls - Play/Pause - Volume - Settings ### Settings Settings are available through the cog icon in the preview toolbar. - Audio Speed: `0.25`, `0.5`, normal (`1`), `1.25`, `1.5`, `2.0` ### Supported File Extensions `aac`, `aif`, `aifc`, `aiff`, `amr`, `au`, `flac`, `m4a`, `mp3`, `ra`, `wav`, `wma` ### Events The MP3 viewer triggers the following events. | Event Name | Explanation | Event Data | | --- | --- | --- | | destroy | The preview is intentionally destroyed | | | load | The preview loads | 1. string error (optional): error message | | | | 2. object file: current file | | | | 3. object metrics: information from the logger | | | | 4. object viewer: current viewer | | notification | A notification is displayed | | | navigate | The preview is shown for a given index | object file | | reload | The preview reloads | | | resize | The preview resizes | 1. number height: window height | | | | 2. number width: window width | | speedchange | Media speed changes | string playback speed | | play | The video plays | | | pause | The video pauses | | | seek | The video skips to a time | number time | ## Office Viewer The Office viewer renders previews of Microsoft Office documents by embedding an `<iframe>` of Microsoft's Office Online viewer. ### Behavior The Office viewer currently supports previews of Excel files using Microsoft Office Online from within the Box Web Application. Support for platform use cases and other Office file formats is in progress. There are several limitations at the moment: - File must be downloadable - File size cannot be greater than 5MB - File cannot be shared via a Box shared link with a password (shared links without passwords are okay) ### Supported File Extensions `xlsx` ### Events The Office viewer triggers the following events. | Event Name | Explanation | Event Data | | --- | --- | --- | | destroy | The preview is intentionally destroyed | | | load | The preview loads | 1. {string} error (optional): error message | | | | 2. {object} file: current file | | | | 3. {object} metrics: information from the logger | | | | 4. {object} viewer: current viewer | | notification | A notification is displayed | | | navigate | The preview is shown for a given index | {object} file | | reload | The preview reloads | | | resize | The preview resizes | 1. {number} height: window height | | | | 2. {number} width: window width | ## Markdown Viewer The Markdown viewer uses [`Remarkable`](https://github.com/jonschlinkert/remarkable) to parse markdown files and [`highlight.js`](https://github.com/isagalaev/highlight.js) to add syntax highlighting to code blocks contained within. ### Behavior The Markdown viewer parses the first 192KB of raw markdown in the file and renders it using GitHub's Markdown style. Additional content is truncated and a notification along with a download button are appended to the bottom of the preview. The viewer supports GitHub Flavored Markdown including tables, syntax highlighting, and automatic URL linking. Re-sizing the viewer window will reflow the markdown to fit the available space. Also, this viewer supports printing and will attempt to print the parsed markdown and with syntax highlighting on code when either `print()` is invoked or the print button is pressed. Note that printing large files may cause some browsers to freeze for a few seconds. ### Controls - Fullscreen (can be exited with the escape key) ### Supported File Extensions `md` ### Events The Markdown viewer triggers the following events. | Event Name | Explanation | Event Data | | --- | --- | --- | | destroy | The preview is intentionally destroyed | | | load | The preview loads | 1. {string} error (optional): error message | | | | 2. {object} file: current file | | | | 3. {object} metrics: information from the logger | | | | 4. {object} viewer: current viewer | | notification | A notification is displayed | | | navigate | The preview is shown for a given index | {object} file | | reload | The preview reloads | | | resize | The preview resizes | 1. {number} height: window height | | | | 2. {number} width: window width | | printsuccess | An attempt to print triggered successfully | | ## Model3D viewer The `Model3D` viewer renders previews of 3D model files and allows you to enable different rendering modes to inspect various aspects of the model. for example wireframe, texture coordinates, and more. Animation data is also supported for files that contain it (for example `box3d`, `fbx`, `dae`). ### Behavior The `Model3D` viewer gives you an interactive view of the model. The left mouse button allows you to orbit about the model (single touch on touch-enabled device). Double-clicking somewhere on the model allows you to change orbit focus. ### Controls - Zoom (change distance to the model) with the mouse wheel (or two-finger scroll on a touch-enabled device). - Pan (lateral movement) with the right mouse button (or three-finger swipe on a touch-enabled device). - Animation Selection: If the model that is being viewed contains animations, two animation buttons will be visible in the toolbar. The first allows you to play and pause the animation and the second allows the selection of the current animation. - VR button: If using a browser that supports WebVR and a suitable VR device is attached to your computer, the VR button will allow toggling in and out of VR mode. ### Settings Settings are available through the cog icon in the preview toolbar. - Render Mode: Lit, Unlit, Normal, Shape, UV Overlay - Toggle Wireframe - Toggle Skeleton - Camera Projection: Perspective, Orthographic - Render Quality: Auto, Full - Rotate Model: X, Y, Z ## Box3D Packages Preview gives users the ability to view a single file within Box so, by default, you can't view textures on your model. However, the Box web application gives users the ability to create a Box3D package that combines all dependent files into a single file that can be shared and previewed. To do this, right-click the model file within Box and choose "Create 3D Package". All referenced files found within Box will be included in the resulting package. ### Supported File Extensions `box3d`, `fbx`, `dae`, `3ds`, `obj`, `stl`, `ply` ### Events The `Model3D` viewer triggers the following events. | Event Name | Explanation | Event Data | | --- | --- | --- | | destroy | The preview is intentionally destroyed | | | load | The preview loads | 1. {string} error (optional): error message | | | | 2. {object} file: current file | | | | 3. {object} metrics: information from the logger | | | | 4. {object} viewer: current viewer | | notification | A notification is displayed | | | navigate | The preview is shown for a given index | {object} file | | reload | The preview reloads | | | resize | The preview resizes | 1. {number} height: window height | | | | 2. {number} width: window width | ## 360 Image viewer The 360 image viewer renders a preview of an image stored as an equirectangular projection (often taken with a 360 camera). ### Behavior This viewer gives you an interactive view of a 360 degree image. First, a low resolution version of the image is loaded to give a quick view before the full resolution image is finished loading. Clicking and dragging with the left mouse button will change the view direction (single touch and drag on touch-enabled device). ### Controls - Fullscreen (can be exited with the escape key) - VR Button: When using a browser that supports WebVR and a suitable VR device is attached to your computer, a VR button will be available to allow toggling in and out of VR mode. ### Limitations Currently, this previewer requires that the file be named with a '.360' preceding the file extension. This is so that Preview SDK knows to run this viewer rather than the standard image viewer. ### Supported File Extensions `360.jpg`, `360.jpeg`, `360.png`, `360.ai`, `360.bmp`, `360.dcm`, `360.eps`, `360.gif`, `360.ps`, `360.psd`, `360.svg`, `360.svs`, `360.tga`, `360.tif`, `360.tiff` ### Events The 360 image viewer triggers the following events. | Event Name | Explanation | Event Data | | --- | --- | --- | | destroy | The preview is intentionally destroyed | | | load | The preview loads | 1. {string} error (optional): error message | | | | 2. {object} file: current file | | | | 3. {object} metrics: information from the logger | | | | 4. {object} viewer: current viewer | | notification | A notification is displayed | | | navigate | The preview is shown for a given index | {object} file | | reload | The preview reloads | | | resize | The preview resizes | 1. {number} height: window height | | | | 2. {number} width: window width | ## Image viewer The image viewer renders previews of image files. ### Behavior Rotating the viewer will rotate the image 90 degrees clockwise. At the default zoom level, clicking on the image will zoom in once. When zoomed in, clicking on the document will return to the default zoom level. When zoomed out, clicking on the document will zoom in until the original zoom level is reached. ### Controls - Zoom In - Zoom Out - Rotate - Fullscreen: can be exited with the escape key ### Supported File Extensions `ai`, `bmp`, `dcm`, `eps`, `idml`, `indt`, `indd`, `inx`, `gif`, `png`, `ps`, `psd`, `svs`, `tga` ### Options | Option | Type | Description | | --- | --- | --- | | annotations | boolean | Optional. Whether annotations on content are shown. Defaults to false | ### Events The image viewer triggers the following events. | Event Name | Explanation | Event Data | | --- | --- | --- | | destroy | The preview is intentionally destroyed | | | load | The preview loads | 1. {string} error (optional): error message | | | | 2. {object} file: current file | | | | 3. {object} metrics: information from the logger | | | | 4. {object} viewer: current viewer | | notification | A notification is displayed | | | navigate | The preview is shown for a given index | {object} file | | reload | The preview reloads | | | resize | The preview resizes | 1. {number} height: window height | | | | 2. {number} width: window width | | zoom | The preview zooms in or out | 1. {number} zoom: new zoom value | | | | 2. {boolean} canZoomIn: true if the viewer can zoom in more | | | | 3. {boolean} canZoomOut: true if the viewer can zoom out more | | pan | The preview is panning | | | panstart | Panning starts | | | panend | Panning ends | | | rotate | The image rotates | | | printsuccess | An attempt to print triggered successfully | | ## Multi-image viewer The multi-image viewer renders previews of multi-image files (`tiff`, `tif`). ### Behavior At the default zoom level, clicking on the image will zoom in once. When zoomed in, clicking on the document will return to the default zoom level. When zoomed out, clicking on the document will zoom in until the original zoom level is reached. ### Controls - Zoom In - Zoom Out - Fullscreen: can be exited with the escape key - Set Page: either with the up and down arrows, or by clicking the page number and entering text ### Supported File Extensions `tif`, `tiff` ### Options | Option | Type | Description | | --- | --- | --- | | annotations | boolean | Optional. Whether annotations on content are shown. Defaults to false | ### Events The image viewer triggers the following events. | Event Name | Explanation | Event Data | | --- | --- | --- | | destroy | The preview is intentionally destroyed | | | load | The preview loads | 1. {string} error (optional): error message | | | | 2. {object} file: current file | | | | 3. {object} metrics: information from the logger | | | | 4. {object} viewer: current viewer | | notification | A notification is displayed | | | navigate | The preview is shown for a given index | {object} file | | reload | The preview reloads | | | resize | The preview resizes | 1. {number} height: window height | | | | 2. {number} width: window width | | zoom | The preview zooms in or out | 1. {number} zoom: new zoom value | | | | 2. {boolean} canZoomIn: true if the viewer can zoom in more | | | | 3. {boolean} canZoomOut: true if the viewer can zoom out more | | pagefocus | A page is visible | {number} page number of focused page | | pan | The preview is panning | | | panstart | Panning starts | | | panend | Panning ends | | ### Methods The following methods are available for the multi-page image viewer. | Method Name | Explanation | Method Parameters | | --- | --- | --- | | zoom | Zooms the image | {string} 'in', 'out', or 'reset' | | previousPage | Navigates to the previous page | | | nextPage | Navigates to the next page | | | setPage | Navigates to a given page | {number} page number | | toggleFullscreen | Toggles full screen mode | | ## Document viewer The document viewer renders previews for a variety of document types. ### Behavior The document viewer remembers which page you were viewing upon closing the preview. The next time that file is opened, you will immediately be brought to that page. Resizing the viewer window will cause the document to resize. ### Controls - Zoom In - Zoom Out - Set Page: either with the up and down arrows, or by clicking the page number and entering text - Fullscreen (can be exited with the escape key) ### Supported File Extensions `as`, `as3`, `asm`, `bat`, `c`, `cc`, `cmake`, `cpp`, `cs`, `css`, `csv`, `cxx`, `diff`, `doc`, `docx`, `erb`, `gdoc`, `groovy`, `gsheet`, `h`, `haml`, `hh`, `htm`, `html`, `java`, `js`, `less`, `log`, `m`, `make`, `md`, `ml`, `mm`, `msg`, `odp`, `ods`, `odt`, `pdf`, `php`, `pl`, `plist`, `ppt`, `pptx`, `properties`, `py`, `rb`, `rst`, `rtf`, `sass`, `scala`, `scm`, `script`, `sh`, `sml`, `sql`, `tsv`, `txt`, `vi`, `vim`, `webdoc`, `wpd`, `xhtml`, `xls`, `xlsm`, `xlsx`, `xml`, `xsd`, `xsl`, `yaml` ### Options | Option | Type | Description | | --- | --- | --- | | annotations | boolean | Optional. Whether annotations on content are shown. Defaults to false | ### Events The document viewer triggers the following events. | Event Name | Explanation | Event Data | | --- | --- | --- | | destroy | The preview is intentionally destroyed | | | load | The preview loads | 1. {string} error (optional): error message | | | | 2. {object} file: current file | | | | 3. {object} metrics: information from the logger | | | | 4. {object} viewer: current viewer | | notification | A notification is displayed | | | navigate | The preview is shown for a given index | {object} file | | reload | The preview reloads | | | resize | The preview resizes | 1. {number} height: window height | | | | 2. {number} width: window width | | zoom | The preview zooms in or out | 1. {number} newScale: new zoom value | | | | 2. {boolean} canZoomIn: true if the viewer can zoom in more | | | | 3. {boolean} canZoomOut: true if the viewer can zoom out more | | pagerender | A page is rendered | {number} page number of rendered page | | pagefocus | A page is visible | {number} page number of focused page | | scrollstart | The viewer starts to scroll | 1. {number} scrollTop: number of pixels scrolled from top of viewport | | | | 2. {number} scrollLeft: number of pixels scrolled from left of viewport | | scrollend | The viewer stops scrolling | 1. {number} scrollTop: number of pixels scrolled from top of viewport | | | | 2. {number} scrollLeft: number of pixels scrolled from left of viewport | | printsuccess | An attempt to print triggered successfully | | | printsuccess | An attempt to print failed | | ### Annotations | Event Name | Explanation | Event Data | | --- | --- | --- | | annotationdraw | Text is highlighted | | | annotationcommentpending | User hovers back into a dialog that has a pending comment | | | annotationcreate | Annotation is created | | | annotationcancel | An annotation was started but then abandoned before it was created | | | annotationdelete | Annotation with provided AnnotationID is deleted. If no ID is provided, deletes the first (and only remaining) annotation in the thread. | | ## <iframe> viewer The `<iframe>` viewer embeds an frame to show content rendered from an external source. ### Behavior The `<iframe>` viewer is used for previews of Box Notes. Box Notes has full-featured viewers within the main Box Web Application, but these full viewers are not initialized when users navigate from previews of other files that may be in the same directory as the Notes files. In this situation, the `<iframe>` viewer embeds an view-only render of the Box Note file. ### Supported File Extensions `boxnote` ### Events The `<iframe>` viewer triggers the following events. | Event Name | Explanation | Event Data | | --- | --- | --- | | destroy | The preview is intentionally destroyed | | | load | The preview loads | 1. {string} error (optional): error message | | | | 2. {object} file: current file | | | | 3. {object} metrics: information from the logger | | | | 4. {object} viewer: current viewer | | notification | A notification is displayed | | | navigate | The preview is shown for a given index | {object} file | | reload | The preview reloads | | | resize | The preview resizes | 1. {number} height: window height | | | | 2. {number} width: window width | ## DASH viewer The DASH viewer renders previews for video files using [`Shaka Player`](https://github.com/google/shaka-player). ### Behavior The DASH viewer uses a black background to create a more native viewing experience. Video is streamed in chunks of bytes at an initial quality determined automatically. Volume can be muted or unmuted by clicking the volume icon, or changed by dragging the volume scrubber. The position of the video can be changed by clicking or dragging the playback scrubber. ### Controls - Play/Pause - Volume - Settings - Fullscreen (can be exited with the escape key) ### Settings Settings are available through the cog icon in the preview toolbar. - Video Speed: `0.25`, `0.5`, normal (`1`), `1.25`, `1.5`, `2.0` - Video Quality: `480p`, `1080p`, `auto` ### Supported File Extensions `3g2`, `3gp`, `avi`, `m2v`, `m2ts`, `m4v`, `mkv`, `mov`, `mp4`, `mpeg`, `mpg`, `ogg`, `mts`, `qt`, `wmv` ### Events The DASH viewer triggers the following events. | Event Name | Explanation | Event Data | | --- | --- | --- | | destroy | The preview is intentionally destroyed | | | load | The preview loads | 1. {string} error (optional): error message | | | | 2. {object} file: current file | | | | 3. {object} metrics: information from the logger | | | | 4. {object} viewer: current viewer | | notification | A notification is displayed | | | navigate | The preview is shown for a given index | {object} file | | reload | The preview reloads | | | resize | The preview resizes | 1. {number} height: window height | | | | 2. {number} width: window width | | speedchange | The media speed changes | {string} playback speed | | qualitychange | The video quality changes | {string} media quality | | bandwidthhistory | Gives bandwidth history when the preview is destroyed | {array} bandwidth information | | switchhistory | Gives quality switching history when the preview is destroyed | {array} quality switch objects | | adaptation | Quality adapts to a change in bandwidth | {number} bandwidth | | play | The video plays | | | pause | The video pauses | | | seek | The video skips to a time | {number} time | ## CSV viewer The CSV viewer uses [`PapaParse`](https://github.com/mholt/PapaParse) to parse CSV and TSV files and [`React Virtualized`](https://github.com/bvaughn/react-virtualized) to display the parsed data in a table. ### Behavior Resizing the viewer window will cause the table to resize, and the zoom in and out buttons will increase and decrease font size respectively. Currently, column and row sizes are fixed and overflowing text will be truncated. This viewer does not support printing. ### Controls - Zoom In - Zoom Out - Fullscreen (can be exited with the escape key) ### Supported File Extensions `csv`, `tsv` ### Events The CSV viewer triggers the following events. | Event Name | Explanation | Event Data | | --- | --- | --- | | destroy | The preview is intentionally destroyed | | | load | The preview loads | 1. {string} error (optional): error message | | | | 2. {object} file: current file | | | | 3. {object} metrics: information from the logger | | | | 4. {object} viewer: current viewer | | notification | A notification is displayed | | | navigate | The preview is shown for a given index | {object} file | | reload | The preview reloads | | | resize | The preview resizes | 1. {number} height: window height | | | | 2. {number} width: window width | | zoom | The preview zooms in or out | 1. {number} zoom: new zoom value | | | | 2. {boolean} canZoomIn: true if the viewer can zoom in more | | | | 3. {boolean} canZoomOut: true if the viewer can zoom out more | **Reference:** https://developer.box.com/guides/embed/ui-elements/viewers-and-events/ --- ## Untitled *Type: guide | Category: Folders * Create Folder To create a folder in Box you will need to provide our API with a name for the new folder, as well as the id of the parent… # Create Folder To create a folder in Box you will need to provide our API with a `name` for the new folder, as well as the `id` of the `parent` folder that you would like to create the new folder within. # Name restrictions There are some restrictions to the folder name. Names containing non-printable ASCII characters, forward and backward slashes (`/`, `\`), as well as names with trailing spaces are prohibited. Additionally, the names `.` and `..` are reserved names and therefore also prohibited. **Reference:** https://developer.box.com/guides/folders/single/create/ --- ## Untitled *Type: guide | Category: Folders * Bulk Manipulation The following pages discuss how to do various bulk methods to folders. # Bulk Manipulation The following pages discuss how to do various bulk methods to folders. **Reference:** https://developer.box.com/guides/folders/bulk/ --- ## Untitled *Type: guide | Category: Folders * Create Folder Lock To create a lock on a folder in Box you will need to provide our API with the id of the folder for which the lock should… # Create Folder Lock To create a lock on a folder in Box you will need to provide our API with the `id` of the folder for which the lock should be applied. Optionally you may supply the specific `locked_operations` to be applied with the folder lock. # Folder Locks When using any folder lock API endpoints, you must be authenticated as the owner/co-owner of the folder you are trying to access. # Setting Locked Operations If the `locked_operations` object is included with a folder lock request, both `move` and `delete` need to be set to `true`. Supplying only one lock operation in the object, or setting the values of both to something other than `true` will produce an error. These options are in place to allow for additional operations in the future. ## Lock Operations There are two possible lock operations that may be applied to a folder, `move` and `delete`. The `move` lock will prevent the folder from being moved to a new location or owner while the lock is still applied. The `delete` lock will prevent the folder from being deleted while the lock is still applied. **Reference:** https://developer.box.com/guides/folders/single/create-lock/ --- ## Untitled *Type: guide | Category: Folders * Copy Folder Creates a copy of a folder within a destination folder. The original folder will not be changed. To copy a folder in Box you… # Copy Folder Creates a copy of a folder within a destination folder. The original folder will not be changed. To copy a folder in Box you will need to provide our API with the `id` of the `parent` folder that you would like to copy the folder into. Optionally, you can provide a different name for the new folder. # Name restrictions There are some restrictions to the folder name. Names containing non-printable ASCII characters, forward and backward slashes (`/`, `\`), as well as names with trailing spaces are prohibited. Additionally, the names `.` and `..` are reserved names and therefore also prohibited. ## Asynchronous copying If the folder being copied contains up to 500 items the copy will happen synchronously with the API call. The call will not return until the copy operation has completed. If the folder contains more than 500 items the copy operation will be run asynchronously and the API call will return directly yet before the copy operation has completed. We currently have no API to check when a copy operation has finished. ## Folder locking During this operation, part of the file tree will be locked, mainly the source folder and all of its descendants, as well as the destination folder. For the duration of the operation, no other move, copy, delete, or restore operation can performed on any of the locked folders. Most importantly, this means that the same folder can not be copied to two different parts of the folder tree at the same time. ## Metadata If the destination folder has a metadata cascade policy attached to any of the parent folders a metadata cascade operation will be kicked off asynchronously. We currently have no API to check when this operation has finished. **Reference:** https://developer.box.com/guides/folders/single/copy/ --- ## Untitled *Type: guide | Category: Folders * Delete Folder Lock To remove a lock applied to a folder in Box, call the DELETE /folder_locks/:id API with the id of the folder lock. Folder… # Delete Folder Lock To remove a lock applied to a folder in Box, call the `DELETE /folder_locks/:id` API with the id of the folder lock. # Folder Locks When using any folder lock API endpoints, you must be authenticated as the owner/co-owner of the folder you are trying to access. # Locating the folder Lock ID To delete a folder lock you must supply the folder lock ID to the API. A folder lock ID is supplied in the response when [creating a folder lock](g://folders/single/create-lock), or when [listing locks](g://folders/single/get-locks) on a given folder. **Reference:** https://developer.box.com/guides/folders/single/delete-lock/ --- ## Untitled *Type: guide | Category: Folders * Delete Folder To remove a folder in Box you will need to provide our API with the ID of the folder. Deleting non-empty folders This API… # Delete Folder To remove a folder in Box you will need to provide our API with the ID of the folder. ## Deleting non-empty folders This API returns an error if the folder is not empty. When deleting a folder, you can pass in the `recursive` parameter to force a folder to be deleted even if it is not empty. This will delete all items within this folder, including any of their descendants. ## Folder locking The enterprise settings determine whether the folder will be permanently deleted from Box or moved to the trash. During this operation, part of the file tree will be locked, mainly the source folder and all of its descendants. For the duration of the operation, no other move, copy, delete, or restore operation can performed on any of the locked folders. ## Timeout Timeout for this operation is 60 seconds. The operation will include after a `HTTP 503` has been returned. **Reference:** https://developer.box.com/guides/folders/single/delete/ --- ## Untitled *Type: guide | Category: Folders * Get Folder Lock To get a list of the current locks on a folder in Box, call the GET /folder_locks/ API with the id of the folder supplied as… # Get Folder Lock To get a list of the current locks on a folder in Box, call the `GET /folder_locks/` API with the id of the folder supplied as a `folder_id` query string parameter. # Folder Locks When using any folder lock API endpoints, you must be authenticated as the owner/co-owner of the folder you are trying to access. ## Folder ID The `id` of any folder can be determined by visiting a folder in the web application and copying the `id` from the URL. For example, for the URL `https://*.app.box.com/folder/123` the `folder_id` is `123`. **Reference:** https://developer.box.com/guides/folders/single/get-locks/ --- ## Untitled *Type: guide | Category: Folders * Single Folders The following pages discuss how to do various single folder methods. # Single Folders The following pages discuss how to do various single folder methods. **Reference:** https://developer.box.com/guides/folders/single/ --- ## Untitled *Type: guide | Category: Folders * Move Folder To move a folder, update the ID of its parent folder. Operation details This call will return synchronously. This holds true… # Move Folder To move a folder, update the ID of its parent folder. ## Operation details This call will return synchronously. This holds true even for folder moves when the folder contains a large number of items in all of its descendants. For very large folders, this means the call could take minutes or hours to complete and some parts of the tree are locked during moves. When moving a folder, part of the file tree will be locked, mainly the source folder and all of its descendants, as well as the destination folder. For the duration of the move you cannot perform other move, copy, delete, or restore operation on any of the locked folders. ## Moving a subfolder to a private folder When you attempt to move a collaborated subfolder to a private one, you will get the [`cannot_make_collaborated_subfolder_private`](../../api-calls/permissions-and-errors/common-errors.md#400-bad-request) error. In such a case, specify the ID of the user that folder belongs to setting the `owned_by.id` parameter in the request: **Reference:** https://developer.box.com/guides/folders/single/move/ --- ## Untitled *Type: guide | Category: Folders * Update Folder To update a folder in Box you will need to call the following API. Name restrictions There are some restrictions to the folder… # Update Folder To update a folder in Box you will need to call the following API. ## Name restrictions There are some restrictions to the folder name. Names containing non-printable ASCII characters, forward and backward slashes (`/`, `\`), as well as names with trailing spaces are prohibited. Additionally, the names `.` and `..` are reserved names and therefore also prohibited. ## Timeout Timeout for this operation is 60 seconds. The operation will complete after a `HTTP 503` has been returned. **Reference:** https://developer.box.com/guides/folders/single/update/ --- ## Untitled *Type: guide | Category: Folders * Rename Folder To rename a folder in Box you will need to provide our API with a new name for the folder. Name restrictions There are some… # Rename Folder To rename a folder in Box you will need to provide our API with a new `name` for the folder. # Name restrictions There are some restrictions to the folder name. Names containing non-printable ASCII characters, forward and backward slashes (`/`, `\`), as well as names with trailing spaces are prohibited. Additionally, the names `.` and `..` are reserved names and therefore also prohibited. **Reference:** https://developer.box.com/guides/folders/single/rename/ --- ## Untitled *Type: guide | Category: Integration Mappings * Delete Slack Integration Mapping The DELETE integration_mappings/slack/:integration_mapping_id call removes the mapping between the channel… # Delete Slack Integration Mapping The `DELETE integration_mappings/slack/:integration_mapping_id` call removes the mapping between the channel and the folder. A new mapping and a new folder in the default folder structure will be created when the next file is uploaded to the channel. Deleting the mapping does not delete the Box folder, or the Slack channel. **Reference:** https://developer.box.com/guides/integration-mappings/slack-mappings/delete-mapping/ --- ## Untitled *Type: guide | Category: Integration Mappings * Create Slack Integration Mapping Use the POST integration_mappings/slack/:integration_mapping_id call to create a mapping. To make it work… # Create Slack Integration Mapping Use the `POST integration_mappings/slack/:integration_mapping_id` call to create a mapping. To make it work, you need `box_item` and `partner_item` parameters, which refer to a Box folder and a Slack channel, respectively. Remember that before the mapping can be created, this service account must be set as a co-owner role on the folder that is being mapped. If you encounter any errors, see the [troubleshooting guide](g://integration-mappings/slack-mappings/troubleshooting). You can provide options that change the default settings for the created mapping. For example setting `is_access_management_disabled` to `true` will disable collaboration management. Slack channel members will not become collaborators on the channel folder and no shared links will be created for channels with 1000+ members. ## Create Slack Integration Mapping with Box SDK Use Box SDK to automatically create the Integration Mapping, including a co-owner collaboration of the service account on the Slack channel - Box folder mapping. To do so, use this script: ``` const BoxSDK = require('box-node-sdk'); const axios = require('axios'); const integrationMappingsApiUrl = 'https://api.box.com/2.0/integration_mappings/slack' const boxFolderId = 'PASTE YOUR FOLDER ID HERE'; const slackChannelId = 'PASTE YOUR CHANNEL ID HERE'; const slackOrgId = 'PASTE YOUR SLACK ORG ID HERE (CHANGE TO WORKSPACE ID IF NECESSARY)'; const developerToken = 'PASTE YOUR DEVELOPER TOKEN HERE'; let serviceAccountId = '<PLACEHOLDER>'; const client = BoxSDK.getBasicClient(developerToken); async function postIntegrationMappingSlack(){ return axios.post(integrationMappingsApiUrl, { partner_item: { id: slackChannelId, slack_org_id: slackOrgId, // change slack_org_id to slack_workspace_id if Box for Slack is installed on the workspace level type: "channel" }, box_item: { id: boxFolderId, type: "folder" } }, { headers: { 'Authorization': `Bearer ${developerToken}` } }); } function isPlaceholder(str){ return str === '<PLACEHOLDER>'; } async function addCoowner(serviceAccountId, folderId){ try { await client.collaborations.createWithUserID(serviceAccountId, folderId, 'co-owner') } catch (error){ if(error.response.body.code === 'user_already_collaborator'){ console.log('Service account already collaborated in the co-owner role.') } else { throw error; } } } async function logServiceAccountId() { try { await postIntegrationMappingSlack(); } catch (error) { console.log(`Replace the value of serviceAccountId with: ${error.response.data.context_info.service_account_id} and re-run the script.`) } } async function createSlackIntegrationMapping() { if(isPlaceholder(serviceAccountId)){ await logServiceAccountId(); } else { await addCoowner(serviceAccountId, boxFolderId); await postIntegrationMappingSlack(); } } module.exports = { createSlackIntegrationMapping } ``` Make sure to replace `PLACEHOLDER` with the logged value of `serviceAccountId`. **Reference:** https://developer.box.com/guides/integration-mappings/slack-mappings/create-mapping/ --- ## Untitled *Type: guide | Category: Integration Mappings * Slack Integration Mappings When Box as the Content Layer is enabled, Box folder is created for each channel, and all files uploaded to this… # Slack Integration Mappings When [Box as the Content Layer](https://support.box.com/hc/en-us/articles/4415585987859-Box-as-the-Content-Layer-for-Slack) is enabled, Box folder is created for each channel, and all files uploaded to this channel are stored in this folder. The [Integration Mappings API](r://integration-mapping) allows the Box Enterprise Admin (EA) to customize the upload folder of the Slack channel to any folder within the enterprise instead of relying on the default one. **Reference:** https://developer.box.com/guides/integration-mappings/slack-mappings/ --- ## Untitled *Type: guide | Category: Integration Mappings * List Slack Integration Mappings Use the GET /integration_mappings/slack call to fetch and filter the mappings, both the ones created… # List Slack Integration Mappings Use the `GET /integration_mappings/slack` call to fetch and filter the mappings, both the ones created manually by the admin those created automatically by the integration. **Reference:** https://developer.box.com/guides/integration-mappings/slack-mappings/list-mappings/ --- ## Untitled *Type: guide | Category: Integration Mappings * Slack Integration Mappings Setup Before starting work with Integration Mappings API, perform the following steps. Roles Make sure you have… # Slack Integration Mappings Setup Before starting work with Integration Mappings API, perform the following steps. ## Roles Make sure you have an Admin or a Co-admin role. ## Install and configure Box for Slack 1. Install [Box for Slack](https://support.box.com/hc/en-us/articles/360044195313-Installing-and-Using-the-Box-for-Slack-Integration) in the relevant Slack workspace or organizations. 2. Enable the [Box as Content Layer for Slack](https://support.box.com/hc/en-us/articles/4415585987859-Box-as-the-Content-Layer-for-Slack). 3. Make sure the service account you are using is a collaborator on the folder that will be mapped. To do so, use the `Invite People` folder option to invite the service account as a collaborator. If you encounter any errors, see the [troubleshooting guide](g://integration-mappings/slack-mappings/troubleshooting). ## Create Box application 1. Create a [platform app with OAuth authentication](g://authentication/oauth2/oauth2-setup) in the [Box developer console](https://app.box.com/developers/console) 2. Open the application and enable the **Manage enterprise properties** application scope under **Configuration** > **Required Access Scopes**. For Slack-side channel validation, the [Integration Mappings API](e://get-integration-mappings-slack) is calling the Slack API. ## Authorization To authorize your Integration Mappings requests: 1. Navigate to the [platform app](g://applications/app-types/platform-apps) that you have created as one of the prerequisites. 2. Generate a [developer token](g://authentication/tokens/developer-tokens) and add it to the HTTP header of each request: ``` Authorization: Bearer {developer_token} ``` The developer token is valid for 60 minutes. After that time you need to generate it again. **Reference:** https://developer.box.com/guides/integration-mappings/slack-mappings/setup/ --- ## Untitled *Type: guide | Category: Integration Mappings * Slack Integration Mappings Troubleshooting As the Integration Mappings API performs different types of validation, some errors might occur… # Slack Integration Mappings Troubleshooting As the Integration Mappings API performs different types of validation, some errors might occur. You can find descriptions and solutions to these common errors below. ## Service account is not a co-owner of the folder The [Box as Content Layer for Slack](https://support.box.com/hc/en-us/articles/4415585987859-Box-as-the-Content-Layer-for-Slack) service account must be added as an owner or co-owner of the folder to manage collaborations and uploads. ``` Box as File Storage for Slack (user id: 123, user email: user@example.com) must be a collaborator in role co-owner or owner of the folder example123, before it can be mapped to the channel example123. Please create a collaboration or ensure the ownership for Box as File Storage for Slack and retry.` ``` To resolve this error use the data in the response to make sure the service account has the necessary role to perform the mapping. ``` "context_info": { "service_account_id": "12345678", "service_account_email": "AutomationUser_12345678_gdueygwe@boxdevedition.com", } ``` Perform the following steps: 1. Copy the `service_account_email` from `context_info`. 2. In the folder settings, use the `Invite People` option to invite the service account as a co-owner. ## Channel is already mapped to a folder in Box API returns the following error when you attempt to create a mapping for a channel that already has a Box folder mapped: ``` Channel: example123 is already mapped to a folder in Box. ``` If you wanted to start using a new folder, use `GET` to retrieve the `id` of the mapping and then the `UPDATE` method to update the target Box folder. ## Channel was not found API returns this error if the Slack bot associated with the integration can not retrieve information about the channel. ``` Channel: example123 was not found. If it is a private channel, ensure that Box has been added to the channel. ``` Ensure that the `partner_item` is correct - make sure you provide `slack_org_id` for org installations and `slack_workspace_id` for workspace ones. If the channel is private, ensure that the Slack bot has been added to the channel. ## Channel not suitable for Custom File Storage (CFS) ``` Channel: example123 is not suitable for CFS. Slack Connect channels with a pending Connect status can not be mapped to Box folders. ``` Slack Connect channels (cross enterprise channels) are currently not supported as a part of Box as a Content Layer for Slack. ## Box folder externally owned Box folder selected for mapping must be owned by the enterprise that the Admin is part of. ``` Box folder: example123 cannot be mapped, because it is externally owned. Mapped folder must belong to the enterprise: example_enterprise. ``` ## Custom File Storage (CFS) turned off API returns this error when you're trying to create the mapping for an enterprise that has Box for Slack installed, but did not enable [Box as a Content Layer for Slack](https://support.box.com/hc/en-us/articles/4415585987859-Box-as-the-Content-Layer-for-Slack). ## Box enterprise mismatch API returns this error when there is a mismatch between the Admin’s enterprise and Box for Slack configuration. See [Installing and using Box for Slack](https://support.box.com/hc/en-us/articles/360044195313-Installing-and-Using-the-Box-for-Slack-Integration) for information on how to enable Box for Slack. **Reference:** https://developer.box.com/guides/integration-mappings/slack-mappings/troubleshooting/ --- ## Untitled *Type: guide | Category: Integration Mappings * Delete Teams integration mappings The DELETE integration_mappings_teams_id call removes the mapping between the channel and the folder. A… # Delete Teams integration mappings The `DELETE integration_mappings_teams_id` call removes the mapping between the channel and the folder. A new mapping and a new folder in the default folder structure will be created when the next file is uploaded to the channel. Deleting the mapping does not delete the Box folder or the Teams channel. **Reference:** https://developer.box.com/guides/integration-mappings/teams-mappings/delete-mappings/ --- ## Untitled *Type: guide | Category: Integration Mappings * Create Teams integration mappings Use the POST integration_mappings_teams call to create a mapping. To make it work, you need the box_item… # Create Teams integration mappings Use the `POST integration_mappings_teams` call to create a mapping. To make it work, you need the `box_item` and the `partner_item` parameters, which refer to a Box folder and a Teams channel, respectively. **Reference:** https://developer.box.com/guides/integration-mappings/teams-mappings/create-mappings/ --- ## Untitled *Type: guide | Category: Integration Mappings * Update Slack Integration Mapping Use the PUT integration_mappings/slack/:integration_mapping_id call to update the existing mapping or the… # Update Slack Integration Mapping Use the `PUT integration_mappings/slack/:integration_mapping_id` call to update the existing mapping or the target Box folder. **Reference:** https://developer.box.com/guides/integration-mappings/slack-mappings/update-mapping/ --- ## Untitled *Type: guide | Category: Integration Mappings * List Teams integration mappings Use the GET /integration_mappings/teams call to fetch and filter the mappings, both the ones created… # List Teams integration mappings Use the `GET /integration_mappings/teams` call to fetch and filter the mappings, both the ones created manually by the admin and those created automatically by the integration. **Reference:** https://developer.box.com/guides/integration-mappings/teams-mappings/list-mappings/ --- ## Untitled *Type: guide | Category: Integration Mappings * Teams integration mappings Before you can start working with Teams integration mapping API, perform the steps below. Prerequisites Admin or… # Teams integration mappings Before you can start working with [Teams](https://support.box.com/hc/en-us/articles/360044667034-Introducing-Box-for-Microsoft-Teams) integration mapping API, perform the steps below. ## Prerequisites - Admin or Co-Admin role - [Box for Microsoft Teams](https://support.box.com/hc/en-us/articles/360050737154-Assigning-a-Default-Box-Folder-to-a-Teams-Channel-or-Chat) integration installed ## Create a Box application 1. Create a [platform app with OAuth authentication](g://authentication/oauth2/oauth2-setup) in the [Box developer console](https://app.box.com/developers/console). 2. Open the application and enable the **Manage enterprise properties** application scope under **Configuration** > **Required Access Scopes**. ## Authorize integration mappings requests 1. Navigate to the [platform app](g://applications/app-types/platform-apps) that you have created as one of the prerequisites. 2. Generate a [developer token](g://authentication/tokens/developer-tokens) and add it to the HTTP header of each request: ``` Authorization: Bearer {developer_token} ``` The developer token is valid for 60 minutes. After that time, you need to generate it again. **Reference:** https://developer.box.com/guides/integration-mappings/teams-mappings/ --- ## Untitled *Type: guide | Category: Integration Mappings * Update Teams integration mappings Use the PUT integration_mappings_teams_id call to update the existing mapping of the target Box folder. # Update Teams integration mappings Use the `PUT integration_mappings_teams_id` call to update the existing mapping of the target Box folder. **Reference:** https://developer.box.com/guides/integration-mappings/teams-mappings/update-mappings/ --- ## Untitled *Type: guide | Category: Metadata * Create a metadata cascade policy When a metadata template has been applied to a folder, a metadata cascade policy can be created by calling… # Create a metadata cascade policy When a metadata template has been applied to a folder, a metadata cascade policy can be created by calling the [`POST /metadata_cascade_policies`](e://post_metadata_cascade_policies) API endpoint with the `folder_id` of the folder to apply the policy to, and the `scope` and `templateKey` of metadata template to. To get the `scope` and `templateKey` for a template, either [list all metadata templates](g://metadata/templates/list), or [list all instances on an file](g://metadata/instances/list). A cascade policy can only be created if a metadata instance has already been applied to the folder with the given `scope` and `templateKey`. **Reference:** https://developer.box.com/guides/metadata/cascades/create/ --- ## Untitled *Type: guide | Category: Metadata * Delete a metadata cascade policy A metadata cascade policy can be deleted by calling the DELETE /metadata_cascade_policies/:id API endpoint… # Delete a metadata cascade policy A metadata cascade policy can be deleted by calling the [`DELETE /metadata_cascade_policies/:id`](e://delete_metadata_cascade_policies_id) API endpoint with the `id` of the policy to remove. To get the `id` of the policy, [list all policies](g://metadata/cascades/list) for the folder. **Reference:** https://developer.box.com/guides/metadata/cascades/delete/ --- ## Untitled *Type: guide | Category: Metadata * Get a metadata cascade policy Information for a metadata cascade policy can be retrieved by calling the GET /metadata_cascade_policies/:id… # Get a metadata cascade policy Information for a metadata cascade policy can be retrieved by calling the [`GET /metadata_cascade_policies/:id`](e://get_metadata_cascade_policies_id) API endpoint with the `id` of the policy. To get the `id` of the policy, [list all policies](g://metadata/cascades/list) for the folder. **Reference:** https://developer.box.com/guides/metadata/cascades/get/ --- ## Untitled *Type: guide | Category: Metadata * Force-apply metadata to all items in a folder When a metadata cascade policy already exists on a folder, the metadata instance can be force… # Force-apply metadata to all items in a folder When a metadata cascade policy already exists on a folder, the metadata instance can be force-applied to all items in a folder by calling the [`POST /metadata_cascade_policies/:id/apply`](e://post_metadata_cascade_policies_id_apply) API endpoint with the `id` of the metadata cascade policy. To get the `id` of the policy, [list all policies](g://metadata/cascades/list) for the folder. The metadata cascade operation will be started off asynchronously. The API call will return directly with the `202 Accepted` HTTP status code before the cascade operation is complete. There is currently no way to check for when this operation is finished. ## Conflict resolution An additional `conflict_resolution` parameter can be passed to this API to define how to deal with any existing instances of the template on any of the items in the folder. By default, without setting any value for `conflict_resolution` this API will preserve the existing value on any items. When the value is set to `overwrite`, it will force-apply the value of the template attached to the cascade policy over any existing value. **Reference:** https://developer.box.com/guides/metadata/cascades/force-apply/ --- ## Untitled *Type: guide | Category: Metadata * Metadata cascade policies A metadata cascade policy describes how metadata instances applied to a folder should be applied to any item… # Metadata cascade policies A metadata cascade policy describes how metadata instances applied to a folder should be applied to any item within that folder. For example, a user might assign the same `invoiceData` metadata template to a project folder allowing it to automatically apply to all the files and folders within that project folder. Every policy specifies exactly one metadata template instance and one folder. ## Activation and permissions To use metadata cascade policies, an enterprise admin needs to enable them for the entire enterprise. In the **Admin Console**, select **Enterprise Settings**, **Content & Sharing**, **Cascading Folder Level Metadata**. Click the Edit configuration button and pick who can apply cascade policies to folders. Any user with edit permissions on a folder and the ability to create cascade policies can create metadata cascade policies for that given folder. ## Limitations There is some delay from file upload to the metadata being applied. This very much depends on the number of items in a folder. Metadata cascade operations are performed asynchronously and there is currently no way to check when all metadata has been cascaded to all files. **Reference:** https://developer.box.com/guides/metadata/cascades/ --- ## Untitled *Type: guide | Category: Metadata * Date metadata field A metadata field of type date is displayed to a user as a date picker. Although dates are presented to users in the Box… # Date metadata field A metadata field of type `date` is displayed to a user as a date picker. Although dates are presented to users in the Box Web app as date-pickers, the actual dates are actually stored as in `RFC3339` format, with up to millisecond precision. The time part of the date is always set to `T00:00:00.000Z`. ## Create a date field A `date` field can be added to a metadata template either when creating a metadata template, or when updating a template with the `addField` operation. The required attributes for a `date` field are a `type`, a `displayName`, and a `key`. ``` { "scope": "enterprise", "displayName": "Contract", "fields": [ { "type": "date", "key": "effective_date", "displayName": "Effective Date", "description": "The effective date when the contract goes in effect", "hidden": false } ] } ``` Optionally, a `description` can be provided that is shown to a user in the UI, and the field can be set to `hidden` to hide it from users in the web and mobile apps. ## Update a date field A `date` template field can be updated by updating the template it belongs to. Updates to templates happen through **operations** to ensure that any template that is already assigned to a file or folder is updated as well. When updating a `date` metadata field, the only relevant operation is the `editField` operation, which can be used to change the field's `key`, `displayName`, `description`, and `hidden` values. ``` [ { "op": "editField", "fieldKey": "effective_date", "data": { "displayName": "Effective Contract Date", "description": "The contract's effective date", "key": "effective_contract_date", "hidden": true } } ] ``` This will affect existing instances of this template. **Reference:** https://developer.box.com/guides/metadata/fields/date/ --- ## Untitled *Type: guide | Category: Metadata * List metadata cascade policies Metadata cascade policies can be listed for a folder by calling the GET /metadata_cascade_policies API… # List metadata cascade policies Metadata cascade policies can be listed for a folder by calling the [`GET /metadata_cascade_policies`](e://get_metadata_cascade_policies) API endpoint with a `folder_id`. ## Pagination This API uses [marker-based pagination](g://api-calls/pagination/marker-based) and can return a `next_marker` value in the response body to indicate that more templates might be available. **Reference:** https://developer.box.com/guides/metadata/cascades/list/ --- ## Untitled *Type: guide | Category: Metadata * Enum metadata field A metadata field of type enum is displayed to a user as a dropdown list. The user can select one item from the list. An… # Enum metadata field A metadata field of type `enum` is displayed to a user as a dropdown list. The user can select one item from the list. An `enum` allows a user to select zero or a single value. To allow a user to select multiple values, use the [`multiSelect`](g://metadata/fields/multi-select) template field. ## Create an enum field An `enum` field can be added to a metadata template either when creating a metadata template, or when updating a template with the `addField` operation. The required attributes for an `enum` field are a `type`, a `displayName`, a `key`, and a list of options. ``` { "scope": "enterprise", "displayName": "Contract", "fields": [ { "type": "enum", "key": "customer_state", "displayName": "Customer State", "description": "The US state where the customer is located", "hidden": false, "options": [ {"key": "N/A"}, {"key": "AL"}, {"key": "AK"} ] } ] } ``` Optionally, a `description` can be provided that is shown to a user in the UI, and the field can be set to `hidden` to hide it from users in the web and mobile apps. ## Update an enum field An `enum` template field can be updated by updating the template it belongs to. Updates to templates happen through **operations** to ensure that any template that is already assigned to a file or folder is updated as well. ### Change basic field values When updating an `enum` metadata field, one of the possible operations is the `editField` operation, which can be used to change the field's `key`, `displayName`, `description`, and `hidden` values. ``` [ { "op": "editField", "fieldKey": "customer_state", "data": { "displayName": "Customer State (USA)", "key": "customer_state_usa" } } ] ``` The `fieldKey` here represents the original key of the field to change. The `data.key` field is the new key of the field. This will affect existing instances of this template. ### Add an option Adding an option to an `enum` field can be achieved through the `addEnumOption` operation. The operation expects the `fieldKey` to be set to the key of the `enum` field to change, and a `data` object with the `key` of the new option to add. ``` [ { "op": "addEnumOption", "fieldKey": "customer_state", "data": { "key": "AR" } } ] ``` The list of options should now be as follows. ``` ... "options": [ {"key": "N/A"}, {"key": "AL"}, {"key": "AK"}, {"key": "AR"} ] ... ``` This will affect existing instances of this template. ### Reorder options Reordering the options in an `enum` field can be achieved through the `reorderEnumOptions` operation. The operation expects the `fieldKey` to be set to the key of the `enum` field to change, and an `enumOptionKeys` array with the keys of the options in order. ``` [ { "op": "reorderEnumOptions", "fieldKey": "customer_state", "enumOptionKeys": [ "AL", "AK", "AR", "N/A" ] } ] ``` The list of options should now be as follows. ``` ... "options": [ {"key": "AL"}, {"key": "AK"}, {"key": "AR"}, {"key": "N/A"} ] ... ``` New options can not be added with this operation. This will affect existing instances of this template. ### Edit an option Editing an option of an `enum` field can be achieved through the `editEnumOption` operation. The operation expects the `fieldKey` to be set to the key of the `enum` field to change, and an `enumOptionKey` to be set to the key of the field option. Finally, it expects a `data` object with the new `key` of the field option. ``` [ { "op": "editEnumOption", "fieldKey": "customer_state", "enumOptionKey": "N/A", "data": { "key": "Outside USA" } } ] ``` The list of options should now be as follows. ``` ... "options": [ {"key": "AL"}, {"key": "AK"}, {"key": "AR"}, {"key": "Outside USA"} ] ... ``` This will affect existing instances of this template. ### Remove an option Removing an option from an `enum` field can be achieved through the `removeEnumOption` operation. The operation expects the `fieldKey` to be set to the key of the `enum` field to change, and an `enumOptionKey` to be set to the key of the field option to remove. ``` [ { "op": "removeEnumOption", "fieldKey": "customer_state", "enumOptionKey": "AL" } ] ``` The list of options should now be as follows. ``` ... "options": [ {"key": "AK"}, {"key": "AR"}, {"key": "Outside USA"} ] ... ``` This will affect existing instances of this template. Any fields that were set to this value will have the value reset to `null`. **Reference:** https://developer.box.com/guides/metadata/fields/enum/ --- ## Untitled *Type: guide | Category: Metadata * Float metadata field A metadata field of type float is displayed to a user as a standard text-field that only accepts numeric input. Create… # Float metadata field A metadata field of type `float` is displayed to a user as a standard text-field that only accepts numeric input. ## Create a float field A `float` field can be added to a metadata template either when creating a metadata template, or when updating a template with the `addField` operation. The required attributes for a `float` field are a `type`, a `displayName`, and a `key`. ``` { "scope": "enterprise", "displayName": "Contract", "fields": [ { "type": "float", "key": "contract_value", "displayName": "Contract Value", "description": "The contract's total value", "hidden": false } ] } ``` Optionally, a `description` can be provided that is shown to a user in the UI, and the field can be set to `hidden` to hide it from users in the web and mobile apps. ## Update a float field A `float` template field can be updated by updating the template it belongs to. Updates to templates happen through **operations** to ensure that any template that is already assigned to a file or folder is updated as well. When updating a `float` metadata field, the only relevant operation is the `editField` operation, which can be used to change the field's `key`, `displayName`, `description`, and `hidden` values. ``` [ { "op": "editField", "fieldKey": "contract_value", "data": { "displayName": "Total Annual Contract Value", "description": "The contract's total anual value", "key": "contract_tav", "hidden": true } } ] ``` This will affect existing instances of this template. **Reference:** https://developer.box.com/guides/metadata/fields/float/ --- ## Untitled *Type: guide | Category: Metadata * Metadata fields A metadata template field describes a specific piece of data within a metadata template. For example, the ID of an invoice… # Metadata fields A [metadata template field](r://metadata-template/#param-fields) describes a specific piece of data within a metadata template. For example, the ID of an invoice might be represented as an `id` field on an `invoiceData` template. Every metadata template contains a list of `field` objects, and each field is of one of the following types: | | | | --- | --- | | string | A text field | | float | A numeric input field | | date | A date picker field | | enum | A dropdown list for selecting a single value | | multiSelect | A dropdown list for selecting multiple values | ## Basic field types The basic field types are a `string` for a text field, `float` for a numeric field, and `date` for a date-time picker field. ## Lists field types Additionally, metadata templates support two field types to represent dropdown lists. An `enum` field represents a list of predefined items that a user can select, while a `multiSelect` field represents a list of items where the user can select more than one value. **Reference:** https://developer.box.com/guides/metadata/fields/ --- ## Untitled *Type: guide | Category: Metadata * String metadata field A metadata field of type string is displayed to a user as a standard text-field. Create a string field A string field… # String metadata field A metadata field of type `string` is displayed to a user as a standard text-field. ## Create a string field A `string` field can be added to a metadata template either when creating a metadata template, or when updating a template with the `addField` operation. The required attributes for a `string` field are a `type`, a `displayName`, and a `key`. ``` { "scope": "enterprise", "displayName": "Customer", "fields": [ { "type": "string", "key": "name", "displayName": "Name", "description": "The customer's legal name", "hidden": false } ] } ``` Optionally, a `description` can be provided that is shown to a user in the UI, and the field can be set to `hidden` to hide it from users in the web and mobile apps. ## Update a string field A `string` template field can be updated by updating the template it belongs to. Updates to templates happen through **operations** to ensure that any template that is already assigned to a file or folder is updated as well. When updating a `string` metadata field, the only relevant operation is the `editField` operation, which can be used to change the field's `key`, `displayName`, `description`, and `hidden` values. ``` [ { "op": "editField", "fieldKey": "name", "data": { "displayName": "Customer Name", "description": "The contact name at the customer", "key": "customer_name", "hidden": true } } ] ``` This will affect existing instances of this template. **Reference:** https://developer.box.com/guides/metadata/fields/string/ --- ## Untitled *Type: guide | Category: Metadata * Multi Select metadata field A metadata field of type multiSelect is displayed to a user as a dropdown list. The user can select multiple… # Multi Select metadata field A metadata field of type `multiSelect` is displayed to a user as a dropdown list. The user can select multiple items from the list. A `multiSelect` field allows a user to select zero, one, or more values. To force a user to select only one value at most, use the [`enum`](g://metadata/fields/enum) template field. ## Create a multiSelect field A `multiSelect` field can be added to a metadata template either when creating a metadata template, or when updating a template with the `addField` operation. The required attributes for a `multiSelect` field are a `type`, a `displayName`, a `key`, and a list of options. ``` { "scope": "enterprise", "displayName": "Contract", "fields": [ { "type": "multiSelect", "key": "box_entity", "displayName": "Box Entity", "description": "The Box entity that this contract belongs to", "hidden": false, "options": [ {"key": "Box, Inc"}, {"key": "Box.com (UK) Ltd."}, {"key": "KK Box Japan"} ] } ] } ``` Optionally, a `description` can be provided that is shown to a user in the UI, and the field can be set to `hidden` to hide it from users in the web and mobile apps. ## Update a multiSelect field A `multiSelect` template field can be updated by updating the template it belongs to. Updates to templates happen through **operations** to ensure that any template that is already assigned to a file or folder is updated as well. ### Change basic field values When updating a `multiSelect` metadata field, one of the possible operations is the `editField` operation, which can be used to change the field's `key`, `displayName`, `description`, and `hidden` values. ``` [ { "op": "editField", "fieldKey": "box_entity", "data": { "displayName": "Box Entities", "key": "box_entities" } } ] ``` The `fieldKey` here represents the original key of the field to change. The `data.key` field is the new key of the field. This will affect existing instances of this template. ### Add an option Adding an option to a `multiSelect` field can be achieved through the `addMultiSelectOption` operation. The operation expects the `fieldKey` to be set to the key of the `multiSelect` field to change, and a `data` object with the `key` of the new option to add. ``` [ { "op": "addMultiSelectOption", "fieldKey": "box_entity", "data": { "key": "Box (NL) BV" } } ] ``` The list of options should now be as follows. ``` ... "options": [ {"key": "Box, Inc"}, {"key": "Box.com (UK) Ltd."}, {"key": "KK Box Japan"}, {"key": "Box (NL) BV"} ] ... ``` This will affect existing instances of this template. ### Reorder options Reordering the options in a `multiSelect` field can be achieved through the `reorderMultiSelectOptions` operation. The operation expects the `fieldKey` to be set to the key of the `multiSelect` field to change, and a `multiSelectOptionKeys` array with the keys of the options in order. ``` [ { "op": "reorderMultiSelectOptions", "fieldKey": "box_entity", "multiSelectOptionKeys": [ "Box, Inc", "Box.com (UK) Ltd.", "Box (NL) BV", "KK Box Japan" ] } ] ``` The list of options should now be as follows. ``` ... "options": [ {"key": "Box, Inc"}, {"key": "Box.com (UK) Ltd."}, {"key": "Box (NL) BV"}, {"key": "KK Box Japan"} ] ... ``` New options can not be added with this operation. This will affect existing instances of this template. ### Edit an option Editing an option of a `multiSelect` field can be achieved through the `editMultiSelectOption` operation. The operation expects the `fieldKey` to be set to the key of the `multiSelect` field to change, and a `multiSelectOptionKey` to be set to the key of the field option. Finally, it expects a `data` object with the new `key` of the field option. ``` [ { "op": "editMultiSelectOption", "fieldKey": "box_entity", "multiSelectOptionKey": "Box (NL) BV", "data": { "key": "Box.nl BV" } } ] ``` The list of options should now be as follows. ``` ... "options": [ {"key": "Box, Inc"}, {"key": "Box.com (UK) Ltd."}, {"key": "Box.nl BV"}, {"key": "KK Box Japan"} ] ... ``` This will affect existing instances of this template. ### Remove an option Removing an option from a `multiSelect` field can be achieved through the `removeMultiSelectOption` operation. The operation expects the `fieldKey` to be set to the key of the `multiSelect` field to change, and a `multiSelectOptionKey` to be set to the key of the field option to remove. ``` [ { "op": "removeMultiSelectOption", "fieldKey": "customer_state", "multiSelectOptionKey": "KK Box Japan" } ] ``` The list of options should now be as follows. ``` ... "options": [ {"key": "Box, Inc"}, {"key": "Box.com (UK) Ltd."}, {"key": "Box.nl BV"} ] ... ``` This will affect existing instances of this template. Any fields that were set to this value will have the value removed from its list of selected values. **Reference:** https://developer.box.com/guides/metadata/fields/multi-select/ --- ## Untitled *Type: guide | Category: Metadata * Remove metadata from an item Removing an instance of a metadata template assigned to a file or folder can be done using the item's id, and… # Remove metadata from an item Removing an instance of a metadata template assigned to a file or folder can be done using the item's `id`, and the template's `templateKey` and `scope`. Metadata [scopes](g://metadata/scopes) can be either `global` for templates available to all enterprises, `enterprise` for templates available to the current enterprise, or the `enterprise_:id` for templates belonging to an enterprise whose ID is the `:id` value in the scope name. ## Remove metadata from an file Deleting an instance of a metadata template from a file be achieved by calling the [`DELETE /files/:file_id/metadata/:templateKey/schema`](e://delete_files_id_metadata_id_id) API. This API returns a `204 No Content` API response with no response body when the instance has been successfully removed from the file. ## Remove metadata from an folder Deleting an instance of a metadata template from a folder be achieved by calling the [`DELETE /folders/:folder_id/metadata/:templateKey/schema`](e://delete_folders_id_metadata_id_id) API. This API returns a `204 No Content` API response with no response body when the instance has been successfully removed from the folder. **Reference:** https://developer.box.com/guides/metadata/instances/delete/ --- ## Untitled *Type: guide | Category: Metadata * Apply metadata to an item A metadata template can be applied to a file or folder using the item's id, the template's templateKey and scope… # Apply metadata to an item A metadata template can be applied to a file or folder using the item's `id`, the template's `templateKey` and `scope`, and a set of values for each field in the template. Metadata [scopes](g://metadata/scopes) can be either `global` for templates available to all enterprises, `enterprise` for templates available to the current enterprise, or the `enterprise_:id` for templates belonging to an enterprise whose ID is the `:id` value in the scope name. You can assign up to 100 templates to specific file or folder. ## Apply metadata to a file To apply an instance of a metadata template to a file, call the [`POST /files/:file_id/metadata/:scope/:templateKey`](e://post_files_id_metadata_id_id) API endpoint with the file's `file_id`, the template's `scope` and `templateKey`, and an optional set of values for each [field](g://metadata/fields) in the template. To get the `scope` and `templateKey` for a template, either [list all metadata templates](g://metadata/templates/list), or [list all instances on an file](g://metadata/instances/list). ## Tuple already exists error When there is already metadata applied to this file for the given metadata template, a error is returned with the error code `tuple_already_exists`. In this case, it this case the instance needs to be updated instead. ## Apply metadata to a folder To apply an instance of a metadata template to a folder, call the [`POST /folders/:folder_id/metadata/:scope/:templateKey`](e://post_folders_id_metadata_id_id) API endpoint with the folder's `folder_id`, the template's `scope` and `templateKey`, and an optional set of values for each [field](g://metadata/fields) in the template. To get the `scope` and `templateKey` for a template, either [list all metadata templates](g://metadata/templates/list), or [list all instances on an folder](g://metadata/instances/list). ## Tuple already exists error When there is already metadata applied to this folder for the given metadata template, a error is returned with the error code `tuple_already_exists`. In this case, it this case the instance needs to be updated instead. ## Request body The body of the request can contain a value for each [field](g://metadata/fields) in the template. To inspect what fields are present on a template, inspect a metadata metadata template. For example, let's assume the following template. ``` { "id": "8120731a-41e4-11ea-b77f-2e728ce88125", "type": "metadata_template", "templateKey": "productInfo", "scope": "enterprise_1234567", "displayName": "Product Info", "hidden": false, "copyInstanceOnItemCopy": true, "fields": [ { "id": "feed71de-41e5-11ea-b77f-2e728ce88125", "type": "string", "key": "name", "displayName": "Name", "hidden": false }, { "id": "02b36bb6-41e6-11ea-b77f-2e728ce88125", "type": "enum", "key": "category", "displayName": "Category", "hidden": false, "options": [ { "id": "06a7bcc2-41e6-11ea-b77f-2e728ce88125", "key": "SUVs" }, { "id": "0a50df02-41e6-11ea-b77f-2e728ce88125", "key": "Saloons" }, { "id": "0e466be0-41e6-11ea-b77f-2e728ce88125", "key": "Cabriolets" } ] } ] } ``` This template has 2 [template fields](g://metadata/fields), `name` and `category`. The `name` field is a regular text field, and the `category` is an enum. The request body to assign this template to a file or folder can include a value for any of the fields on the template. It is possible for the body to have no values for no fields. In this case, a valid example would be the following request body. ``` { "name": "Model 3", "category": "SUVs" } ``` One exception is a `global` scoped template with the key `properties` that can be used to assign any data to a template. Using this template, any set of key/value pairs can be assigned to a template. The `category` field in this example is an `enum` field and needs to be one of the available options on the field. **Reference:** https://developer.box.com/guides/metadata/instances/create/ --- ## Untitled *Type: guide | Category: Metadata * Metadata instances A metadata instance describes the relation between a metadata template and a file or folder. It includes the values that… # Metadata instances A [metadata instance](g://metadata/instances) describes the relation between a [metadata template](g://metadata/templates) and a file or folder. It includes the values that are assigned for every field of the template. For example, a user might have assigned an `invoiceData` metadata template to a file and provided 2 values, one for the invoice ID and one for the customer ID. You can assign up to 100 templates to specific file or folder. **Reference:** https://developer.box.com/guides/metadata/instances/ --- ## Untitled *Type: guide | Category: Metadata * Comparison to regular search At the surface the Metadata Query API seems very similar to the Search for content API, but there are several… # Comparison to regular search At the surface the [Metadata Query API](e://post_metadata_queries_execute_read) seems very similar to the [Search for content API](e://get_search), but there are several important differences in how they operate. At a high level the Metadata Query API is optimized for exactness and throughput, while regular search is optimized for relevance to a human user. ## Deep comparison | | Metadata Query API | Search API | | --- | --- | --- | | What is indexed? | This API only returns files/folders based on the values in the metadata templates that are searched. | This API returns files, folders and web links based on values in the item names, descriptions, contents (up to the first 10,000 bytes) as well as the associated metadata template instances. | | Indexing time | This API returns accurate results as soon as metadata has been added, removed, updated or deleted for a file or folder. | This API is subject to a search indexing delay, which is typically 10 minutes, yet may be longer in some cases. This means that items may not be returned for more than 10 minutes after metadata has been updated. | | Matching | This API uses exact matching based on SQL conventions. Results are returned based on a specified sort order. | This API uses fuzzy matching and may return results that vary based on string tokenization, removal of special characters, and other search concepts. Result order is based on either relevance or the updated date of the item. | | Conditional logic | This API supports multi-part boolean expressions with comparison operators. | This API has limited support for querying by metadata. It only supports querying 1 metadata template at a time and only allows simple query operations. | | Response type | This API returns both the matched file/folder and the associated metadata matched by the query. | This API only returns the matched item. A subsequent API call is needed to return each item's metadata. | | Throughput | This API is currently subject to per-user rate limits and to a 10 requests per second per enterprise limit. | This API supports 6 searches per second per user, up to 60 searches per minute and 12 searches per second per enterprise. | | Scale | This API has no limit on the number of items with the specified metadata template that can be returned. It is recommended to only send queries which match no more than 2,000 results. | This API has no limit on number of items with the specified metadata template that can be returned, yet the response time increases significantly as the number of items matching the search grows. This API does have a limit of up to 10 million results for a query. It is recommended to only send queries which match no more than 50,000 results. | | Scope | This API is always limited to the content to which the user has access | This API may be either limited to the content to which the user has access (​user_content​) or to all content in the enterprise (​enterprise_content​). | Search API calls that match more than 10,000 results may result in slight changes in relevance. This may result in duplicate results and results that are not returned. Search should not be considered an ​exact matching​ solution on large data sets. ## Scenarios The following are some example scenarios that may help you decide what API to use. ### Searching an entire enterprise for a term You want to find all content in an enterprise with content or metadata that matches the keyword `Guarantee`. In this case the [Search API](e://get_search) is recommended. This API matches terms in both content and metadata, and can be scoped to find all content in the entire enterprise. ### Searching for multiple metadata values You want to find all documents with the metadata template `​Contract​` having a value greater than $100,000, a renewal date in 2019, and which are ​not​ associated with account number `​1234`. In this case the [Metadata Query API](e://post_metadata_queries_execute_read) is recommended. With a metadata query you can write boolean expressions that evaluate multiple fields in a metadata template, such as a number, a date, and a string. ### Mixing metadata and content search You want to find all documents with the metadata template `​Contract​` having a value greater than $100,000, renewal date in 2019, which are ​not​ associated with account number `​1234`, ​and which contain the term “Sale” in the title or document body. This scenarios is currently **not supported**. At the moment neither of the APIs supports mixing both fuzzy search (searching for "Sale") and the boolean expression matching metadata fields. **Reference:** https://developer.box.com/guides/metadata/queries/comparison/ --- ## Untitled *Type: guide | Category: Metadata * Get metadata on item Information about an instance of a metadata template assigned to a file or folder can be retrieved using the item's id… # Get metadata on item Information about an instance of a metadata template assigned to a file or folder can be retrieved using the item's `id`, and the template's `templateKey` and `scope`. Metadata [scopes](g://metadata/scopes) can be either `global` for templates available to all enterprises, `enterprise` for templates available to the current enterprise, or the `enterprise_:id` for templates belonging to an enterprise whose ID is the `:id` value in the scope name. ## Get metadata instance on file To get an instance of a metadata template on a file, call the [`GET /files/:file_id/metadata/:scope/:templateKey`](e://get_files_id_metadata_id_id) API endpoint with the file's `file_id` and the template's `scope` and `templateKey`. To get the `scope` and `templateKey` for a template, either [list all metadata templates](g://metadata/templates/list), or [list all instances on an file](g://metadata/instances/list). ## Get metadata instance on folder To get an instance of a metadata template on a folder, call the [`GET /folders/:folder_id/metadata/:scope/:templateKey`](e://get_files_id_metadata_id_id) API endpoint with the folder's `folder_id` and the template's `scope` and `templateKey`. To get the `scope` and `templateKey` for a template, either [list all metadata templates](g://metadata/templates/list), or [list all instances on an folder](g://metadata/instances/list). **Reference:** https://developer.box.com/guides/metadata/instances/get/ --- ## Untitled *Type: guide | Category: Metadata * Create a query A metadata query is a POST request to the ​/metadata_queries/execute_read- endpoint, in which the body contains all the parts… # Create a query A metadata query is a `POST` request to the ​`/metadata_queries/execute_read`- endpoint, in which the body contains all the parts of the metadata query. Most important here are the `from` attribute that specifies the template to search for, the `ancestor_folder_id` to specify the folder to search in, and the `query` to determine any template fields to search by. ``` curl -X POST https://api.box.com/2.0/metadata_queries/execute_read \ -H 'Authorization: Bearer <ACCESS_TOKEN>' \ -H 'Content-Type: application/json' \ -d '{ "from": "enterprise_123456.contractTemplate", "query": "amount >= :value", "query_params": { "value": 100 }, "fields": [ "name", "metadata.enterprise_123456.contractTemplate.customerName", "metadata.enterprise_123456.contractTemplate.amount" ], "ancestor_folder_id": "5555", "order_by": [ { "field_key": "amount", "direction": "asc" } ], "limit": 100 }' ``` For more details about all the available parameters, check out any of our other [metadata query guides](g://metadata/queries), or the associated endpoint reference. Learn more about the query syntax ## Response Any files or folders that match the query are returned in the API response. The body of the response is a JSON object with a list of `entries` for each file or folder, and a `next_marker` value that you can use to find the next page of results. Each of the entries will represent the file or folder that matched the query, and only any fields explicitly requested in the `field` parameter are returned. ``` { "entries": [ { "type": "file", "id": "1617554169109", "name": "My Contract.docx", "metadata": { "enterprise_123456": { "contractTemplate": { "$parent": "file_161753469109", "$scope": "enterprise_123456", "$template": "contractTemplate", "$version": 0, "customerName": "Phoenix Corp", "amount": 100 } } } } ], "limit": 20, "next_marker": "AAAAAmVYB1FWec8GH6yWu2nwmanfMh07IyYInaa7DZDYjgO1H4KoLW29vPlLY173OKsci6h6xGh61gG73gnaxoS+o0BbI1/h6le6cikjlupVhASwJ2Cj0tOD9wlnrUMHHw3/ISf+uuACzrOMhN6d5fYrbidPzS6MdhJOejuYlvsg4tcBYzjauP3+VU51p77HFAIuObnJT0ff" } ``` By default this API returns `20` items per page, but more items can be requested using marker-based pagination. Learn more about paginating query results **Reference:** https://developer.box.com/guides/metadata/queries/create/ --- ## Untitled *Type: guide | Category: Metadata * 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… # 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`](e://put_files_id_metadata_id_id) 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. 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`](e://put_folders_id_metadata_id_id) 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. 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](https://tools.ietf.org/html/rfc6902), 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](https://tools.ietf.org/html/rfc6901) `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. **Reference:** https://developer.box.com/guides/metadata/instances/update/ --- ## Untitled *Type: guide | Category: Metadata * List all metadata on an item Metadata instances can be listed for either a file or a folder. List metadata on a file To list all metadata… # List all metadata on an item Metadata instances can be listed for either a file or a folder. ## List metadata on a file To list all metadata instances on a file, call the [`GET /files/:file_id/metadata`](e://get_files_id_metadata) API endpoint. This API does not support paging and it will always return all of the metadata instances for this file. ## List metadata on a folder To list all metadata instances on any folder (except for the root folder), call the [`GET /folders/:file_id/metadata`](e://get_files_id_metadata) API endpoint. This API does not support paging and it will always return all of the metadata instances for this file. This API can not be used on the root folder with ID `0`. **Reference:** https://developer.box.com/guides/metadata/instances/list/ --- ## Untitled *Type: guide | Category: Metadata * Limitations A few limits apply to the the metadata query APIs. Files and folders ​A metadata query will only return items (files or folders… # Limitations A few limits apply to the the metadata query APIs. ## Files and folders ​A metadata query will only return items (files or folders) to which the requesting user has at least `previewer` access. ## Enterprise vs Global templates A metadata query only works with metadata templates that have been created by that enterprise. A query will not return results based on the content of free form key-value pairs stored in the `​global.properties` template. ## Classification metadata templates Box uses metadata templates to power its content classification. These metadata templates can not be used in metadata queries, as they will most likely run into issues regarding large results sets. More on this next. There are no immediate plans to start supporting these queries in the future. ## Recommended result set size Where possible it is recommended to only send requests for which the result set is less than 2,000 items.​ The ​result set​ is the entire collection of files and folder that match the metadata query exclusively based on evaluating the `​from​`, `​query​`, and `​query_params​` parameters - before the requesting user’s permissions and the `​ancestor_folder​` scope are considered. When sending a metadata query request for which the result set exceeds 2,000 items, the API can only guarantee that it returns all all matching results if both the following conditions are met. 1. The requesting user has at least **​Previewer** ​permission to all items in the result set 2. The ancestor folder contains all of the files in the result set When sending a metadata query request for which the result set exceeds 2,000 items and for which these conditions are not true, the API might return error with a 4XX response code indicating that the query will need to be restricted to return fewer results. For example, consider the following simplified representation of a metadata template called `catalogImages​` which has one string field called `​photographer`. ``` { "templateKey": "catalogImages", "fields": [ { "type": "string", "key": "photographer" } ] } ``` In this example, assume that there are 10 photographers, each of which have captured the same number of images that have the ​`catalogImages​` template applied. Now consider assume that there are 4,000 files in your Box enterprise which have the `catalogImages` template ​applied and which are split evenly between the two folders,​ `Parts​` and `Products`, ​which are children of the parent folder `​Catalog​` as shown below. ``` Catalog/ | |- Parts/ | |- file_0000.jpeg |- ... |- file_1999.jpeg |- Products/ | |- file_2000.jpeg |- ... |- file_3999.jpeg ``` The following table indicates the outcome of several possible queries. The query is described in plain language for readability. Remember that the ​result set ​is defined as a collection of items (files and folders) which match the metadata query exclusively based on evaluating the `​from​`, `​query​`, and `​query_params` parameters before the requesting user’s permissions and the `​ancestor_folder​` scope are considered. | Query | Result Set | Outcome | Notes | | --- | --- | --- | --- | | Select items with the catalogImages ​template where the ​photographer​ is Mike | 200 items | Success | By only selecting items for Mike the result set is constrained to only 400 files | | Select items with the catalogImages ​template | 4,000 items | May fail | The result set exceeds 2,000 items. If the user does not have access to all of the files in the result set the query may fail | | Select items with the catalogImages ​template in the Products​ folder | 4,000 items | May fail | The result set exceeds 2,000 items and not all results are contained within the ancestor folder. | | Select items with the catalogImages ​template in the Products folder where the photographer​ is Mike | 200 items | Success | By only selecting items for Mike the result set is constrained to only 400 files | **Reference:** https://developer.box.com/guides/metadata/queries/limitations/ --- ## Untitled *Type: guide | Category: Metadata * Common errors Metadata query API errors mostly are similar to errors returned by other APIs, however at this time some incorrect client… # Common errors Metadata query API errors mostly are similar to errors returned by other APIs, however at this time some incorrect client requests may result in a server-side error with a HTTP status code in the `5XX` range rather than an appropriate `400 Bad Request` error. This is a known issue which will be addressed soon. ## Incorrect template key and scope A common error is to use an incorrect value for the the `from` value in the request, which can result in various errors in the `HTTP 4XX` range. Without a correct `from` value the API does not know what template to search for. The value in `from` must be formed as `scope.templateKey`. In this case `scope` is your enterprise's template scope, which looks something like `enterprise_123456`. The numeric value here is your enterprise's ID. Any scope that does not match this format, including the `global` scope and the `enterprise` shorthand scope, will return an error. The `templateKey` is the unique key for the metadata template within your enterprise. The API returns an error when a template with the given key does not exist, or when the key is used within the wrong enterprise were the key does not exist. The [List all metadata templates for enterprise endpoint](e://get-metadata-templates-enterprise) can be used to list all templates available in your enterprise, including their `scope` and `templateKey`. ## Missing value in query_param A common error is to forget to include a query argument in the `query_params` object, which results in a `HTTP 400` error with a code of `unexpected_json_type`. Without all arguments present in the `query_params` the API can not compile your request into a full query. For example, assume your search `query` is as follows `amount >= :value AND status = :status`. All of the arguments that start with a colon `:` will need to be present in the `query_params`. In this case your query parameters would have to look something like this. Missing out on any of these values will result in an error. ``` "query_params": { "value": 100, "status": "active" } ``` The name of each argument can configured to your liking and does not need to match the field key. The only requirement is that it starts with a `:`. ## Missing ancestor_folder_id A common error is to forget the `ancestor_folder_id` in the request, which results in a `HTTP 400` error with a code of `bad_request`. Without the `ancestor_folder_id` value the API does not know what folder to search for results in. When in doubt a value of `0` can be used for the user's root folder. **Reference:** https://developer.box.com/guides/metadata/queries/errors/ --- ## Untitled *Type: guide | Category: Metadata * Metadata queries A metadata query provides a way to find files and folders by searching for the metadata attached to them. For example, to… # Metadata queries A metadata query provides a way to find files and folders by searching for the metadata attached to them. For example, to find the all files for an invoice with a certain ID, the query would look for all files and folders with the `invoiceData` template attached to it and a value of `id = :id`, where `:id` would be the value of the invoice. ## Authentication The metadata query API can be used by applications that have been authenticated using traditional [OAuth 2.0](g://authentication/oauth2) or [JWT](g://authentication/jwt). **Reference:** https://developer.box.com/guides/metadata/queries/ --- ## Untitled *Type: guide | Category: Metadata * Paginate and sort By default only 20 query results are returned per page and the order of these results is not guaranteed and may change… # Paginate and sort By default only 20 query results are returned per page and the order of these results is not guaranteed and may change. Pagination and sorting allow for getting more results and defining the order of the results. ## Pagination By default the API only returns the first page of 20 results. To request more results per page the `limit` query parameter can be send with the request. ``` curl -X POST https://api.box.com/2.0/metadata_queries/execute_read \ -H 'Authorization: Bearer <ACCESS_TOKEN>' \ -H 'Content-Type: application/json' \ -d '{ "from": "enterprise_123456.contractTemplate", "fields": ["name"], "ancestor_folder_id": "5555", "limit": 100 }' ``` The maximum value for `limit` is 100. To return more pages of results each page returns a `next_marker` value. ``` { "entries": [...], "next_marker": "AAAAAmVYB1FWec8GH6yWu2nwmanfMh07IyYInaa7DZDYjgO1H4KoLW29vPlLY173OKsci6h6xGh61gG73gnaxoS+o0BbI1/h6le6cikjlupVhASwJ2Cj0tOD9wlnrUMHHw3/ISf+uuACzrOMhN6d5fYrbidPzS6MdhJOejuYlvsg4tcBYzjauP3+VU51p77HFAIuObnJT0ff" } ``` This `next_marker` can be used to form a new request for the next page of results. ``` curl -X POST https://api.box.com/2.0/metadata_queries/execute_read \ -H 'Authorization: Bearer <ACCESS_TOKEN>' \ -H 'Content-Type: application/json' \ -d '{ "from": "enterprise_123456.contractTemplate", "fields": ["name"], "ancestor_folder_id": "5555", "limit": 100, "marker": "AAAAAmVYB1FWec8GH6yWu2nwmanfMh07IyYInaa7DZDYjgO1H4KoLW29vPlLY173OKsci6h6xGh61gG73gnaxoS+o0BbI1/h6le6cikjlupVhASwJ2Cj0tOD9wlnrUMHHw3/ISf+uuACzrOMhN6d5fYrbidPzS6MdhJOejuYlvsg4tcBYzjauP3+VU51p77HFAIuObnJT0ff" }' ``` When a response does not include a next marker it indicates that no more matching results exist. To use the `next_marker` it is important to use the exact same query for the next page of results. The API will return an error if any of the parameters like the `query`, `query_params`, or others are changed. ## Sort Results can be ordered by any of the supported metadata field types. These only supported types currently are `float`, `date`, and `string` [fields](g://metadata/fields). Ordering by `enum` or `multiSelect` value is currently not supported. To order result, provide one or more field keys to sort by, as well as a direction. ``` "order_by": [ { "field_key": "amount”, "direction": "desc" }, { "field_key": "created_at" "direction": "desc" } ] ``` The ordering direction must be the same for all ​​specified keys. In other words, for each `field_key` the `direction` must be the same. **Reference:** https://developer.box.com/guides/metadata/queries/pagination/ --- ## Untitled *Type: quick-start | Category: Metadata * Apply metadata to a file With your new customerData template in hand you can now apply this template to a file or folder. To apply this… # Apply metadata to a file With your new `customerData` template in hand you can now apply this template to a file or folder. To apply this template you will need the `scope` and `templateKey` of the template, as well as the ID of the item to apply the template to. ``` curl -X POST https://api.box.com/2.0/files/12345/metadata/enterprise/customerInfo \ -H "authorization: Bearer <ACCESS_TOKEN>" \ -H "content-type: application/json" \ -d '{ "name": "Box, Inc", "industry": "Technology", "tav": 1000000 }' ``` ``` var metadataValues = new Dictionary<string, object>() { { "name", "Box, Inc" }, { "industry", "Technology" }, { "tav", 100000 } }; Dictionary<string, object> metadata = await client.MetadataManager .CreateFileMetadataAsync(fileId: "12345", metadataValues, "enterprise", "customerInfo"); ``` ``` BoxFile file = new BoxFile(api, "12345"); file.createMetadata( "customerInfo", "enterprise", new Metadata() .add("name", "Box, Inc") .add("industry", "Technology") .add("tav", 100000) ); ``` ``` metadata = { 'name': 'Box, Inc', 'industry': 'Technology', 'tav': 1000000 } client.file(file_id='11111').metadata(scope='enterprise', template='customerInfo').set(metadata) ``` ``` client.files.addMetadata( '12345', client.metadata.scopes.ENTERPRISE, "customerInfo", { name: "Box, Inc", industry: "Technology", tav: 1000000 } ).then(metadata => { // ... }); ``` The `industry` field in this example is an `enum` field and therefore the value needs to be one of the available options on the field. Any other value will result in an error. The API will return the newly created metadata instance. ``` { "name": "Box, Inc", "industry": "Technology", "tav": 1000000, "$id": "01234500-12f1-1234-aa12-b1d234cb567e", "$parent": "folder_12345,", "$scope": "enterprise_34567", "$template": "customerInfo", "$type": "customerInfo-6bcba49f-ca6d-4d2a-a758-57fe6edf44d0", "$typeVersion": 2, "$version": 1, "$canEdit": true } ``` I've applied metadata to a file **Reference:** https://developer.box.com/guides/metadata/quick-start/create-instance/ --- ## Untitled *Type: quick-start | Category: Metadata * Create a metadata query As a final step, let's look at how you can create a query to find specific files and folders based on the metadata… # Create a metadata query As a final step, let's look at how you can create a query to find specific files and folders based on the metadata attached to those files/folders. A [metadata query](g://metadata/queries) provides a way to find files and folders by searching for the metadata attached to them. The search syntax is similar to SQL and it supports boolean operations and comparative operators to perform powerful searches. In our case, let's create a query to find any files or folders that have an instance of the `customerInfo` metadata template attached to them. We will filter this list down to any files that belong to a customer who's total account value is more than $200,000. ``` curl -X POST https://api.box.com/2.0/metadata_queries/execute_read \ -H 'Authorization: Bearer <ACCESS_TOKEN>" ' \ -H 'Content-Type: application/json' \ -d '{ "from": "enterprise_123456.customerInfo", "fields": [ "name", "enterprise_123456.customerInfo.name" "enterprise_123456.customerInfo.tav" ], "query": "tav >= :value", "query_params": { "value": 200000 }, "ancestor_folder_id": "0" }' ``` This API will return a list of matched files and folders, as well as the metadata that matched the query for that file. ``` { "entries": [ { "type": "file", "id": "11111", "etag": "0", "metadata": { "enterprise_123456": { "customerInfo": { "name": "Box", "tav": 1000000, "$parent": "folder_12345,", "$scope": "enterprise_123456", "$template": "customerInfo", "$version": 1 } } } } ], "limit": 20, "next_marker": "AAAAAmVYB1FWec8GH6yWu2nwmanfMh07IyYInaa7DZDYjgO1H4KoLW29vPlLY173OKsci6h6xGh61gG73gnaxoS+o0BbI1/h6le6cikjlupVhASwJ2Cj0tOD9wlnrUMHHw3/ISf+uuACzrOMhN6d5fYrbidPzS6MdhJOejuYlvsg4tcBYzjauP3+VU51p77HFAIuObnJT0ff" } ``` By default this API returns `20` items per page, but more items can be requested using marker-based pagination. Learn more about metadata queries I've queried a file using metadata **Reference:** https://developer.box.com/guides/metadata/quick-start/create-query/ --- ## Untitled *Type: guide | Category: Metadata * Query syntax The query syntax for the metadata query API is similar to that of a SQL database. To query for all files and folders that match… # Query syntax The query syntax for the metadata query API is similar to that of a SQL database. To query for all files and folders that match a contract metadata template with a contract value of over $100 the following metadata query could be created. ``` { "from": "enterprise_123456.contractTemplate", "query": "amount >= :value", "query_params": { "value": 100 }, "fields": [ "name", "metadata.enterprise_123456.contractTemplate.amount" ], "ancestor_folder_id": "5555" } ``` In this case the `from` value represents the `scope` and `templateKey` of the metadata template, and the `ancestor_folder_id` represents the folder ID to search within, including its subfolders. ## The fields parameter By default, this API will only return the base-representation of a file or folder, which includes their `id`, `type`, and `etag` values. To request any additional data the `fields` parameter can be used to query any additional fields, as well as any metadata associated to the item. For example: - `created_by` will add the details of the user who created the item to the response. - `metadata.<scope>.<templateKey>` will return the base-representation of the metadata instance identified by the `scope` and `templateKey`. - `metadata.<scope>.<templateKey>.<field>` will return all fields in the base-representation of the metadata instance identified by the `scope` and `templateKey` plus the field specified by the `field` name. Multiple fields for the same `scope` and `templateKey` can be defined. ## The query parameter The `query` parameter represents the SQL-like query to perform on the selected metadata instance. This parameter is optional, and without this parameter the API would return all files and folders for this template. Every left hand field name, like `amount`, needs to match the `key` of a field on the associated metadata template. In other words, you can only search for fields that are actually present on the associated metadata instance. Any other field name will result in the error returning an error. ### The query_params parameter To make it less complicated to embed dynamic values into the query string, an argument can be defined using a colon syntax, like `:value`. Each argument that is specified like this needs a subsequent value with that key in the `query_params` object, for example: ``` { ..., "query": "amount >= :amount AND country = :country", "query_params": { "amount": 100, "country": "United States" }, ... } ``` ### Logical operators A query supports the following logical operators. | Operator | | | --- | --- | | AND | Matches when all the conditions separated by AND are TRUE. | | OR | Matches when any of the conditions separated by OR is TRUE. | | NOT | Matches when the preceding condition(s) is not TRUE. | | LIKE | Matches when the template field value matches a pattern. Only supported for string values. See pattern matching for more details. See additional limitations below. | | NOT LIKE | Matches when the template field value does not match a pattern. Only supported for string values. See pattern matching for more details. See additional limitations below. | | ILIKE | Identical to LIKE but case insensitive. See additional limitations below. | | NOT ILIKE | Identical to NOT LIKE but case insensitive. See additional limitations below. | | IN | Matches when a template field value equals any value in a specified list. Use explicitly defined query_params arguments for each list item, such as amount IN (:arg1, :arg2, :arg3). By design, the IN operator isn't supported for multiselect fields in metadata queries and returns a 400 error. | | NOT IN | Similar to IN but when the template field value matches none of the arguments provided in the list. | | IS NULL | Matches when the template field value is null. | | IS NOT NULL | Matches when the template field value is not null . | Any match on a `string` or `enum` field is case sensitive except when using the `ILIKE` operator. ### Comparison operators A query supports the following comparison operators. | Operator | | | --- | --- | | = | Ensures a template field value is equal to the specified value | | > | Ensures a template field value is greater than the specified value | | < | Ensures a template field value is less than the specified value | | >= | Ensures a template field value is greater than or equal to the specified value | | <= | Ensures a template field value is less than or equal to the a specified value | | <> | Ensures a template field value is not equal to the a specified value | Bit-wise and arithmetic operators are not supported by the Metadata Query API. ### Pattern matching The `LIKE`, `NOT LIKE`, `ILIKE`, and `NOT ILIKE` operators match a string to a pattern. The pattern supports the following reserved characters. - `%` The percent sign represents zero, one, or multiple characters, for example `%Contract` matches `Contract`, `Sales Contract`, but not `Contract (Sales)`, - `_` The underscore represents a single character, for example `Bo_` matches `Box`, `Bot`, but not `Bots`, Both of these reserved characters can be used before, after, or in between other characters. A pattern can include multiple reserved characters, for example `Box% (____)` would match `Box Contract (2020)`. An example query would looks something like this. Note that the `%`-wrapped string is not in the `query` attribute but in the list of `query_params`. ``` { ..., "query": "country ILIKE :country", "query_params": { "country": "%United%" }, ... } ``` The backslash character `\` can be used to escape the `%` or `_` characters if those need to be matched literally, for example `20\%` would match the literal value of `20%`. **Reference:** https://developer.box.com/guides/metadata/queries/syntax/ --- ## Untitled *Type: quick-start | Category: Metadata * Create a custom metadata template To create a custom metadata template for your enterprise you can use our API directly or one of our SDKs… # Create a custom metadata template To create a custom metadata template for your enterprise you can use [our API directly](e://post-metadata-templates-schema) or one of our SDKs to create a new template. For this `customerInfo` template, we're going to create a template with 3 fields. The first field is a text field to hold the customer's `name`, the second is a dropdown list of the possible values for the `industry` the customer operates in, and the final field represents the total annual contract value (`tav`). Learn about the different field types To create this template we need to pass in the configuration for the fields, as well as a display name for the field. ``` curl -X POST https://api.box.com/2.0/metadata_templates/schema \ -H "authorization: Bearer <ACCESS_TOKEN>" \ -H "content-type: application/json" \ -d '{ "scope": "enterprise", "displayName": "Customer Info", "fields": [ { "type": "string", "displayName": "Name" }, { "type": "enum", "displayName": "Industry", "options": [ {"key": "Technology"}, {"key": "Healthcare"}, {"key": "Legal"} ] }, { "type": "float", "displayName": "Total account value", "key": "tav" } ] }' ``` ``` var templateParams = new BoxMetadataTemplate() { DisplayName = "Customer Info", Scope = "enterprise", Fields = new List<BoxMetadataTemplateField>() { new BoxMetadataTemplateField() { Type = "string", DisplayName = "Name" }, new BoxMetadataTemplateField() { Type = "enum", DisplayName = "Industry", Options = new List<BoxMetadataTemplateFieldOption>() { new BoxMetadataTemplateFieldOption() { Key = "Technology" }, new BoxMetadataTemplateFieldOption() { Key = "Healthcare" }, new BoxMetadataTemplateFieldOption() { Key = "Legal" } } }, new BoxMetadataTemplateField() { Type = "float", DisplayName = "Total account value", Key="tav" }, } }; BoxMetadataTemplate template = await client.MetadataManager.CreateMetadataTemplate(templateParams); ``` ``` MetadataTemplate.Field name = new MetadataTemplate.Field(); name.setType("string"); name.setDisplayName("Name"); MetadataTemplate.Field industry = new MetadataTemplate.Field(); industry.setType("enum"); industry.setDisplayName("Industry"); MetadataTemplate.Option technology = new MetadataTemplate.Option(); technology.setKey("Technology"); MetadataTemplate.Option healthcare = new MetadataTemplate.Option(); healthcare.setKey("Healthcare"); MetadataTemplate.Option legal = new MetadataTemplate.Option(); legal.setKey("Legal"); List<MetadataTemplate.Option> options = new ArrayList<MetadataTemplate.Option>(); options.add(technology); options.add(healthcare); options.add(legal); MetadataTemplate.Field tav = new MetadataTemplate.Field(); tav.setType("float"); tav.setDisplayName("Total account value"); tav.setKey("tav"); List<MetadataTemplate.Field> fields = new ArrayList<MetadataTemplate.Field>(); fields.add(name); fields.add(industry); fields.add(tav); MetadataTemplate template = MetadataTemplate.createMetadataTemplate(api, "enterprise", "customerInfo", "Customer Info", false, fields); ``` ``` from boxsdk.object.metadata_template import MetadataField, MetadataFieldType fields = [ MetadataField(MetadataFieldType.STRING, 'Name') MetadataField(MetadataFieldType.ENUM, 'Industry', options=['Technology', 'Healthcare', 'Legal']) ] template = client.create_metadata_template('Customer Info', fields) ``` ``` client.metadata.createTemplate( 'Customer Info', [ { type: 'string', displayName: 'Name' }, { type: 'enum', displayName: 'Industry', options: [ {key: 'Technology'}, {key: 'Healthcare'}, {key: 'Legal'} ] }, { type: 'float', displayName: 'Total account value', key: 'tav' } ] ).then(template => { // ... }); ``` # Admin permissions required Creating metadata templates is restricted to users with admin permission. This means that only admins, or co-admins who have been granted rights to Create and edit metadata templates for your company by the admin can use the web app or the API to manage templates. The API will return the newly created metadata template. ``` { "id": "100ac693-a468-4b37-9535-05984b804dc2", "type": "metadata_template", "templateKey": "customerInfo", "scope": "enterprise_34567", "displayName": "Customer Info", "hidden": false, "copyInstanceOnItemCopy": false, "fields": [ { "id": "5c6a5906-003b-4654-9deb-472583fc2930", "type": "string", "key": "name", "displayName": "Name", "hidden": false }, { "id": "cf3eb5b8-52ef-456c-b175-44354a27e289", "type": "enum", "key": "industry", "displayName": "Industry", "options": [ {"key": "Technology"}, {"key": "Healthcare"}, {"key": "Legal"} ], "hidden": false }, { "id": "5c6a5906-4654-9deb-003b-472583fc2930", "type": "float", "key": "tav", "displayName": "Total account value", "hidden": false } ] } ``` # Template key Although you did not explicitly set the template key the key is automatically derived from the `displayName` value. In this case, the `templateKey` would be `customerInfo`. I've created a custom template **Reference:** https://developer.box.com/guides/metadata/quick-start/create-template/ --- ## Untitled *Type: quick-start | Category: Metadata * Working with Metadata The Metadata APIs are a great tool to allow applications to automatically add additional information to files and… # Working with Metadata The Metadata APIs are a great tool to allow applications to automatically add additional information to files and folders. ## Overview This guide will take you through the following steps. 1. [List all templates](g://metadata/quick-start/list-all) available to you. 2. [Create a custom metadata template](g://metadata/quick-start/create-template) to hold data specific to your enterprise. 3. [Apply a custom metadata template](g://metadata/quick-start/create-instance) to a file, allowing you to assign custom data to a file. 4. [Update metadata instance](g://metadata/quick-start/update-instance) on a file, allowing you to change the data assigned to a file. 5. [Update a metadata template](g://metadata/quick-start/update-template) and change the data applied to all instances of this template. 6. [Create a query to find files by metadata](g://metadata/quick-start/create-query) and change the data applied to all instances of this template. I am ready to get started **Reference:** https://developer.box.com/guides/metadata/quick-start/ --- ## Untitled *Type: quick-start | Category: Metadata * List all metadata templates It's likely your enterprise already has a list of metadata templates that you can use right off-the-shelve… # List all metadata templates It's likely your enterprise already has a list of metadata templates that you can use right off-the-shelve without having to create your own. In general, metadata templates are either available to only your enterprise or to every enterprise using Box. The `scope` of a template defines if a template is available to everyone (`global`) or only to your enterprise (`enterprise`). Learn more about metadata scopes ## Listing templates A few [global templates](e://get_metadata_templates_global) are available to all customers. Many of these templates are for Box's internal use, yet your application can use and apply these. More useful are templates created by applications and admins within your enterprise to hold data specific to your enterprise's needs. ## A metadata template A [metadata template](g://metadata/templates) describes a set of key/value pairs that can be assigned to a file or folder. For example, a `customerInfo` template might hold data about a customer, having a field for the customer name as well as the customer's industry. ``` { "id": "100ac693-a468-4b37-9535-05984b804dc2", "type": "metadata_template", "templateKey": "customerInfo", "scope": "enterprise_12345", "displayName": "Customer Info", "hidden": false, "copyInstanceOnItemCopy": false, "fields": [ { "id": "5c6a5906-003b-4654-9deb-472583fc2930", "type": "string", "key": "name", "displayName": "Name", "hidden": false }, { "id": "cf3eb5b8-52ef-456c-b175-44354a27e289", "type": "enum", "key": "industry", "displayName": "Industry", "options": [ {"key": "Technology"}, {"key": "Healthcare"}, {"key": "Legal"} ], "hidden": false } ] } ``` I've listed the templates available to me **Reference:** https://developer.box.com/guides/metadata/quick-start/list-all/ --- ## Untitled *Type: quick-start | Category: Metadata * Next steps You've reached the end of this Quick Start guide. By now you should have taken the following steps. Listed all templates… # Next steps You've reached the end of this Quick Start guide. By now you should have taken the following steps. 1. [Listed all templates](g://metadata/quick-start/list-all) available to you. 2. [Created a custom metadata template](g://metadata/quick-start/create-template) to hold data specific to your enterprise. 3. [Applied a custom metadata template](g://metadata/quick-start/create-instance) to a file, allowing you to assign custom data to a file. 4. [Updated a metadata instance](g://metadata/quick-start/update-instance) on a file, allowing you to change the data assigned to a file. 5. [Updated a metadata template](g://metadata/quick-start/update-template) and changed the data applied to all instances of this template. 6. [Queried for files and folders](g://metadata/quick-start/create-query) by matching the values in the metadata instances attached to them. ## Next Steps We recommend the following resources to learn more about using metadata with the Box API. - [Learn more about metadata scopes](g://metadata/scopes) - [Learn more about the different field types](g://metadata/fields) that can be used on a template. **Reference:** https://developer.box.com/guides/metadata/quick-start/next-steps/ --- ## Untitled *Type: quick-start | Category: Metadata * Update a metadata template Let's look at how we can update an existing metadata template. Updates to metadata templates are performed… # Update a metadata template Let's look at how we can update an existing metadata template. Updates to metadata templates are performed through **operations** rather than directly changing the template itself. This method allows you to update any existing metadata instances that are already applied to files and folders. Learn more about updating templates In this case, let's assume that we realized that having a `Name` field is a bit ambiguous, and therefore, we want to change the `Name` field of the `customerInfo` template to `Company Name` instead. By using the `editField` operation we can change the `displayName` and the `key` of the field on the template and on every instance of the template that might be applied to a file or folder. ``` curl -X PUT https://api.box.com/2.0/metadata_templates/enterprise/blueprintTemplate/schema \ -H "authorization: Bearer <ACCESS_TOKEN>" \ -H "content-type: application/json" \ -d '[ { "op": "editField", "fieldKey": "name", "data": { "key": "company_name", "displayName": "Company Name" } } ]' ``` ``` var updates = new List<BoxMetadataTemplateUpdate>() { new BoxMetadataTemplateUpdate() { Op = MetadataTemplateUpdateOp.editField, FieldKey = "name", Data = new { key = "company_name", displayName = "Company Name" } } }; BoxMetadataTemplate updatedTemplate = await client.MetadataManager .UpdateMetadataTemplate(updates, "enterprise", "customerData"); ``` ``` List<MetadataTemplate.FieldOperation> updates = new ArrayList<MetadataTemplate.FieldOperation>(); String editField = "{\"op\":\"editField\",\"fieldKey\":\"name\",\"data\":{\"key\":\"company_name\",\"displayName\":\"Company Name\"}}"; updates.add(new MetadataTemplate.FieldOperation(editField)); MetadataTemplate.updateMetadataTemplate(api, "enterprise", "customerData", updates); ``` ``` template = client.metadata_template('enterprise', 'customerData') updates = template.start_update() updates.edit_field('name', key='company_name', display_name="Company Name") template.update_info(updates) ``` ``` var operations = [ { op: 'editField', fieldKey: 'name', data: { key: 'company_name', displayName: "Company Name" } } ]; client.metadata.updateTemplate( 'enterprise', 'customerData', operations ).then(template => { //.. }); ``` The API will return the updated metadata template. ``` { "id": "100ac693-a468-4b37-9535-05984b804dc2", "type": "metadata_template", "templateKey": "customerInfo", "scope": "enterprise_34567", "displayName": "Customer Info", "hidden": false, "copyInstanceOnItemCopy": false, "fields": [ { "id": "5c6a5906-003b-4654-9deb-472583fc2930", "type": "string", "key": "company_name", "displayName": "Company Name", "hidden": false }, { "id": "cf3eb5b8-52ef-456c-b175-44354a27e289", "type": "enum", "key": "industry", "displayName": "Industry", "options": [ {"key": "Technology"}, {"key": "Healthcare"}, {"key": "Legal"} ], "hidden": false }, { "id": "5c6a5906-4654-9deb-003b-472583fc2930", "type": "float", "key": "tav", "displayName": "Total account value", "hidden": false } ] } ``` Updating the template through operations has the benefit that any instance of the template is automatically updated as well. In this case, the instance we created in previous steps would now look something like this. ``` { "company_name": "Box", "industry": "Technology", "tav": 1000000, "$id": "01234500-12f1-1234-aa12-b1d234cb567e", "$parent": "folder_12345,", "$scope": "enterprise_34567", "$template": "customerInfo", "$type": "customerInfo-6bcba49f-ca6d-4d2a-a758-57fe6edf44d0", "$typeVersion": 2, "$version": 1, "$canEdit": true } ``` I've updated metadata template **Reference:** https://developer.box.com/guides/metadata/quick-start/update-template/ --- ## Untitled *Type: guide | Category: Metadata * Create a metadata template To create a metadata template, pass a scope, displayName and an optional set of fields to the POST /metadata… # Create a metadata template To create a metadata template, pass a `scope`, `displayName` and an optional set of `fields` to the [`POST /metadata_templates/schema`](e://post_metadata_templates_schema) API. Metadata templates can only be created for the `enterprise` scope. Templates can not be created for the `global` scope. # Admin permissions required Creating metadata templates is restricted to users with admin permission. This means that only admins, or co-admins who have been granted rights to Create and edit metadata templates for your company by the admin can use the web app or the API to manage templates. ## Template Fields The `fields` attribute represents the set of individual fields that a user can fill in on a template. For example, a `customer` template might have a `name` field of type `string`. Template fields can be of `string`, `enum`, `float`, `date`, `enum` or `multiSelect` type. Each field requires at least a `type`, `displayName` and `key`. ``` { "scope": "enterprise", "displayName": "Customer", "fields": [ { "type": "string", "key": "name", "displayName": "Name" } ] } ``` The `enum` and `multiSelect` field types represent a dropdown list where a user can select respectively one or many options from a list of items. ``` { "scope": "enterprise", "displayName": "Customer", "fields": [ { "type": "enum", "key": "industry", "displayName": "Industry", "options": [ {"key": "Technology"}, {"key": "Healthcare"}, {"key": "Legal"} ] } ] } ``` Learn more about Metadata Template Fields ## Template Keys When a metadata template is created, a `templateKey` is automatically generated from the `displayName` of the template unless a `templateKey` is explicitly provided. When creating the template key, any spaces and irregular characters in the name are removed, and the string is transformed to camel case. For example, a metadata template that is named `Test Name (with-special_) Characters` will have a `templateKey` of `testNameWithspecialCharacters`. This template key is then used when making any API requests to get the template's information or assign it to an item. **Reference:** https://developer.box.com/guides/metadata/templates/create/ --- ## Untitled *Type: quick-start | Category: Metadata * Update metadata on a file Once metadata has been applied to a file or folder there is often a need to update the metadata at a later date… # Update metadata on a file Once metadata has been applied to a file or folder there is often a need to update the metadata at a later date. Updating a metadata instance is done by applying a set of operations to the original data. These operations are performed atomically, ensuring that the changes are either all applied or not applied at all. Learn more about updating instances In this case, let's assume we want to change the `name` of the customer from `Box, Inc` to `Box`. We can apply two operations, firstly, we ensure the value of the name is still `Box, Inc` before we change it, and secondly we make the change. ``` curl -X PUT https://api.box.com/2.0/files/12345/metadata/enterprise/customerInfo \ -H "authorization: Bearer <ACCESS_TOKEN>" \ -H "content-type: application/json-patch+json" \ -d '[ { "op": "test", "path": "/name", "value": "Box, Inc" }, { "op": "replace", "path": "/name", "value": "Box" } ]' ``` ``` var updates = new List<BoxMetadataUpdate>() { new BoxMetadataUpdate() { Op = MetadataUpdateOp.test, Path = "/name", Value = "Box, Inc" }, new BoxMetadataUpdate() { Op = MetadataUpdateOp.replace, Path = "/name", Value = "Box" } }; Dictionary<string, object> updatedMetadata = await client.MetadataManager .UpdateFileMetadataAsync("12345", updates, "enterprise", "customerInfo"); ``` ``` BoxFile file = new BoxFile(api, "12345"); file.updateMetadata( file.createMetadata( "customerInfo", "enterprise", new Metadata().test("/name", "Box, Inc").replace("/name", "Box") ); ``` ``` file = client.file(file_id='12345') metadata = file.metadata(scope='enterprise', template='customerInfo') updates = metadata.start_update() updates.test('/name', 'Box, Inc') updates.replace('/name', 'Box') file.update(updates) ``` ``` var updates = [ { op: 'test', path: '/name', value: 'Box, Inc' }, { op: 'replace', path: '/name', value: 'Box' } ]; client.files.updateMetadata( '12345', client.metadata.scopes.ENTERPRISE, "customerInfo", updates ).then(metadata => { //... }); ``` Learn more about all operations The API will return the updated metadata instance. ``` { "name": "Box", "industry": "Technology", "tav": 1000000, "$id": "01234500-12f1-1234-aa12-b1d234cb567e", "$parent": "folder_12345,", "$scope": "enterprise_34567", "$template": "customerInfo", "$type": "customerInfo-6bcba49f-ca6d-4d2a-a758-57fe6edf44d0", "$typeVersion": 2, "$version": 1, "$canEdit": true } ``` I've updated metadata on a file **Reference:** https://developer.box.com/guides/metadata/quick-start/update-instance/ --- ## Untitled *Type: guide | Category: Metadata * List all metadata templates Metadata templates can be listed for either the enterprise or the global scope. List global templates To list… # List all metadata templates Metadata templates can be listed for either the enterprise or the global scope. ## List global templates To list all global metadata templates, call the [`GET /metadata_templates/global`](e://get_metadata_templates_global) API endpoint. This API returns a list of all the metadata templates created by Box and made available to all enterprises. ## List current enterprise's templates To list all metadata templates created for usage within the current enterprise, call the [`GET /metadata_templates/enterprise`](e://get_metadata_templates_enterprise) API endpoint. This API returns a list of all the metadata templates created by this enterprise, and these templates are only available to apply to files in this enterprise. ## Pagination This API uses [marker-based pagination](g://api-calls/pagination/marker-based) and can return a `next_marker` value in the response body to indicate that more templates might be available. **Reference:** https://developer.box.com/guides/metadata/templates/list/ --- ## Untitled *Type: guide | Category: Metadata * Update a metadata template Updating a metadata template can be achieved by passing an array of operations to the PUT /metadata_templates… # Update a metadata template Updating a metadata template can be achieved by passing an array of operations to the [`PUT /metadata_templates/:scope/:templateKey/schema`](e://put_metadata_templates_id_id_schema) API. # Admin permissions required Updating metadata templates is restricted to users with admin permission. This means that only admins, or co-admins who have been granted rights to Create and edit metadata templates for your company by the admin can use the web app or the API to manage templates. ## Operations Updates to metadata templates are performed through **operations** rather than directly changing the template itself. This method allows us to update any existing metadata instances that are already applied to files and folders. ## Template operations Template operations update a template's details or fields. These operations are generally safe as they are applied to any template instance without much impact. ### Edit a template The operation `editTemplate` allows for editing any of the base properties of the template, like the `displayName`, `copyInstanceOnItemCopy` and more. | Parameter | | | --- | --- | | data | An object representing the properties to change | ``` [ { "op": "editTemplate", "data": { "displayName": "Client", "copyInstanceOnItemCopy": true } } ] ``` This will update the template to have a new display name of Client. This will affect existing instances of this template. ### Add a field to a template The operation `addField` adds a field to a template. | Parameter | | | --- | --- | | data | An object representing field to add | ``` [ { "op": "addField", "data": { "displayName": "Category", "key": "category", "hidden": false, "type": "string" } } ] ``` This will add a new non-hidden string field with a `displayName` and `key` of **category**. This will affect existing instances of this template. ### Reorder fields The operation `reorderFields` reorders the list of fields in a template to match the requested field list. | Parameter | | | --- | --- | | fieldKeys | The new list of field keys in the requested order | ``` { "op": "reorderFields", "fieldKeys": ["field2", "field1", "field3"] } ``` This will reorder the fields for the template to have `field2` first, followed by `field1`, then `field3`. This will affect existing instances of this template. It will reorder the fields, yet keep the values of the fields intact. ## Field operations Field operations transform the schema of a template. The following is a list of operations that can be used in this API and can potentially change the data of any previously assigned templates. These changes will be logged as template changes but not as file changes. ### Edit a field The operation `editField` edits any number of the base properties of a field like the `displayName`, `description`, `key`, and `hidden` state. | Parameter | | | --- | --- | | data | An object representing the new properties to set for the field | | fieldKey | The key of the field to be edited | ``` { "op": "editField", "fieldKey": "category", "data": { "displayName": "Customer Group" } } ``` This will update the field `category` to have a new display name of **Customer Group**. If the key is changed, existing values of the specified field are migrated to the new key. The search index will be updated, yet it may take time depending on how many files are affected by the change. This may affect existing instances of this template. ### Remove a field The operation `removeField` removes a field from a template. | Parameter | | | --- | --- | | fieldKey | The key of the field to remove from the template | ``` { "op": "removeField", "fieldKey": "brand" } ``` This will remove the field `brand` from the template as well as all instances of the template. The search index will be updated, yet it may take time depending on how many files are affected by the change. This will affect existing instances of this template. ## Field Option Operations Both the [`enum`](g://metadata/fields/enum) and [`multiSelect`](g://metadata/fields/multi-select) metadata field types support some additional operations to change the options of the fields. | Operation | | | --- | --- | | addEnumOption | Adds an option to an enum field | | editEnumOption | Edits an enum field option | | reorderEnumOptions | Re-orders the options on an enum field | | removeEnumOption | Removes an enum field option | | addMultiSelectOption | Adds an option to a multiSelect field | | editMultiSelectOption | Edits a multiSelect field option | | reorderMultiSelectOptions | Re-orders the options on a multiSelect field | | removeMultiSelectOption | Removes a multiSelect field option | **Reference:** https://developer.box.com/guides/metadata/templates/update/ --- ## Untitled *Type: guide | Category: Mobile * Android The Box Android SDK provides native access to the Box API from within your Android project. As of May 31, 2023 Android SDK is no… # Android The [Box Android SDK](https://github.com/box/box-android-sdk) provides native access to the Box API from within your Android project. As of May 31, 2023 Android SDK is no longer supported. You can still use your existing Android SDK applications with no impact, but you won't receive new features, updates, or bug fixes. If you would like to continue getting the latest and greatest Android features, we recommend using Java SDK to build apps on Android. Refer to [this](https://github.com/box/box-java-sdk/blob/main/doc/android.md) documentation for more details. **Reference:** https://developer.box.com/guides/mobile/android/ --- ## Untitled *Type: guide | Category: Mobile * Android SDK Installation As of May 31, 2023 Android SDK is no longer supported. You can still use your existing Android SDK applications… # Android SDK Installation As of May 31, 2023 Android SDK is no longer supported. You can still use your existing Android SDK applications with no impact, but you won't receive new features, updates, or bug fixes. If you would like to continue getting the latest and greatest Android features, we recommend using Java SDK to build apps on Android. Refer to [this](https://github.com/box/box-java-sdk/blob/main/doc/android.md) documentation for more details. The Android SDK may be obtained through several methods: - Adding as a Maven or Gradle dependency - Cloning the source into your project - Downloading one of the precompiled JARs ## Add as a Maven dependency Add the following to your Maven dependencies. ``` <dependency> <groupId>com.box</groupId> <artifactId>box-android-sdk</artifactId> </dependency> ``` ## Add as a Gradle dependency Add the following to your `build.gradle` file. ``` dependencies { implementation 'com.box:box-android-sdk:4.2.3' } ``` ## Clone the source The Box Android SDK source code may be obtained by cloning or downloading the [project from Github](https://github.com/box/box-android-sdk/tree/master/box-content-sdk). ## Download precompiled JARs Precompiled JARs for the Android SDK may be obtained from the Github project [releases page](https://github.com/box/box-android-sdk/releases) If one of the precompiled JARs is being used, the Android SDK will also require the following dependency: `minimal-json v0.9.1` (for Maven: `com.eclipsesource.minimal-json:minimal-json:0.9.1`) **Reference:** https://developer.box.com/guides/mobile/android/install/ --- ## Untitled *Type: guide | Category: Mobile * iOS The Box iOS SDK provides native access to the Box API from within your iOS project. # iOS The [Box iOS SDK](https://github.com/box/box-ios-sdk) provides native access to the Box API from within your iOS project. **Reference:** https://developer.box.com/guides/mobile/ios/ --- ## Untitled *Type: quick-start | Category: Search * Add Metadata Template to a File Your metadata template needs to be applied to at least one file to ensure a result in step 4. There are two… # Add Metadata Template to a File Your metadata template needs to be applied to at least one file to ensure a result in step 4. There are two ways to do add a metadata template to a file: via the UI or via the API. ## UI To apply metadata to a file using the UI, navigate to a file and open the preview. Use the **Metadata** tab and click **Add**. Find the metadata template created in step 1 and select a value. Ensure you click **Save**. ## API To add a metadata template to a file you will need to use the [create metadata instance on a file endpoint](e://post-files-id-metadata-id-id/). You will also need the `scope` and `templateKey` template values from the previous step. Below is an example of what the API call looks like to apply the same metadata shown in the UI above. Due to scale considerations a 403 error will returns when the metadata template is applied to more than 10,000 files or folders. I applied my template to at least one file **Reference:** https://developer.box.com/guides/search/quick-start/apply-template-to-file/ --- ## Untitled *Type: quick-start | Category: Search * Create a Metadata Template There are two ways to create a new metadata template: via the Admin Console or via the API using an Admin Access… # Create a Metadata Template There are two ways to create a new metadata template: via the Admin Console or via the API using an Admin Access Token. If you already have a metadata template created that you would like to use you can skip to the next step. ## Admin Console To create a template via the Admin Console, navigate to: **Admin Console** > **Content** Tab > **Metadata** > **Create New** The metadata feature is reserved for Business Plus accounts and above. To upgrade your account, please contact your Box account team. Once you select **Create New**, you are brought to a form used to create a new template, which is shown below. Select a **Dropdown-Single Select** format. Selecting a Dropdown-Multi Select format will change the structure of you query later on in this quick-start. ## API Creating a metadata template via the API requires an [Access Token](g://authentication/tokens) associated with a Box Admin or Co-Admin with permission to **create and edit metadata templates for your company**. If you are not sure who your token is associated with, make an API call to the [get current user endpoint](e://get-users-me). The easiest way to obtain a token meeting these requirements is to log in as an Admin or Co-Admin, pull up the application in the [Developer Console](https://account.box.com/developers/console), and click **Generate Developer Token** under the **Configuration** tab. [Developer tokens](g://authentication/tokens/developer-tokens) are always be associated with the user logged into the Developer Console when the button is clicked. Using [Postman](https://postman.com/) and the [Box Postman Collection](g://tooling/postman), below is an example of what an API call looks like to create the same metadata template created above using the Admin Console. If you would like to use a different template format than a Dropdown-Single Select, you will need to consult our reference documentation, as the body of the API call will differ from the example above. The response to this API call provides crucial information you will need later. If you created your template via the UI, you will learn how to obtain this information in the next step. I created a metadata template **Reference:** https://developer.box.com/guides/search/quick-start/create-metadata-template/ --- ## Untitled *Type: quick-start | Category: Search * Search content using metadata The Box Metadata Query API enables programmatic search of Box content based strictly on applied custom… # Search content using metadata The Box Metadata Query API enables programmatic search of Box content based strictly on applied custom metadata values. The structure of a metadata query is similar to that of a SQL query and allows for Boolean operators, such as AND, OR, and NOT, as well as comparison or range operators, such as equal to, greater-than, and less-than. Some benefits of the Metadata Query API include: - no indexing delay; query immediately after metadata creations, updates, and deletions - ability to specify a sort order by one or more fields - no limitation on the number of characters that are queryable - queries return item properties and metadata instances ## Overview This guide will take you through the following steps. 1. [Create a metadata template](g://search/quick-start/create-metadata-template) 2. [Locate information about the metadata template](g://search/quick-start/locate-template-info) via API 3. [Apply the metadata template](g://search/quick-start/apply-template-to-file) to at least one file 4. [Construct a metadata query API call](g://search/quick-start/metadata-query-api) to obtain the content from step 3 I am ready to get started **Reference:** https://developer.box.com/guides/search/quick-start/ --- ## Untitled *Type: quick-start | Category: Search * Locate Information about a Metadata Template If you created your metadata template using the API and have the successful response on hand… # Locate Information about a Metadata Template If you created your metadata template using the API and have the successful response on hand, you can skip this step. If you created your metadata template via the Admin Console, you will need obtain an [Access Token](g://authentication/tokens) for an Admin or Co-Admin with permission to **Create and edit metadata templates for your company**. As mentioned in step 1, the easiest way to obtain a token meeting these requirements is to log in as an Admin or Co-Admin, pull up the application in the [Developer Console](https://account.box.com/developers/console), and click **Generate Developer Token** under the **Configuration** tab. Use this token to make an API call to the [list all metadata templates for enterprise endpoint](e://get-metadata-templates-enterprise/), as shown below. You should be able to find information about your template in the response. Specifically, note the `scope` and `templateKey`. I obtained the details about my template **Reference:** https://developer.box.com/guides/search/quick-start/locate-template-info/ --- ## Untitled *Type: quick-start | Category: Search * Use the Metadata Query API Finally, build a metadata query API call. Here is an example of what the body of the API call looks like: This… # Use the Metadata Query API Finally, build a [metadata query API call](e://post-metadata-queries-execute-read/). Here is an example of what the body of the API call looks like: ``` { "from": "enterprise_22201764.contact_role", "query": "departments = :departments", "query_params": {"departments": "legal"}, "ancestor_folder_id": "0", "fields": ["id"] } ``` This API call will return any file ID that meets the following criteria: - Has the Contact Role template applied - Has a value of departments is legal - Is located anywhere under the root folder, since "0" is the `ancestor_folder_id` As you can see below, this results in a successful call showing information about the file we added the template to in step 2. I found my content using a search query **Reference:** https://developer.box.com/guides/search/quick-start/metadata-query-api/ --- ## Untitled *Type: quick-start | Category: Search * Next Steps You've reached the end of this Quick Start guide. By now you should have taken the following steps. Created a metadata template… # Next Steps You've reached the end of this Quick Start guide. By now you should have taken the following steps. 1. [Created a metadata template](g://search/quick-start/create-metadata-template) 2. [Located information about the metadata template](g://search/quick-start/locate-template-info) from step 1 via API 3. [Added a metadata template](g://search/quick-start/apply-template-to-file) to at least one file 4. [Used the metadata query API](g://search/quick-start/metadata-query-api) to locate the file(s) from step 3 ## Next Steps We recommend the following resources to learn more: - [Metadata query guide](g://metadata/queries) - Review [common errors](g://metadata/queries/errors) and [limitations](g://metadata/queries/limitations) - Reference documentation for the [metadata query](e://post-metadata-queries-execute-read) API Our SDKs that support the metadata query API: - [Python SDK](https://github.com/box/box-python-sdk/blob/main/docs/usage/search.md#metadata-query) - [.NET SDK](https://github.com/box/box-node-sdk/blob/3fcc0d8bbd1ca11f1a3a78d741e4572718af53f0/docs/metadata.md#query) - [iOS SDK](https://github.com/box/box-ios-sdk/blob/c5ff8396e28c31fcf3c433f1b9e8f2f0d7a0e0db/docs/usage/search.md#metadata-search) - [Java SDK](https://github.com/box/box-java-sdk/blob/5e3a96c903fffa198c97e981ce75765a69bd6cb6/doc/metadata_template.md#execute-metadata-query) - [Node](https://github.com/box/box-node-sdk/blob/3fcc0d8bbd1ca11f1a3a78d741e4572718af53f0/docs/metadata.md#query) **Reference:** https://developer.box.com/guides/search/quick-start/next-steps/ --- ## Untitled *Type: guide | Category: Security * Find Terms for Collaboration Information about the Terms of Service that is in effect for any Collaboration can be inspected by calling the… # Find Terms for Collaboration Information about the Terms of Service that is in effect for any [Collaboration](r://collaboration) can be inspected by calling the [`GET /collaborations/:id`](e://get-collaborations-id) API and passing the query parameter `fields=acceptance_requirements_status`. ``` curl -X GET https://api.box.com/2.0/collaborations/2342342?fields=acceptance_requirements_status \ -H "authorization: Bearer <ACCESS_TOKEN>" ``` The resulting response will include a new `acceptance_requirements` object that includes a mini `terms_of_service` object. ``` { "type": "collaboration", "id": 2342342>, "acceptance_requirements": { "terms_of_service": { "type": "terms_of_service", "id": 6766677 } } } ``` This information is only returned if the Terms of Service for external users is enabled for the enterprise, and the user making the request has the [permission](g://security/terms-of-service/permissions) to see the Terms of Service. This holds true for both admin and end users, even though admins can generally view Terms of User information via the API even if the specific Terms of Service type is turned off. If the Terms of Service type is not enabled, the API will return an empty result. ``` { "type": "collaboration", "id": 2342342>, "acceptance_requirements": { "terms_of_service": null } } ``` The `terms_of_service` information is returned within the `acceptance_requirements` even if they have already been accepted by the user. **Reference:** https://developer.box.com/guides/security/terms-of-service/for-colaboration/ --- ## Untitled *Type: guide | Category: Security * Application Flow In general, applications use Terms of Services as follows. When an application, authenticated as a user, tries to access an… # Application Flow In general, applications use Terms of Services as follows. When an application, authenticated as a user, tries to access an item in Box that requires the user to have accepted the relevant Terms of Service it receives a `TERMS_OF_SERVICE_REQUIRED` error. ``` { "type": "error", "status": 400, "code": "terms_of_service_required", "context_info": { "tos_id": 261346614, "tos_user_status_id": 4562456 }, "help_url": "https://developer.box.com/guides/api-calls/permissions-and-errors/common-errors/", "message": "User must accept custom terms of service before action can be taken", "request_id": "ADF7722DD" } ``` The application requests the Terms of Service's information by calling [`GET /terms_of_services/:id`](e://get_terms_of_services_id). ``` { "id": 261346614, "type": "terms_of_service", "status": "enabled", "enterprise": { "id": 11446498, "type": "enterprise", "name": "Acme Inc." }, "tos_type": "managed", "text": "By using this service, you agree to ...", "created_at": "2012-12-12T10:53:43-08:00", "modified_at": "2012-12-12T10:53:43-08:00" } ``` The application can then show the text from the Terms of Service to the user. When the user accepts or rejects the terms, it makes a call to either [`PUT /terms_of_service_user_statuses/:id`](e://put_terms_of_service_user_statuses_id) or [`POST /terms_of_service_user_statuses`](e://post_terms_of_service_user_statuses) depending on if the initial error returned a `tos_user_status_id` in the response. **Reference:** https://developer.box.com/guides/security/terms-of-service/flow/ --- ## Untitled *Type: guide | Category: Security * Terms of Service The Box API allows administrators to configure Terms of Services for working on Box, and for users to accept & re-accept… # Terms of Service The Box API allows administrators to configure Terms of Services for working on Box, and for users to accept & re-accept Terms of Services for custom applications. ## Terminology ### Terms of Services A Terms of Service is a enterprise-level record that represent the conditions within which all users are allowed to work with an enterprise's data in Box. There are currently two types of Terms of Service for any enterprise that can be enabled independently. The **Managed Terms of Service** can be enabled for the enterprise's own users, where the **External Terms of Service* can be enabled for users from other enterprises that collaborated in on the primary enterprise's data. ## Terms of Service User Statuses A Terms of Service User Status represents the status of the Terms of Service acceptance for a specific user. There is exactly one Terms of Service User Status for any given combination of Terms of Service and a user. There are multiple Terms of Service User Statuses for any Terms of Service, specifically one for each each user. There could be multiple Terms of Service User Statuses for a given user. The user could accept or reject multiple External Terms of Services for different enterprises they have been collaborated into, in addition to accepting or rejecting their own enterprise’s Managed Terms of Service. ## APIs Applications that are authenticated as a Box Admin that has the Edit settings for your company permissions can view, create, and edit Terms of Services for their enterprise via the API. - [`GET /terms_of_services/:id`](e://get-terms-of-services-id): To get the information for a specific Terms of Service - [`GET /terms_of_services`](e://get-terms-of-services): To get a list of all the Terms of Services used within an enterprise, either for managed or external users. - [`POST /terms_of_services`](e://post-terms-of-services): To create Terms of Service settings for either an external or managed user. [`PUT /terms_of_services/:id`](e://put-terms-of-services-id): To update a specific Terms of Service setting Additionally, application can view and accept Terms of Services for a regular user via the API. - [`GET /terms_of_service_user_statuses`](e://get-terms-of-service-user-statuses): To get a list of all the Terms of Services for a user - [`POST /terms_of_service_user_statuses`](e://post-terms-of-service-user-statuses): To accept or reject a specific Terms of Service for the first time - [`PUT /terms_of_service_user_statuses/:id`](e://put-terms-of-service-user-statuses-id): To accept or reject a specific Terms of Service that had been previously accepted or rejected. ## Scopes The following scopes should be granted to the application in order to take the outlined actions. - **Manage Enterprise Properties**: Required to enable or edit the enterprise's settings for Terms of Services as well as to view them for external users. - **Manage Users**: Required to accept Terms of Services for other users. **Reference:** https://developer.box.com/guides/security/terms-of-service/ --- ## Untitled *Type: guide | Category: Box Skills * Handle Skills Payloads Within the application or site that is set up as your invocation URL you will generally need to perform two tasks… # Handle Skills Payloads Within the application or site that is set up as your [invocation URL](guide://skills/invocation-url) you will generally need to perform two tasks. - [Handle the Skill payload](guide://skills/handle/payload) - Every time a Box Skills detects a new file is uploaded, copied, or moved to a folder it sends a JSON notification to the invocation URL. This URL needs to be parsed. - [Apply a Skill Card to a file](guide://skills/handle/metadata) - The metadata returned from the processing service will need to be stored back as metadata on the file that triggered the event. **Reference:** https://developer.box.com/guides/skills/handle/ --- ## Untitled *Type: guide | Category: Security * Permissions The following is a list of permissions users, admins, and co-admins need to have when working with Terms of Services and Terms… # Permissions The following is a list of permissions users, admins, and co-admins need to have when working with Terms of Services and Terms of Service Statuses. ## Terms of Service An end user is considered subject to Terms of Service when: - User belongs to or is collaborated into an enterprise that has a Terms of Service enabled The type of Terms of Service reflects the user's relationship to the Enterprise - A managed Terms of Service for a user that is part of the same enterprise - An external Terms of Service for users collaborating into the enterprise Terms of Service settings can be viewed by an end user if: - The user is subject to a Terms of Service; and - The Terms of Service Type is enabled on the enterprise A Terms of Service's settings can be viewed by an enterprise admin or co-admin if: - They have **View settings for your company** permissions - The application has the **Manage enterprise properties** scope enabled - The Terms of Service belongs to their enterprise Terms of Service settings can be edited by an enterprise admin or co-admin if: - They have **'Edit settings for your company** permissions - The application has the **Manage enterprise properties** scope enabled - The Terms of Service belongs to their enterprise Enterprise admins and co-admins can view, create, and edit Terms of Service settings for both external and managed Terms of Service without having accepted managed the managed Terms of Service for their own enterprise. ## Terms of Service User Status Terms of Service User Status can be viewed and edited by an end user if: - The User Status belongs to the end user - The Terms of Service type is enabled on the enterprise - The end user is subject to the Terms of Service Terms of Service User Statuses belonging to other users can be viewed by enterprise admins and co-admins if: - They have **Manager users** permissions - The application has the **Manage users** scope enabled - The Terms of Service belongs to their enterprise - They have accepted the managed Terms of Service for their own enterprise, if applicable Terms of Service User Status belonging to other users can be edited by enterprise admins and co-admins if: - They have **Manager users** permissions - The application has the **Manage users** scope enabled - The end user is subject to the Terms of Service - The end user is not an admin or co-admin - The Terms of Service belongs to their enterprise - They have accepted the managed Terms of Service for their own enterprise, if applicable An end user cannot accept, reject, view external Terms of Service settings for an enterprise they are collaborating into until the end user accepts the managed Terms of service for their own enterprise, where applicable. Trying to do so will result in a `TERMS_OF_SERVICE_REQUIRED` error. **Reference:** https://developer.box.com/guides/security/terms-of-service/permissions/ --- ## Untitled *Type: guide | Category: Box Skills * Box Skills Payload When a new file is uploaded, copied, or moved to a folder monitored by a Skills app, the invocation URL that was… # Box Skills Payload When a new file is uploaded, copied, or moved to a folder monitored by a Skills app, the invocation URL that was specified during application setup and authentication will receive an event payload from Box. The event payload will contain all information needed to read in the content of the uploaded file to send to a processing system, such as a machine learning system, and to write metadata back to the file once the processing system has completed. Example payload and reference ## Access Tokens Every Skills payload includes a set of Access Tokens that can be used to access the file that triggered the event. ``` { ... "token": { "write": { "access_token": "c3FIOG9vSGV4VHo4QzAyg5T1JvNnJoZ3ExaVNyQWw6WjRsanRKZG5lQk9qUE1BVQ", "expires_in": 1540924150, "restricted_to": ..., "token_type": "bearer" }, "read": { "access_token": "Z3ExaVNyQWw6WjRsanRKZG5lQk9qUE1BVQc3FIOG9vSGV4VHo4QzAyg5T1JvNnJo", "expires_in": 1540924150, "restricted_to": ..., "token_type": "bearer" } }, ... } ``` The `token.write.access_token` can be used to write metadata to the file, while the `token.read.access_token` can be only used to read the file content. The read-only token is useful when creating a download URL for the file which can then be shared with other services. ## Downloadable file URL Many machine learning services support directly passing a file URL to that service for processing. To create a download URL for a Box file, you will need to parse the `token.read.access_token` as well as the `source.id` from the event payload. ``` { ... "source": { "type": "file", "id": 12345, }, "token": { ... "read": { "access_token": "Z3ExaVNyQWw6WjRsanRKZG5lQk9qUE1BVQc3FIOG9vSGV4VHo4QzAyg5T1JvNnJo", "expires_in": 1540924150, "restricted_to": ..., "token_type": "bearer" } }, ... } ``` The download URL for a file can be constructed as follows. ``` https://api.box.com/2.0/files/{source.id}/content?access_token={token.read.access_token} ``` In our example, this URL would be as follows. ``` https://api.box.com/2.0/files/12345/content?access_token=Z3ExaVNyQWw6WjRsanRKZG5lQk9qUE1BVQc3FIOG9vSGV4VHo4QzAyg5T1JvNnJo ``` **Reference:** https://developer.box.com/guides/skills/handle/payload/ --- ## Untitled *Type: guide | Category: Box Skills * Skills Cards Metadata Once a processing service has determined the metadata for the file, your application can write that data back to the… # Skills Cards Metadata Once a processing service has determined the metadata for the file, your application can write that data back to the file stored on Box as metadata. This process involves the following steps. 1. Prepare the Skill Cards metadata 2. Write the metadata to the file ## Prepare Skill Cards metadata The Skills metadata uses a globally available metadata template called `boxSkillsCards`. This template follows a specific format for the JSON structure that will be stored on the associated files. Box currently supports 4 kinds of cards. | | | | | --- | --- | --- | | Keyword | Displays a list of keywords next to the file. | | | Timeline | Displays a set of text/images, and when clicked shows when those images appear in a timeline. | | | Transcript | Displays a transcript with the corresponding timestamps. | | | Status | Displays a status to the user, which can be used to inform the user of the status of the Skill while it is processing the file. | | ## Write metadata cards to file To write one or more cards to a file, you can use the [`POST /files/:id/metadata/global/boxSkillsCards`](e://post_files_id_metadata_global_boxSkillsCards) API and pass along a list of Box Skill `cards`. ``` curl -X POST https://api.box.com/2.0/files/12345/metadata/global/boxSkillsCards \ -H 'Authorization: Bearer <ACCESS_TOKEN>' \ -H 'Content-Type: application/json' \ -d '{ "cards": [{ "type": "skill_card", "skill_card_type": "keyword", "skill_card_title": { "code": "license-plates", "message": "Licence Plates" }, "skill": { "type": "service" "id": "license-plates-service" }, "invocation": { "type": "skill_invocation" "id": "license-plates-service-123" }, "entries": { { "text": "DD-26-YT" }, { "text": "DN86 BOX" } } }], }' ``` ``` const metadata = { cards: [{ "type": "skill_card", "skill_card_type": "keyword", "skill_card_title": { "code": "license-plates", "message": "Licence Plates" }, "skill": { "type": "service" "id": "license-plates-service" }, "invocation": { "type": "skill_invocation" "id": "license-plates-service-123" }, "entries": { { "text": "DD-26-YT" }, { "text": "DN86 BOX" } } }] } client.files.addMetadata('12345', 'global', 'boxSkillsCards', metadata) .then(metadata => { // ... }) ``` ``` metadata = { cards: [{ "type": "skill_card", "skill_card_type": "keyword", "skill_card_title": { "code": "license-plates", "message": "Licence Plates" }, "skill": { "type": "service" "id": "license-plates-service" }, "invocation": { "type": "skill_invocation" "id": "license-plates-service-123" }, "entries": { { "text": "DD-26-YT" }, { "text": "DN86 BOX" } } }] } client.file(file_id='12345').metadata(scope='global', template='boxSkillsCards').create(metadata) ``` ``` BoxFile file = new BoxFile(api, "12345"); Metadata metadata = new Metadata() file.createMetadata("global", "boxSkillsCards", metadata); ``` ``` var metadataValues = new Dictionary<string, object>() { cards: [{ "type": "skill_card", "skill_card_type": "keyword", "skill_card_title": { "code": "license-plates", "message": "Licence Plates" }, "skill": { "type": "service" "id": "license-plates-service" }, "invocation": { "type": "skill_invocation" "id": "license-plates-service-123" }, "entries": { { "text": "DD-26-YT" }, { "text": "DN86 BOX" } } }] }; Dictionary<string, object> metadata = await client.MetadataManager .CreateFileMetadataAsync(fileId: "12345", metadataValues, "global", "boxSkillsCards"); ``` If Box Skill cards have already been applied to this file, this API call will return an error with a HTTP status code of `409`. ## Update metadata card on file If Box Skill cards have already been applied to to a file then it can be updated using the [`PUT /files/:id/metadata/global/boxSkillsCards`](e://put_files_id_metadata_global_boxSkillsCards) API. This API accepts a number of operations (`op`) to perform, and each operation can be used to replace a card at a position (`path`). ``` curl -X PUT https://api.box.com/2.0/files/12345/metadata/global/boxSkillsCards \ -H 'Authorization: Bearer <ACCESS_TOKEN>' \ -H 'Content-Type: application/json-patch+json' \ -d '[ "op": "replace", "path": "/cards/0", "value": { "type": "skill_card", "skill_card_type": "keyword", "skill_card_title": { "code": "license-plates", "message": "Licence Plates" }, "skill": { "type": "service" "id": "license-plates-service" }, "invocation": { "type": "skill_invocation" "id": "license-plates-service-123" }, "entries": { { "text": "DD-26-YT" }, { "text": "DN86 BOX" } } } ]' ``` ``` const updates = [ { 'op': 'replace', 'path': '/cards/0', 'value': { "type": "skill_card", "skill_card_type": "keyword", "skill_card_title": { "code": "license-plates", "message": "Licence Plates" }, "skill": { "type": "service" "id": "license-plates-service" }, "invocation": { "type": "skill_invocation" "id": "license-plates-service-123" }, "entries": { { "text": "DD-26-YT" }, { "text": "DN86 BOX" } } } } ] client.files.updateMetadata('12345', 'global', 'boxSkillsCards', updates) .then(metadata => { // ... }) ``` ``` file_metadata = client.file(file_id='12345').metadata(scope='global', template='boxSkillsCards') card = { "type": "skill_card", "skill_card_type": "keyword", "skill_card_title": { "code": "license-plates", "message": "Licence Plates" }, "skill": { "type": "service" "id": "license-plates-service" }, "invocation": { "type": "skill_invocation" "id": "license-plates-service-123" }, "entries": { { "text": "DD-26-YT" }, { "text": "DN86 BOX" } } } updates = file_metadata.start_update() updates.replace('/cards/0', card) file_metadata.update(updates) ``` ``` BoxFile file = new BoxFile(api, "12345"); Metadata metadata = new Metadata() file.updateMetadata("global", "boxSkillsCards", metadata); ``` ``` var card = new Dictionary<string, object>() { "type": "skill_card", "skill_card_type": "keyword", "skill_card_title": { "code": "license-plates", "message": "Licence Plates" }, "skill": { "type": "service" "id": "license-plates-service" }, "invocation": { "type": "skill_invocation" "id": "license-plates-service-123" }, "entries": { { "text": "DD-26-YT" }, { "text": "DN86 BOX" } } }; var updates = new List<BoxMetadataUpdate>() { new BoxMetadataUpdate() { Op = MetadataUpdateOp.replace, Path = "/cards/0", Value = card } }; Dictionary<string, object> updatedMetadata = await client.MetadataManager .UpdateFileMetadataAsync("12345", updates, "global", "boxSkillsCards"); ``` **Reference:** https://developer.box.com/guides/skills/handle/metadata/ --- ## Untitled *Type: guide | Category: Box Skills * Setup Setting up a Custom Skill is a multi-step process. Prerequisites To set up a Platform App using OAuth 2.0 authentication, you will… # Setup Setting up a Custom Skill is a multi-step process. ## Prerequisites To set up a Platform App using OAuth 2.0 authentication, you will need to ensure you have access the [Developer Console](https://app.box.com/developers/console) from your Box enterprise account. Alternatively, you may sign up for a [developer account](https://account.box.com/signup/n/developer). ## App creation steps ### 1. Log in to the Developer Console Log into Box and navigate to the [Developer Console](https://app.box.com/developers/console). Select **Create New App**. ### 2. Create a Custom Skill Select **Box Custom Skill** option from the list of application types. A modal will appear to prompt the next step. ### 3. Provide a name Finally, select a unique name for your application and click **Create Platform App**. ## Approval You must select a folder that will trigger your skill before you can start using it. Learn more about approving Custom Skills ## Basic configuration Before a Custom Skill can be enabled enabled on a folder, you must complete some additional configuration. ### Invocation URL For every file uploaded, copied, or moved into the selected folder, your skill will send a payload to a remote URL. This URL is called the invocation URL. The Invocation URL can be any HTTP endpoint representing a server, development machine, or serverless function. The only requirement is that the URL is publicly available and accessible by Box servers. For this reason, `localhost` is not a valid address. To set up the Invocation URL, navigate to the **Configuration** tab of the [Developer Console](https://app.box.com/developers/console) and scroll down to the "Invocation URL" section. Fill in a secure HTTPS address and save the form. The invocation URL is now configured. ### File Extensions By default a Custom Skill will trigger for any file type in the folder. To specify only selected file extensions to trigger the skill, navigate to the **Configuration** tab of the [Developer Console](https://app.box.com/developers/console) and scroll down to the **File Extensions** section. **Reference:** https://developer.box.com/guides/skills/handle/setup/ --- ## Untitled *Type: quick-start | Category: SSO & App users * Configure Box Once we have created a login experience with Okta we need to have a Box application available that will permit us to use the… # Configure Box Once we have created a login experience with Okta we need to have a Box application available that will permit us to use the Box APIs to search for and create users that are associated with the Okta user account. ## Set up a Box app # Create a new Box app Create and configure a new Box JWT application to start with a clean user list. # Use an existing approved app Use one of your existing admin approved Box JWT applications from the Box developer console. # Create a new Box app To create a new Box application that may be used to call the Box APIs, use the following steps. 1. Go to the [Developer Console](https://cloud.app.box.com/developers/console) 2. Select **Create Platform App** 3. Select **Platform App** as the type of application to create, and click **Next** 4. Select **OAuth 2.0 with JWT** as the authentication method, and click **Next** 5. Give your Box app a unique name and click **Create App** 6. Go to the app's configuration by clicking **View Your App**. Scroll to the **Required Access Scopes** section of the same screen and ensure that at least the following scopes are enabled: - Read and write all files and folders stored in Box - Manage Users 7. Under **Advanced Features** ensure that both options are enabled to perform actions as users and generate user access tokens. 8. At the top of the page click the button to **Save Changes** Once the application is created it will still need to be approved by an enterprise admin before you will be able to make calls to the Box APIs. Follow [this guide](g://authorization/custom-app-approval) to have the application approved in your enterprise. # Use an Existing JWT Box application If you have an existing JWT based Box application in your [developer console](https://cloud.app.box.com/developers/console) that you would like to use, ensure that the following options are set in the **Configuration** section of your application. - Authentication Method: Should be set to OAuth 2.0 with JWT (Server Authentication). Application Scopes: Set at least the following scopes. - Read and write all files and folders stored in Box - Manage Users Advanced Features: Both options should be enabled to perform actions as users and generate user access tokens. ## Download Required Data To begin working with the Box SDKs used in this tutorial, you will need the application configuration file from the **Configuration** page of your application. This will include all information needed to verify your application to start making API requests with the Box SDKs. Within the **Add and Manage Public Keys** section of the **Configuration** page, click to **Generate a Public/Private Keypair**. This will send you through 2FA verification before downloading the configuration file for your application. Store that file as `config.json` in a location accessible by your application. ## Summary - You created a new, or are using an existing, Box app which is approved by an enterprise admin. - You downloaded your application configuration file and stored it in a location accessible by your application. I downloaded my application configuration file **Reference:** https://developer.box.com/guides/sso-identities-and-app-users/connect-okta-to-app-users/configure-box/ --- ## Untitled *Type: quick-start | Category: SSO & App users * Configure Okta Our next step in the Okta / Box integration is to create and configure the Okta application and users, then extract a few… # Configure Okta Our next step in the Okta / Box integration is to create and configure the Okta application and users, then extract a few pieces of information that we will need to connect to Okta in our application. For this tutorial we will be starting with a blank Okta application and user dashboard to avoid any negative effects on existing installations that may be in place, and to ensure that we have admin rights to the instance. ## Create an Okta Application Starting from the [Okta developer site](https://developer.okta.com/), sign up for a new developer account, or log in under your personal account if you already have one. If you're logging in with an existing account, you should see the Okta dashboard. Click on the **Admin** button at the top right. If you've created a new developer account rather than logging into an existing account, you will have already be redirected to the admin dashboard. You should now see the admin panel. Click on the **Applications** option at the top. On the application page, click the **Add Application** button. Select **Web** as the application type and click the **Next** button. Okta employs both [OAuth 2](https://oauth.net/2/) and [OpenID Connect](https://openid.net/) (OIDC) for application authorization and user authentication, respectively. The OpenID Connect integration allows us to use baked in OIDC connectors within a number of popular language frameworks to simplify application and user management by handling the callback routes and providing methods for logging in, logging out, and protecting routes into your application. To simplify this first integration, we're going to use the default callback routes and settings for the language and framework OIDC connector. Depending on your preferred integration type, the configuration settings will slightly change. Fill out the application details with the following configuration settings: - Name: Any - Base URIs: `http://localhost:3000/` - Login redirect URIs: `http://localhost:3000/authorization-code/callback` - Logout redirect URIs: `http://localhost:3000/logout` - Grant type allowed: Only **Authorization Code** selected - Name: Any - Base URIs: `http://localhost:8080/` - Login redirect URIs: `http://localhost:8080/authorization-code/callback` - Logout redirect URIs: `http://localhost:8080/logout` - Grant type allowed: Only **Authorization Code** selected - Name: Any - Base URIs: `http://127.0.0.1:5000/` - Login redirect URIs: `http://127.0.0.1:5000/oidc/callback` - Logout redirect URIs: `http://127.0.0.1:5000/logout` - Grant type allowed: Only **Authorization Code** selected - Name: Any - Base URIs: `https://localhost:5001/` - Login redirect URIs: `https://localhost:5001/authorization-code/callback` - Logout redirect URIs: `https://localhost:5001/logout` - Grant type allowed: Only **Authorization Code** selected # Incomplete previous step Please select a preferred language / framework in step 1 to get started. Click the **Done** button to create the application and be dropped on to the general settings of the application. ## Copy Application Credentials Using the configuration files set up in the last step, we next have to add in the Okta application org and app details within the files. Most Okta application information can be found on the general settings page, with the exception of the `Org URL` that is used in the configuration URIs to reference back to your Okta organization. To obtain the `Org URL`, go to the dashboard of your Okta admin console. The `Org URL` will be in the top right corner of the screen. Depending on the language and framework previously chosen, we'll set up the appropriate configuration files. - Open `config.json` within the local application directory in your preferred editor. Update the following line items with the appropriate Okta configuration info: - `oktaClientId`: Obtained from the **Client Credentials** section of the application general settings. - `oktaClientSecret`: Obtained from the **Client Credentials** section of the application general settings. - `oktaOrgUrl`: Obtained from the top right of the main admin dashboard page. Save the file. Your `config.json` file should look similar to the following. ``` const oktaClientId = exports.oktaClientId = '0oa48567frkg5KW4x6'; const oktaClientSecret = exports.oktaClientSecret = 'cugDJy2ERfIQHDXv-j2134DfTTes-Sa3'; const oktaOrgUrl = exports.oktaOrgUrl = 'YOURDOMAIN.okta.com'; const oktaBaseUrl = exports.oktaBaseUrl = 'http://localhost:3000'; const oktaRedirect = exports.oktaRedirect = '/authorization-code/callback'; ``` Open the `/src/main/resources/application.properties` file and update the following lines: - `okta.oauth2.issuer`: Your Org URL, obtained from the top right of the main admin dashboard page, followed by `/oauth2/default`. For example, if your Org URL was `https://dev-123456.okta.com`, the issuer string should be `https://dev-123456.okta.com/oauth2/default`. - `okta.oauth2.clientId`: Obtained from the **Client Credentials** section of the application general settings. - `okta.oauth2.clientSecret`: Obtained from the **Client Credentials** section of the application general settings. Save the file Your `/src/main/resources/application.properties` file should look similar to the following. ``` okta.oauth2.redirect-uri=/authorization-code/callback okta.oauth2.issuer=https://YOURDOMAIN.okta.com/oauth2/default okta.oauth2.clientId=0oa48567frkg5KW4x6 okta.oauth2.clientSecret=cugDJy2ERfIQHDXv-j2134DfTTes-Sa3 security.oauth2.sso.loginPath=/authorization-code/callback ``` In addition to the standard configuration information for the org and app, the Python / Flask integration requires an additional auth token. To create an auth token: - Go to the **API** -> **Token** section of the Okta admin dashboard. - Click the **Create Token** button. - Enter a name for the token and click **Create**. - Copy the token that is generated. Next, update the local application configuration file. - Open `config.py` within the local application directory in your preferred editor. Update the following line items with the appropriate Okta configuration info: - `okta_client_secret`: Obtained from the **Client Credentials** section of the application general settings. - `okta_org_url`: Obtained from the top right of the main admin dashboard page. - `okta_auth_token`: The token created above. Save the file. Your `config.py` file should look similar to the following. ``` okta_client_id = '0oa48567frkg5KW4x6' okta_client_secret = 'cugDJy2ERfIQHDXv-j2134DfTTes-Sa3' okta_org_url = 'http://YOURDOMAIN.okta.com' okta_auth_token = '01KkTQTRfs1yKLr4Ojy26iqoIjK_4fHyq132Dr5T' okta_callback_route = '/oidc/callback' ``` Lastly, update the Flask configuration file - Open `client_secrets.json` within the local application directory in your preferred editor. Update the following line items with the appropriate Okta configuration info: - `client_id`: Obtained from the **Client Credentials** section of the application general settings. - `client_secret`: Obtained from the **Client Credentials** section of the application general settings. - `auth_uri`: Your Org URL, obtained from the top right of the main admin dashboard page, followed by `/oauth2/default/v1/authorize`. For example, if your Org URL was `https://dev-123456.okta.com`, the issuer string should be `https://dev-123456.okta.com/oauth2/default/v1/authorize`. - `token_uri`: Your Org URL, obtained from the top right of the main admin dashboard page, followed by `/oauth2/default/v1/token`. For example, if your Org URL was `https://dev-123456.okta.com`, the issuer string should be `https://dev-123456.okta.com/oauth2/default/v1/token`. - `issuer`: Your Org URL, obtained from the top right of the main admin dashboard page, followed by `/oauth2/default`. For example, if your Org URL was `https://dev-123456.okta.com`, the issuer string should be `https://dev-123456.okta.com/oauth2/default`. - `userinfo_uri`: Your Org URL, obtained from the top right of the main admin dashboard page, followed by `/oauth2/default/userinfo`. For example, if your Org URL was `https://dev-123456.okta.com`, the issuer string should be `https://dev-123456.okta.com/oauth2/default/userinfo`. Save the file. Your `client_secrets.json` file should look similar to the following. ``` { "web": { "client_id": "0oa48567frkg5KW4x6", "client_secret": "cugDJy2ERfIQHDXv-j2134DfTTes-Sa3", "auth_uri": "https://YOURDOMAIN.okta.com/oauth2/default/v1/authorize", "token_uri": "https://YOURDOMAIN.okta.com/oauth2/default/v1/token", "issuer": "https://YOURDOMAIN.okta.com/oauth2/default", "userinfo_uri": "https://YOURDOMAIN.okta.com/oauth2/default/userinfo", "redirect_uris": [ "http://127.0.0.1:5000/oidc/callback" ] } } ``` - Open `Startup.cs` within the local application directory in your preferred editor. Update the following line items within the `ConfigureServices` method with the appropriate Okta configuration info: - `OktaDomain`: Obtained from the top right of the main admin dashboard page. - `ClientId`: Obtained from the **Client Credentials** section of the application general settings. - `ClientSecret`: Obtained from the **Client Credentials** section of the application general settings. Save the file. Your `ConfigureServices` method should look similar to the following. ``` services.AddControllersWithViews(); services.AddAuthentication(options => { options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = OktaDefaults.MvcAuthenticationScheme; }) .AddCookie() .AddOktaMvc(new OktaMvcOptions { OktaDomain = "https://YOURDOMAIN.okta.com", ClientId = "0oa48567frkg5KW4x6", ClientSecret = "cugDJy2ERfIQHDXv-j2134DfTTes-Sa3" }); ``` # Incomplete previous step Please select a preferred language / framework in step 1 to get started. ## Create a User Our last step in the Okta setup is to create a test user that we will use to log in to the application. 1. Go to the **Users** section of the Okta admin dashboard. 2. Click on the **Add Person** button. 3. Enter all appropriate user info. Under password, select **Set by admin** and input a password for the user. Also deselect the **User must change password on first login** option. You will use the username and password to log in. These settings will only be used for testing purposes and are not best practices for user creation and security. 4. Click the **Save** button to create the user. ## Summary - You created an Okta application. - You updated the Okta configuration information in the local application. - You created a test Okta user. I've created my Okta app and set up user / local configuration **Reference:** https://developer.box.com/guides/sso-identities-and-app-users/connect-okta-to-app-users/configure-okta/ --- ## Untitled *Type: quick-start | Category: SSO & App users * Connect Okta identities to Box App Users in a web app Okta is a popular access management and identity platform used by companies to provide… # Connect Okta identities to Box App Users in a web app [Okta](https://www.okta.com/) is a popular access management and identity platform used by companies to provide a unified method for managing and authenticating into multiple applications with a single set of credentials and a single secure dashboard. When connected to a custom Box application, the [Okta APIs](https://developer.okta.com/) may be used to provide a single login mechanism to identify users within the Box application, allowing you to have a unified experience between your unified identity system (Okta) and the Box APIs. ## Overview This quick start guide will walk you through how to programmatically use Okta to log in to a Box application, provision an app user in Box that is linked to the Okta user, then make Box API calls on behalf of that user. This will take you through the following steps. 1. [Scaffold your application code](g://sso-identities-and-app-users/connect-okta-to-app-users/scaffold-application-code) so that we have a web application that we can log in to. 2. [Setup and configure your Okta application](g://sso-identities-and-app-users/connect-okta-to-app-users/configure-okta) and create a first user that we can use to log into the web app with, and eventually create a Box account for. 3. [Setup and configure your Box application](g://sso-identities-and-app-users/connect-okta-to-app-users/configure-box) so that we can connect your web application to Box. 4. [Create a log in flow for your application](g://sso-identities-and-app-users/connect-okta-to-app-users/logging-into-app), allowing the Okta user to log in to your web application. 5. [Find an existing Box user, and optionally create a Box user](g://sso-identities-and-app-users/connect-okta-to-app-users/find-or-create-box-users) the first time that Okta user logs into your web application. 6. [And finally, run the application](g://sso-identities-and-app-users/connect-okta-to-app-users/run-the-app) and the see the complete flow in action. At the end of the tutorial we will output a single message to the browser. If this is the first time an Okta user logs in to Box via the application, an associated Box user will be created and the message `New user created: {{USERNAME}}` will be output to the browser. When attempting to log in with this user in subsequent attempts, a message stating `Hello {{USERNAME}}` will now be output to the browser. To simplify this guide we will not create any user interface for the web application. Instead we will provide some output via the application console / terminal and directly as text output in the browser. I am ready to get started **Reference:** https://developer.box.com/guides/sso-identities-and-app-users/connect-okta-to-app-users/ --- ## Untitled *Type: quick-start | Category: SSO & App users * Find or create Box app users At this point we have application code that will handle traffic from users visiting, forward them to Okta to… # Find or create Box app users At this point we have application code that will handle traffic from users visiting, forward them to Okta to login, provide Okta user information, before finally handing off to a yet to be created handler for Box. This section will cover the final Box components: - Validating if an Okta user has an associated Box app user account. - Creating a new app user for the associated Okta record if they don't. - Fetching tokens for the Box user to make user-specific API calls. ## Create Platform App Users Before validating users we need a method for creating an associated Box user account if one doesn't already exist for the Okta user. In your local application directory, load the `server.js` file created in step 1. Add the following `box` object into the file and save. ``` const box = (() => { const configJSON = JSON.parse(fs.readFileSync(path.resolve(__dirname, './config.json'))); const sdk = boxSDK.getPreconfiguredInstance(configJSON); const client = sdk.getAppAuthClient('enterprise'); let oktaRecord = {}; let userId = ''; let userClient; function validateUser(userInfo, res) { // TODO: VALIDATE USER } function createUser(res) { // TODO: CREATE USER } return { validateUser, createUser }; })(); ``` This object defines a number of items: - Configuration: A new instance of the Box Node SDK is instantiated and made available to the object functions, along with a number of variables. - `validateUser` function: Will house the code to validate whether a Box user exists for an associated Okta user. - `createUser` function: Creates a new Box user bound to the associated Okta user ID. With that structure defined, replace the `// TODO: CREATE USER` section with the following code. ``` const spaceAmount = 1073741824; // ~ 1gb client.enterprise.addAppUser( this.oktaRecord.name, { space_amount: spaceAmount, external_app_user_id: this.oktaRecord.sub } ).then(appUser => { res.send(`New user created: ${appUser.name}`); }); ``` This code will create a new Box app user and will set the `external_app_user_id` parameter of the user object to the unique Okta user ID, which will define the binding between the two user records. In your local application directory, load the `/src/main/java/com/box/sample/Application.java` file created in step 1, or similar directory if an alternate application name was used. Within the `public class Application` definition, add the following methods: ``` static String validateUser(OidcUser user) throws IOException { // TODO: VALIDATE USER } static String createUser(OidcUser user) { // TODO: CREATE USER } ``` These methods will handle the Box user validation and creation. Breaking them down: - `validateUser`: Will house the code to validate whether a Box user exists for an associated Okta user. - `createUser`: Creates a new Box user bound to the associated Okta user ID. With those methods defined, replace `# TODO: CREATE USER` with the following code: ``` String oktaName = (String) user.getAttributes().get("name"); Object oktaSub = user.getAttributes().get("sub"); CreateUserParams params = new CreateUserParams(); params.setExternalAppUserId((String) oktaSub); BoxUser.Info createdUserInfo = BoxUser.createAppUser(api, oktaName, params); return "New User Created: " + createdUserInfo.getName(); ``` This code will create a new Box app user and will set the `external_app_user_id` parameter of the user object to the unique Okta user ID, which will define the binding between the two user records. In your local application directory, load the `server.py` file created in step 1. Add the following `Box` class object to the existing code, below the route definitions. ``` # Box user class class Box(object): def __init__(self): # Instantiate Box Client instance auth = JWTAuth.from_settings_file('../config.json') self.box_client = Client(auth) # Validate if Box user exists def validateUser(self, g): # TODO: VALIDATE USER # Create new Box user def createUser(self, ouser): # TODO: CREATE USER ``` This class defines: - `init`: When initialized, a new instance of the Box Python SDK is instantiated and made available to the object methods. - `validateUser` method: Accepting a user object as input, this will house the code to validate whether a Box user exists for an associated Okta user. - `createUser` method: Accepting a user object as input, this creates a new Box user bound to the associated Okta user ID. With that class defined, replace the `# TODO: CREATE USER` section with the following code. ``` user_name = f'{ouser.profile.firstName} {ouser.profile.lastName}' uid = ouser.id space = 1073741824 user = self.box_client.create_user(user_name, None, space_amount=space, external_app_user_id=uid) return f'New user created: {user_name}' ``` This code will create a new Box app user and will set the `external_app_user_id` parameter of the user object to the unique Okta user ID, which will define the binding between the two user records. Within the `Controllers` > `AccountController.cs` file, inside the associated `AccountController` class, add the following method. ``` static async Task validateUser(string name, string sub) { // Configure Box SDK instance var reader = new StreamReader("config.json"); var json = reader.ReadToEnd(); var config = BoxConfig.CreateFromJsonString(json); var sdk = new BoxJWTAuth(config); var token = sdk.AdminToken(); BoxClient client = sdk.AdminClient(token); // Search for matching Box app user for Okta ID BoxCollection<BoxUser> users = await client.UsersManager.GetEnterpriseUsersAsync(externalAppUserId:sub); System.Diagnostics.Debug.WriteLine(users.TotalCount); if (users.TotalCount > 0) { // TODO: VALIDATE USER } else { // TODO: CREATE USER } } ``` Within the code block a new Box .NET SDK client is created using the `config.json` file downloaded in step 2. In the case of this code sample, that `config.json` file is stored at the root of the local application directory. That client is then used to search all users in the Box enterprise, passing in the Okta `sub` unique ID as the `externalAppUserId` search parameter. The number of users returned is then checked to see if a valid user was found. With that structure defined, replace the // TODO: CREATE USER section with the following code. ``` var userRequest = new BoxUserRequest() { Name = name, ExternalAppUserId = sub, IsPlatformAccessOnly = true }; var user = await client.UsersManager.CreateEnterpriseUserAsync(userRequest); System.Diagnostics.Debug.WriteLine("New user created: " + user.Name); ``` This code will create a new Box app user and will set the `external_app_user_id` parameter of the user object to the unique Okta user ID, which will define the binding between the two user records. A diagnostic message is then written back stating that the new user was created. # Incomplete previous step Please select a preferred language / framework in step 1 to get started. ## Validate Okta Users With the create user functionality defined, let's turn our attention to defining the code for validating whether an Okta user record has an associated Box user record by searching all Box enterprise users for the associated `external_app_user_id`. Replace the `// TODO: VALIDATE USER` comment with the following: ``` this.oktaRecord = userInfo client.enterprise.getUsers({ "external_app_user_id": this.oktaRecord.sub }) .then((result) => { if (result.total_count > 0) { // TODO: MAKE AUTHENTICATED USER CALL } else { // User not found - create user this.createUser(); } }); ``` Using the Box Node SDK, we call `enterprise.getUsers` to search all enterprise users, and pass in the unique Okta user ID as the `external_app_user_id` value to search specifically for that user. If found (number of records is greater than 0) we can make an authenticated call to the Box APIs using that user record, which will be defined in the next section. If not found, we call the `createUser` function we defined in the last section to create a new Box user with that `external_app_user_id` association. Replace the `// TODO: VALIDATE USER` comment with the following: ``` // Set up Box enterprise client Reader reader = new FileReader("config.json"); BoxConfig config = BoxConfig.readFrom(reader); api = BoxDeveloperEditionAPIConnection.getAppEnterpriseConnection(config); // Get Okta user sub for unique ID attachment to Box user Object oktaSub = user.getAttributes().get("sub"); // Check enterprise users for matching external_app_user_id against Okta sub URL url = new URL("https://api.box.com/2.0/users?external_app_user_id=" + oktaSub); BoxAPIRequest request = new BoxAPIRequest(api, url, "GET"); BoxJSONResponse jsonResponse = (BoxJSONResponse) request.send(); JsonObject jsonObj = jsonResponse.getJsonObject(); JsonValue totalCount = jsonObj.get("total_count"); // Set return string String outputString = ""; if (totalCount.asInt() > 0) { // TODO: MAKE AUTHENTICATED USER CALL } else { outputString = createUser(user); } return outputString; ``` Using the Box Java SDK generic request method, we make a call directly to the `https://api.box.com/2.0/users` endpoint to search enterprise users, passing in the unique Okta user ID as the `external_app_user_id` value to search specifically for that user. If found (number of records is greater than 0) we can make an authenticated call to the Box APIs using that user record, which will be defined in the next section. If not found, we call the `createUser` function we defined in the last section to create a new Box user with that `external_app_user_id` association. Replace the `# TODO: VALIDATE USER` comment with the following: ``` # Fetch Okta user ID uid = g.user.id # Validate is user exists url = f'https://api.box.com/2.0/users?external_app_user_id={uid}' response = self.box_client.make_request('GET', url) user_info = response.json() # If user not found, create user, otherwise fetch user token if (user_info['total_count'] == 0): self.createUser(g.user) else: # TODO: MAKE AUTHENTICATED USER CALL ``` Using the Box Python SDK generic request method, we make a call directly to the `https://api.box.com/2.0/users` endpoint to search enterprise users, passing in the unique Okta user ID as the `external_app_user_id` value to search specifically for that user. If found (number of records is greater than 0) we can make an authenticated call to the Box APIs using that user record, which will be defined in the next section. If not found, we call the `createUser` function we defined in the last section to create a new Box user with that `external_app_user_id` association. Replace the `// TODO: VALIDATE USER` comment with the following: ``` var userId = users.Entries[0].Id; var userToken = sdk.UserToken(userId); BoxClient userClient = sdk.UserClient(userToken, userId); // TODO: MAKE AUTHENTICATED USER CALL ``` If a valid user is found, the Box ID is extracted and used to generate a Box SDK client that is scoped specifically for that user, rather than the application. # Incomplete previous step Please select a preferred language / framework in step 1 to get started. ## Make Authenticated Box User Calls Once an associated Box user is found for the Okta user we're going to generate an access token specifically [scoped for that user](g://authentication/jwt/user-access-tokens) to make Box API calls, then make a call to get the current user to ensure that everything is working and that we have a valid user access token. Replace `// TODO: MAKE AUTHENTICATED USER CALL` from the previous section with the following: ``` this.userId = result.entries[0].id; this.userClient = sdk.getAppAuthClient('user', this.userId); this.userClient.users.get(this.userClient.CURRENT_USER_ID) .then(currentUser => { res.send(`Hello ${currentUser.name}`); }); ``` With a user found we capture the Box user ID, then generate a user client object scoped for that user. We finish by making a call to fetch the current user with the user client object, which should return the user profile information for the Okta associated Box app user. Replace `// TODO: MAKE AUTHENTICATED USER CALL` from the previous section with the following: ``` // User found, authenticate as user // Fetch user ID JsonArray entries = (JsonArray) jsonObj.get("entries"); JsonObject userRecord = (JsonObject) entries.get(0); JsonValue userId = userRecord.get("id"); // Get user scoped access token and fetch current user with it BoxDeveloperEditionAPIConnection userApi = BoxDeveloperEditionAPIConnection.getAppUserConnection(userId.asString(), config); BoxUser currentUser = BoxUser.getCurrentUser(userApi); BoxUser.Info currentUserInfo = currentUser.getInfo(); outputString = "Hello " + currentUserInfo.getName(); ``` With a user found we capture the Box user ID, then generate a user client object scoped for that user. We finish by making a call to fetch the current user with the user client object, which should return the user profile information for the Okta associated Box app user. Replace `# TODO: MAKE AUTHENTICATED USER CALL` from the previous section with the following: ``` # Create user client based on discovered user user = user_info['entries'][0] user_to_impersonate = self.box_client.user(user_id=user['id']) user_client = self.box_client.as_user(user_to_impersonate) # Get current user current_user = user_client.user().get() return f'Hello {current_user.name}' ``` With a user found we capture the Box user ID, then generate a user client object scoped for that user. We finish by making a call to fetch the current user with the user client object, which should return the user profile information for the Okta associated Box app user. Replace `// TODO: MAKE AUTHENTICATED USER CALL` from the previous section with the following: ``` BoxUser currentUser = await userClient.UsersManager.GetCurrentUserInformationAsync(); System.Diagnostics.Debug.WriteLine("Current user name: " + currentUser.Name); ``` Using that user scoped client, the current user record is then extracted from Box, and a diagnostic message is written back stating the current user name. # Incomplete previous step Please select a preferred language / framework in step 1 to get started. ## Summary - You've validated whether an Okta user exists as a Box user. - You've creating a new app user if they don't exist. - You're making a Box API call for an existing Box user. I have set up Box user validation and creation **Reference:** https://developer.box.com/guides/sso-identities-and-app-users/connect-okta-to-app-users/find-or-create-box-users/ --- ## Untitled *Type: quick-start | Category: SSO & App users * Run the application With all the components of the sample in place, it's time to run our program to see if everything is working correctly… # Run the application With all the components of the sample in place, it's time to run our program to see if everything is working correctly. From the terminal / command prompt in the local application directory, type `node server.js` and click enter. The server will start up and output `Server started: Listening on port 3000`. From your browser, go to `http://localhost:3000/`. Since this is the first time we'll be testing out the user sign in flow, you'll see the Okta login. Sign in with the credentials for the Okta user you created in the last section of [step 2](g://sso-identities-and-app-users/connect-okta-to-app-users/configure-okta). Once signed in, you will see a message stating `New user created: {{USERNAME}}` output to the browser. When attempting to log in with this user in subsequent attempts, you will now see `Hello {{USERNAME}}` output to the browser. From Eclipse (or your preferred editor) click to run the application. You will see console output stating that the Spring boot application is now running on port 8080. From your browser, go to `http://localhost:8080/`. Since this is the first time we'll be testing out the user sign in flow, you'll see the Okta login. Sign in with the credentials for the Okta user you created in the last section of [step 2](g://sso-identities-and-app-users/connect-okta-to-app-users/configure-okta). Once signed in, you will see a message stating `New User Created: {{USERNAME}}` output to the browser. When attempting to log in with this user in subsequent attempts, you will now see `Hello {{USERNAME}}` output to the browser. From the terminal / command prompt in the local application directory, type `env FLASK_APP=server.py flask run` and click enter. The server will start up and output `Running on http://127.0.0.1:5000/`. From your browser, go to `http://127.0.0.1:5000/`. Since this is the first time we'll be testing out the user sign in flow, you'll see the Okta login. Sign in with the credentials for the Okta user you created in the last section of [step 2](g://sso-identities-and-app-users/connect-okta-to-app-users/configure-okta). Once signed in, you will see a message stating `New user created: {{USERNAME}}` output to the browser. When attempting to log in with this user in subsequent attempts, you will now see `Hello {{USERNAME}}` output to the browser. From Visual Studio Code (or your preferred editor) click `Run` -> `Start Debugging` from the menu. You will see debug console output as the application loads, then a browser window will be loaded with the application at `https://localhost:5001/`. Since this is the first time testing the application, the sign in link will be displayed. Once clicked, the Okta login will be automatically loaded. Sign in with the credentials for the Okta user you created in the last section of [step 2](g://sso-identities-and-app-users/connect-okta-to-app-users/configure-okta). Once signed in, you will see a message in the debug console stating `New user created: {{USERNAME}}` output to the browser. When attempting to log in with this user in subsequent attempts, you will now see `Current user name: {{USERNAME}}` output to the browser. # Incomplete previous step Please select a preferred language / framework in step 1 to get started. ## Summary You've reached the end of this Quick Start guide. By now you should have taken the following steps. 1. [Created a web application scaffold](g://sso-identities-and-app-users/connect-okta-to-app-users/scaffold-application-code). 2. [Setup and configured an Okta application](g://sso-identities-and-app-users/connect-okta-to-app-users/configure-okta) and created a first user that could be used to log into the web app. 3. [Setup and configured a Box application](g://sso-identities-and-app-users/connect-okta-to-app-users/configure-box) so that the web app can connect to Box. 4. [Created a log in flow for the web application](g://sso-identities-and-app-users/connect-okta-to-app-users/logging-into-app), allowing the Okta user to log in. 5. [Added a check to find an existing Box user](g://sso-identities-and-app-users/connect-okta-to-app-users/find-or-create-box-users), and optionally create a Box user the first time that Okta user logs into the web application. 6. [And finally, ran the application](g://sso-identities-and-app-users/connect-okta-to-app-users/run-the-app) and saw the complete flow in action. ## Next Steps We recommend the following resource for anyone who wants to learn more about more advanced capabilities that may be attached into the user creation and access process. - [User provisioning](g://users/provision) best practices for advanced user folder architecture setup. - [User deprovisioning](g://users/deprovision) best practices cleaning up inactive users and transferring user content to another account. - [Uploading content](g://uploads) into Box, including running preflight checks and large file (chunked) uploading. **Reference:** https://developer.box.com/guides/sso-identities-and-app-users/connect-okta-to-app-users/run-the-app/ --- ## Untitled *Type: quick-start | Category: SSO & App users * Logging into the app with Okta With the Okta, Box, and basic application set up, we can turn our attention to the first step in the… # Logging into the app with Okta With the Okta, Box, and basic application set up, we can turn our attention to the first step in the application code flow, the Okta login. During the Okta login we will employ the OpenID Connect (OIDC) frameworks of the language used to redirect the user to Okta to log in and pass Okta user information back to the application. Those Okta user details will in turn be used to validate and create Box users in the next step. This section will walk you through: - Setting up the application configuration skeleton. - Defining the routes for the chosen framework to handle user traffic. - Passing Okta user information to the next Box user validation step. ## Set up the Skeleton In your local application directory, load the `server.js` file created in step 1. Start by copying the following package definitions and configuration information into the file. ``` const session = require('express-session'); const { ExpressOIDC } = require('@okta/oidc-middleware'); const bodyParser = require('body-parser'); const boxSDK = require('box-node-sdk'); const config = require('./config.js'); const express = require('express')(); const http = require('http'); const path = require('path'); const fs = require('fs'); express.use(session({ secret: 'this should be secure', resave: true, saveUninitialized: false })); const oidc = new ExpressOIDC({ issuer: `https://${config.oktaOrgUrl}/oauth2/default`, client_id: config.oktaClientId, client_secret: config.oktaClientSecret, appBaseUrl: config.oktaBaseUrl, loginRedirectUri: `${config.oktaBaseUrl}${config.oktaRedirect}`, scope: 'openid profile' }); express.use(oidc.router); express.use(bodyParser.json()); express.use(bodyParser.urlencoded({ extended: true })); ``` This sets up the Express configuration and Okta OIDC connector information. Express is set to use the OIDC connector and the Okta information that we saved in step 2 of this quick start is used to configure the connector for our Okta integration. Now add the routing details. ``` // Redirect to Okta login express.get('/', (req, res) => { // TODO: HANDLE ROUTE }); ``` This defines the entry route for our application. When a user attempts to visit our application root (`/`) the code within this route will be run. Lastly, add the Express server initialization to listen for traffic. ``` // Create server const port = process.env.PORT || 3000; http.createServer(express).listen(port, () => { console.log(`Server started: Listening on port ${port}`); }); ``` In your local application directory, load the `/src/main/java/com/box/sample/Application.java` file created in step 1, or similar directory if an alternate application name was used. Copy the following basic application structure into the file. ``` package com.box.okta.sample; import java.io.FileReader; import java.io.IOException; import java.io.Reader; import java.net.URL; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.oauth2.core.oidc.user.OidcUser; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.box.sdk.BoxAPIRequest; import com.box.sdk.BoxConfig; import com.box.sdk.BoxDeveloperEditionAPIConnection; import com.box.sdk.BoxJSONResponse; import com.box.sdk.BoxUser; import com.box.sdk.CreateUserParams; import com.eclipsesource.json.JsonArray; import com.eclipsesource.json.JsonObject; import com.eclipsesource.json.JsonValue; @RestController @EnableAutoConfiguration public class Application { static BoxDeveloperEditionAPIConnection api; // TODO: SET ROUTE // TODO: INITIALIZE SERVER } ``` This sets up the needed imports, the `Application` class, and a standard shared Box API connection attribute, to be defined in the next step. Replace `// TODO: SET ROUTE` with the following. ``` @RequestMapping("/") String home(@AuthenticationPrincipal OidcUser user) throws IOException { // TODO: HANDLE ROUTE } ``` The route mapping defines the entry route for our application. When a user attempts to visit our application root (`/`) in a logged out state, the OIDC connector will automatically push them through the Okta login, so we don't need to setup a redirect. When the user is in a logged in state, the code within this route will be run. Replace `// TODO: INITIALIZE SERVER` with the following to initialize the Spring Boot server to listen for traffic. ``` public static void main(String[] args) { SpringApplication.run(Application.class, args); } ``` In your local application directory, load the `server.py` file created in step 1. Copy the following basic application structure into the file. ``` from flask import Flask, redirect, g, url_for from flask_oidc import OpenIDConnect from okta import UsersClient from boxsdk import Client from boxsdk import JWTAuth import requests import config import json app = Flask(__name__) app.config.update({ 'SECRET_KEY': config.okta_client_secret, 'OIDC_CLIENT_SECRETS': './client_secrets.json', 'OIDC_DEBUG': True, 'OIDC_ID_TOKEN_COOKIE_SECURE': False, 'OIDC_SCOPES': ["openid", "profile"], 'OIDC_CALLBACK_ROUTE': config.okta_callback_route }) oidc = OpenIDConnect(app) okta_client = UsersClient(config.okta_org_url, config.okta_auth_token) ``` This sets up the Flask configuration, Okta client, and Okta OIDC connector information. Flask is set to use the OIDC connector and the Okta information that we saved in step 2 of this quick start is used to configure the connector for our Okta integration. Next, add a `before_request` definition to be run before route handling is engaged. We'll be using this to capture our Okta user information, if available. ``` # Fetch Okta user record if logged in @app.before_request def before_request(): # TODO: HANDLE BEFORE REQUEST ``` Lastly, define the entry route for our application, as well as a `box_auth` route. ``` # Main application route @app.route('/') def start(): # TODO: HANDLE MAIN ROUTE # Box user verification @app.route("/box_auth") @oidc.require_login def box_auth(): # TODO: HANDLE BOX AUTH ROUTE return 'Complete' ``` When a user attempts to visit our application root (`/`) the code within this route will be run. When we validate an Okta user, the code within the `box_auth` route will be run. In your local application, load `Views` > `Shared` > `Layout.cshtml`. Once the ASP.NET application loads this will be the visual component that is rendered. At the top of the page, insert the following. ``` @using System.Security.Claims; @if (User.Identity.IsAuthenticated) { <p class="navbar-text">Hello, @User.Identity.Name</p> } else { <a asp-controller="Account" asp-action="SignIn">Sign In</a> } ``` If a user who is logged in to Okta visits, they will see the hello message. If the user is not logged in a sign in link will be provided to them. Within the line `<a asp-controller="Account" asp-action="SignIn">Sign In</a>`, `asp-controller="Account"` means that the to be created Account controller will handle the request, and `asp-action="SignIn"` states that the `SignIn` method in that controller will be enacted. Save and close that file. Within the `Controllers` directory create a new file, `AccountController.cs`. This will be the controller that is enacted when that sign in link is clicked. Copy the following basic application structure into the file. ``` using System.IO; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; using Okta.AspNetCore; using Box.V2; using Box.V2.Config; using Box.V2.JWTAuth; using Box.V2.Models; public class AccountController : Controller { public IActionResult SignIn() { if (!HttpContext.User.Identity.IsAuthenticated) { return Challenge(OktaDefaults.MvcAuthenticationScheme); } return RedirectToAction("Profile", "Account"); } [Authorize] [Route("~/profile")] public IActionResult Profile() { // TODO: HANDLE ROUTE } } ``` When the user clicks on the sign in link the `SignIn` method in this controller will be run. If the user is not already authenticated they will be sent to `Challenge`, which will redirect the user to Okta to log in. This functionality is handled by the routing framework and does not require any additional code to enact. If the user is authenticated, they will be redirected to the `Profile` routing method. # Incomplete previous step Please select a preferred language / framework in step 1 to get started. ## Setup Application Route We now need to define the code that will run when our main route (`/`) is engaged. Replace `// TODO: HANDLE ROUTE` in the main route with the following code. ``` if (req.userContext && req.userContext.userinfo) { const tokenSet = req.userContext.tokens; const userInfo = req.userContext.userinfo; // If Okta ID is present, pass to Box user validation if (userInfo.sub) { box.validateUser(userInfo, res); } else { console.log('No Okta ID identified'); } } else { res.redirect('/login'); } ``` What we are doing in the above is first checking to see if there is any Okta user information available from the OIDC connector. When a user logs in the connector will make the Okta user and configuration information available to our route within `req.userContext`. If user information is present, meaning the user is logged in to Okta, we pass the user information to `box.validateUser` along with the Express response object to see if there is an associated Box user available, which we'll define in the next step. If no user information is present, we redirect the user to `/login`. The OIDC connector will automatically handle this route and force the user through to the Okta login. Replace `// TODO: HANDLE MAIN ROUTE` in the main route with the following code. ``` // Validate OIDC user against Box return validateUser(user); ``` The Java OIDC connector handles most of the heavy lifting for us. When a logged out user accesses this route they will automatically be pushed to the Okta login. Once logged in, an OIDC user object will be made available to the route. We pass that user object to a `validateUser` function, which we'll define in the next step. Replace `// TODO: HANDLE BEFORE REQUEST` in the main route with the following code. ``` if oidc.user_loggedin: g.user = okta_client.get_user(oidc.user_getfield('sub')) else: g.user = None ``` This will check if an OIDC user is available, meaning that the user has already logged in to Okta. If available we set a user object using the Okta client object, and if not we set the user object to `None`. Next, replace `// TODO: HANDLE ROUTE` in the main route with the following code. ``` return redirect(url_for(".box_auth")) ``` When this code is engaged, if the user is not logged in to Okta they will be redirected to Okta to log in by the OIDC connector. After login, or if the user is already logged in, they will then be forwarded to the `box_auth` route code. Finally, replace `// TODO: HANDLE BOX AUTH ROUTE` in the `box_auth` route with the following code. ``` box = Box() return box.validateUser(g) ``` This creates a new instance of the Box class and calls the `validateUser` method, passing in the Okta user object. We'll define this class and methods in the next step. Replace `// TODO: HANDLE ROUTE` in the main route with the following code. ``` var subClaim = HttpContext.User.Claims.First(c => c.Type == "sub"); var sub = subClaim.Value; var nameClaim = HttpContext.User.Claims.First(c => c.Type == "name"); var name = nameClaim.Value; Task userSearch = validateUser(name, sub); Task.WaitAll(userSearch); return Content(name); ``` This block will capture the Okta user account sub (unique ID) and name, then sends that data to the to be created `validateUser` method to find a matching Box user, which will be created in the next step. # Incomplete previous step Please select a preferred language / framework in step 1 to get started. ## Summary - You set up the skeleton routes and configuration for Okta. - You set up the main route handler to pass to Box user verification. I have set up the Okta login **Reference:** https://developer.box.com/guides/sso-identities-and-app-users/connect-okta-to-app-users/logging-into-app/ --- ## Untitled *Type: guide | Category: Tasks * Assign a task to a user To assign a task to a user you will need to provide the POST /task_assignments API with the id of the task and the… # Assign a task to a user To assign a task to a user you will need to provide the [`POST /task_assignments`](e://post_task_assignments) API with the `id` of the task and the user's details. For the user an application can either use the user `id` or the user's login email, which Box refers to as their `login`. # Notifications When creating a task an email notification is sent to the user who the task is assigned to. # Permissions Both the user assigning the task and the user the task is being assigned to needs to be a collaborator on the file. **Reference:** https://developer.box.com/guides/tasks/assignments/assign/ --- ## Untitled *Type: quick-start | Category: SSO & App users * Scaffold application code Our first step in this guide is to create a local application to house our code and configuration data as we start… # Scaffold application code Our first step in this guide is to create a local application to house our code and configuration data as we start to create the Okta and Box applications that will be needed. Depending on your language / framework preference, create a blank application, install the required dependencies, as well as all configuration and program files. Choose your preferred language / framework below to get started. # Node Using the Express.js framework. # Java Using the Spring Boot framework. # Python Using the Flask framework. # .NET Using the ASP.NET Core framework. - Create a local directory for your application. - Create a `package.json` file inside the local directory, open it in your preferred editor, copy / paste the following into it, and save / exit the file. ``` { "name": "okta-box", "version": "1.0.0", "description": "Box / Okta sample integration", "main": "server.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "node server.js" }, "author": "Box", "license": "ISC", "dependencies": { "@okta/oidc-middleware": "^4.0.0", "@okta/okta-sdk-nodejs": "^3.2.0", "box-node-sdk": "^1.31.0", "express-session": "^1.17.0" } } ``` - Run `npm init` from the terminal / console to install dependencies. - Create two files, `server.js` and `config.js` in the local directory. - Open `config.js` and save the following default configuration. ``` const oktaClientId = exports.oktaClientId = ''; const oktaClientSecret = exports.oktaClientSecret = ''; const oktaOrgUrl = exports.oktaOrgUrl = ''; const oktaBaseUrl = exports.oktaBaseUrl = 'http://localhost:3000'; const oktaRedirect = exports.oktaRedirect = '/authorization-code/callback'; ``` - From Eclipse, create a new project. When prompted, select a Gradle project. - Enter a unique name for the project, we used `okta.sample` for this guide. - Open your `build.gradle` file and add the following dependencies. Once saved, refresh the Gradle project. ``` dependencies { implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'com.okta.spring:okta-spring-boot-starter:1.4.0' testImplementation('org.springframework.boot:spring-boot-starter-test') { exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' } testImplementation 'org.springframework.security:spring-security-test' compile 'com.box:box-java-sdk:2.44.1' } ``` - Open your `/src/main/resources/application.properties` file and save the following defaults. ``` okta.oauth2.redirect-uri=/authorization-code/callback okta.oauth2.issuer= okta.oauth2.clientId= okta.oauth2.clientSecret= security.oauth2.sso.loginPath=/authorization-code/callback ``` - Create a local directory for your application. - Install needed dependencies using the following `pip` command from your terminal / command prompt: `pip install flask flask_oidc okta boxsdk config` - Create three files in the local directory, `client_secrets.json`, `config.py`, and `server.py`. - Open `config.py` and save the following into it. This will be some of Okta app configuration information needed. We'll fill in the remaining information in the next step. ``` okta_client_secret = 'YOUR OKTA CLIENT SECRET' okta_org_url = 'YOUR OKTA ORG URL' okta_auth_token = 'YOUR OKTA APP TOKEN' okta_callback_route = '/oidc/callback' ``` - Open `client_secrets.json` and save the following into it. This will be a standard object that the Flask OpenID Connect integration will use during configuration. We'll fill in the remaining information in the next step. ``` { "web": { "client_id": "OKTA CLIENT ID", "client_secret": "OKTA CLIENT SECRET", "auth_uri": "OKTA AUTHORIZE URI", "token_uri": "OKTA TOKEN URI", "issuer": "OKTA APP DEFAULT", "userinfo_uri": "OKTA APP USER INFO URI", "redirect_uris": [ "http://127.0.0.1:5000/oidc/callbac" ] } } ``` - Create a local directory for your application. - Open a command prompt / terminal window and go to the local application directory. Using the [.NET Core CLI](https://docs.microsoft.com/en-us/dotnet/core/tools/) type `dotnet new mvc` and hit enter. This will create the main scaffolding for a ASP.NET Core MVC (Model-View-Controller) web app. Alternately, create the application [directly from Visual Studio](https://docs.microsoft.com/en-us/visualstudio/ide/quickstart-aspnet-core). - From the command prompt / terminal window in the local application directory, add the Okta ASP.NET Core dependencies by typing `dotnet add package Okta.AspNetCore`and the Box dependencies by typing `dotnet add package Box.V2.Core`. - Load your new application into Visual Studio or your preferred editor. - Within the root of the project, open `Startup.cs`. - Add the following package declarations to the top of the file. ``` using Microsoft.AspNetCore.Authentication.Cookies; using Okta.AspNetCore; ``` - Replace the content of the `ConfigureServices` method with the following. We will fill in the specific Okta application values in the next step. ``` services.AddControllersWithViews(); services.AddAuthentication(options => { options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = OktaDefaults.MvcAuthenticationScheme; }) .AddCookie() .AddOktaMvc(new OktaMvcOptions { // Replace these values with your Okta configuration OktaDomain = "", ClientId = "", ClientSecret = "" }); ``` Add the following line to the **top** of the `Configure` method. ``` app.UseAuthentication(); ``` # Incomplete previous step Please select a preferred language / framework to get started. ## Summary - You created a new local application, files, and basic configuration details. - You installed all project dependencies. I have my local application set up **Reference:** https://developer.box.com/guides/sso-identities-and-app-users/connect-okta-to-app-users/scaffold-application-code/ --- ## Untitled *Type: guide | Category: Tasks * Change task assignment state To update a task assignment's state call the PUT /tasks/:task_id/assignments API and include a resolution_state… # Change task assignment state To update a task assignment's state call the [`PUT /tasks/:task_id/assignments`](e://put_task_assignments_id) API and include a `resolution_state`, such as `completed`, `incomplete`, `approved`, or `rejected`. ## Resolution states Box currently supports two types of tasks defined by the `action` value: `review` tasks and `complete` tasks. The type of task determines the possible resolution states a task can be in and the interface shown to a user in the web and mobile apps. | Task action | Possible resolution states | | --- | --- | | review | incomplete, approved, rejected | | complete | incomplete, complete | A `review` task starts out in an `incomplete` state and can be marked as `incomplete`, `approved`, or `rejected`. In the user interface a user is provided with a text box and an pair of buttons to approve or reject the task. A `complete` task starts out in an `incomplete` state and can be marked `incomplete` or `completed`. Once a this task is marked completed, no further changes can be made to the task's state. In the user interface a user is provided with a text box and an button to mark the task as completed. **Reference:** https://developer.box.com/guides/tasks/assignments/change-state/ --- ## Untitled *Type: guide | Category: Tasks * Get task assignment information To get information about an assigned task, call the GET /tasks/:task_id/assignments API with the id of the… # Get task assignment information To get information about an assigned task, call the [`GET /tasks/:task_id/assignments`](e://get_task_assignments_id) API with the `id` of the task assignment. **Reference:** https://developer.box.com/guides/tasks/assignments/get/ --- ## Untitled *Type: guide | Category: Tasks * Assigning tasks When a task is delegated to a user in Box it creates a task assignment. A task assignment is a request for that user to… # Assigning tasks When a task is delegated to a user in Box it creates a task assignment. A task assignment is a request for that user to complete the task. The way a task is resolved depends on the `action` type of a task and the `resolution_state` used to define how a task is resolved. Learn about types of tasks **Reference:** https://developer.box.com/guides/tasks/assignments/ --- ## Untitled *Type: guide | Category: Tasks * List a task's assignments To list all of the assignments for a specific tasks, call the GET /tasks/:task_id/assignments with the task id. # List a task's assignments To list all of the assignments for a specific tasks, call the [`GET /tasks/:task_id/assignments`](e://get_task_assignments_id) with the task `id`. **Reference:** https://developer.box.com/guides/tasks/assignments/list/ --- ## Untitled *Type: guide | Category: Tasks * Unassign a task To unassign a task, you will need to provide the DELETE /task_assignments API with the id of the task assignment… # Unassign a task To unassign a task, you will need to provide the [`DELETE /task_assignments`](e://delete_task_assignments_id) API with the `id` of the task assignment. # Permissions The user deleting the assignment needs to be a collaborator on the file. **Reference:** https://developer.box.com/guides/tasks/assignments/unassign/ --- ## Untitled *Type: guide | Category: Tasks * Change an assignment's message To update a task assignment's message call the PUT /tasks/:task_id/assignments API, and include the new… # Change an assignment's message To update a task assignment's message call the [`PUT /tasks/:task_id/assignments`](e://put_task_assignments_id) API, and include the new `message` for the task assignment. **Reference:** https://developer.box.com/guides/tasks/assignments/update-message/ --- ## Untitled *Type: guide | Category: Tooling * Retrieve an Access Token Every API call requires an Access Token to identify the authenticated user. For security purposes, Access Tokens… # Retrieve an Access Token Every API call requires an **Access Token** to identify the authenticated user. For security purposes, Access Tokens expire after 60 minutes. If you are using [OAuth 2.0](g://authentication/oauth2), use the provided [Refresh Token](g://authentication/tokens/refresh/) to obtain a new Access Token. If you are using server authentication, [JWT](g://authentication/jwt) or [Client Credentials Grant](g://authentication/client-credentials), make an API call to the [token endpoint](e://post-oauth2-token) to request a new Access Token. ## OAuth 2.0 If your application leverages [OAuth 2.0](g://authentication/oauth2) for authentication, you can follow the steps below to obtain a token pair via [Postman](g://tooling/postman/install). - The `grant_type` will always be `authorization_code`. - The `client_id` and `client_secret` values can be obtained from the **Configuration** tab for your application in the [Developer Console](https://app.box.com/developers/console). To obtain the value for `code`, build and visit your [authorization URL](g://authentication/oauth2/without-sdk) in your browser. Complete the OAuth 2.0 flow and, upon redirecting to your configured redirect URL, the authorization code will be at the end of the URL. As a reminder, this authorization code is only valid for 30 seconds. This means you must enter it to the designated field in Postman and click **Send** before it expires. Therefore, we recommend entering the other values so the API call is ready to send as soon as you get the code. **Reference:** https://developer.box.com/guides/tooling/postman/access-token/ --- ## Untitled *Type: guide | Category: Metadata * Get a metadata template Information for a metadata template can be retrieved using the template's name and scope, or the template's… # Get a metadata template Information for a metadata template can be retrieved using the template's name and scope, or the template's identifier. The authenticated user can only get Information about metadata templates [scoped](g://metadata/scopes) within the `global` scope or the `enterprise_:id` scope where `:id` is the ID of their enterprise. ## Get a metadata template by name To get a metadata template by name, call the [`GET /metadata_templates/:scope/:templateKey/schema`](e://get_metadata_templates_id_id_schema) API endpoint with the template's `scope` and `templateKey`. To get the `scope` and `templateKey` for a template, either [list all metadata templates](g://metadata/templates/list), or [list all instances on an item](g://metadata/instances/list). ## Get a metadata template by ID To get a metadata template by ID, you will need to pass the template's `id` to the [`GET /metadata_templates/:id`](e://get_metadata_templates_id) API endpoint. To get the `id` for a template, either [list all metadata templates](g://metadata/templates/list), or [list all instances on an item](g://metadata/instances/list). **Reference:** https://developer.box.com/guides/metadata/templates/get/ --- ## Untitled *Type: guide | Category: Tooling * Postman Collection Postman is a tool that lets you build and test HTTP requests in an easy-to-use interface without configuring a full… # Postman Collection [Postman](https://postman.com) is a tool that lets you build and test HTTP requests in an easy-to-use interface without configuring a full development environment. The **Box Postman Collection** is a set of preconfigured requests that make it possible to get started with the Box API without having to manually configure the requests. The simplest way to get started with Postman is with our Postman Quick Start guide. Get Started with the Box Postman Collection ## Latest Collection Clicking the following button will fork our latest Postman collection. It is a complete Postman collection that covers every API endpoint and is auto-generated from our [OpenAPI specification](https://github.com/box/box-openapi). It is expected to get many more updates including some to make authentication a lot easier. **Reference:** https://developer.box.com/guides/tooling/postman/ --- ## Untitled *Type: guide | Category: Metadata * Metadata templates A metadata template describes a set of key/value pairs that can be assigned to a file or folder. For example, an… # Metadata templates A metadata template describes a set of key/value pairs that can be assigned to a file or folder. For example, an `invoiceData` template might hold data about an invoice, having a field for the invoice ID as well as the customer ID. A file or folder can have multiple distinct template [instances](g://metadata/instances) associated with it, such as a `marketingCollateral` instance and a `retentionPolicy` instance. ## Metadata scopes Metadata templates are grouped into two distinct groups, or **scopes**. Learn more about Metadata Scopes ## Permissions and restrictions There is a limit of 500 templates per enterprise. Creating metadata templates is restricted to users with admin permission. This means that only admins, or co-admins who have been granted rights to Create and edit metadata templates for your company by the admin can use the web app or the API to manage templates. **Reference:** https://developer.box.com/guides/metadata/templates/ --- ## Untitled *Type: guide | Category: Tooling * Install Postman & Collection To use the Box Postman Collection you will need to have Postman installed on your device. Postman is available… # Install Postman & Collection To use the **Box Postman Collection** you will need to have [Postman](https://getpostman.com) installed on your device. Postman is available for Windows, Mac, and Linux environments. [Download the appropriate version for you operating system](https://www.postman.com/downloads/) Next, install Postman on your machine, [create an account, and sign in](https://identity.getpostman.com/signup). ## Loading a collection and environment With the Postman App installed the **Box Postman Collection** can be loaded into it. Clicking the button below you will load the Box Postman Collection into Postman. **Reference:** https://developer.box.com/guides/tooling/postman/install/ --- ## Untitled *Type: guide | Category: Tooling * Box for Agentforce Extension package The Box for Agentforce Extension package is an extension of the Box for Salesforce managed package… # Box for Agentforce Extension package The [Box for Agentforce Extension package](https://support.box.com/hc/en-us/articles/40370228349331-Installing-Box-for-Agentforce) is an extension of the Box for Salesforce managed package. This extension provides reusable Agentforce [actions](g://tooling/salesforce-toolkit/flow-actions) that help automate workflows and enhance intelligent agent—based processes within Salesforce. It builds on the core features of the Box for Salesforce package and uses global, invocable Apex methods to improve functionality. The Box for Agentforce Extension package references the methods by using the Box name space in Agentforce Actions. ## Methods in Agentforce Flows The list below contains example [methods](g://tooling/salesforce-toolkit/methods) that can be invoked in Agentforce. ### Folder & File Management - Create Folder - Create Folder Association - Create Folder for Record ID - Create Folder for Record ID from Template - Get Folder Contents by Folder ID - Get Folder ID by Record ID - Get Folder URL - Move Folder - Get Object Folder by Record ID - Get Record ID by Folder ID - Get URL for Folder ### Metadata Management - Create Box Metadata by File ID - Create Box Metadata by Folder ID - Delete Box Metadata by File ID - Delete Box Metadata by Folder ID - Update Box Metadata by Folder ID - Get Box Metadata by File ID - Get Box Metadata by Folder ID ### Collaboration - Create Collaboration - Create Collaboration on Record - Edit Collaboration - Delete Collaboration ### Additional Actions - Create File from Attachment - Get DocGen Batch - Get Folder Associations by Salesforce Record ID - Get Metadata Cascade Policies by Folder ID - Get Metadata Cascade Policy by ID - Create Metadata Cascade Policy - Delete Metadata Cascade Policy - Create Slack Channel Mapping - Set Slack Channel Access Management Disabled **Reference:** https://developer.box.com/guides/tooling/salesforce-toolkit/box-agentforce-package/ --- ## Untitled *Type: guide | Category: Tooling * Flow Actions Salesforce toolkit includes wrappers that allow admins to invoke the following methods. It allows Box for Salesforce users to… # Flow Actions Salesforce toolkit includes wrappers that allow admins to invoke the following [methods](g://tooling/salesforce-toolkit/methods). It allows Box for Salesforce users to build automated solutions, such as folder structure, using [Salesforce Flows](https://help.salesforce.com/s/articleView?id=sf.flow.htm&type=5). ## Methods in Salesforce Flows The list below contains all methods that can be invoked in [Salesforce Flows](https://help.salesforce.com/s/articleView?id=sf.flow.htm&type=5). - Create Box Metadata by Folder Id (`createBoxMetadataByFolderId`) - Create Collaboration (`createCollaboration`) - Create Collaboration On Record (`createCollaborationOnRecord`) - Create File From Attachment (`createFileFromAttachment`) - Create Folder (`createFolder`) - Create Folder Association (`createFolderAssociation`) - Create Folder For Record ID (`createFolderForRecordId`) - Create Folder For Record ID From Template (`createFolderForRecordIdFromTemplate`) - Create Metadata Cascade Policy (`createMetadataCascadePolicy`) - Create Object Folder For Record ID (`createObjectFolderForRecordId`) - Create Slack Channel Mapping (`mapSfdcRecordToSlackChannel`) - Delete Box Metadata by Folder Id (`deleteBoxMetadataByFolderId`) - Delete Collaboration (`deleteCollaboration`) - Delete Metadata Cascade Policy (`deleteMetadataCascadePolicyById`) - Edit Collaboration (`editCollaboration`) - Enable Integration Activity (`enableAppActivity`) - Get Box Metadata by Folder Id (`getBoxMetadataByFolderId`) - Get Folder Associations By Salesforce Record ID (`getFolderAssociationsByRecordId`) - Get Folder ID By Record ID (`getFolderIdByRecordId`) - Get Folder URL (`getFolderUrl`) - Get Metadata Cascade Policies by Folder Id (`getMetadataCascadePoliciesByFolderId`) - Get Metadata Cascade Policy by Id (`getMetadataCascadePolicyById`) - Get Object Folder By Record ID (`getObjectFolderByRecordId`) - Get Record ID By Folder ID (`getRecordIdByFolderId`) - Get Root Folder ID (`getRootFolderId`) - Get URL For Folder (`getUrlForFolder`) - Move Folder (`moveFolder`) - Set Slack Channel Access Management Disabled (`setSlackChannelAccessManagementDisabled`) - Update Box Metadata by Folder Id (`updateBoxMetadataByFolderId`) **Reference:** https://developer.box.com/guides/tooling/salesforce-toolkit/flow-actions/ --- ## Untitled *Type: guide | Category: Tooling * Refresh an Access Token To authenticate to the Box API the Postman collection will use an Access Token to identify you, the User to the API… # Refresh an Access Token To authenticate to the Box API the Postman collection will use an Access Token to identify you, the **User** to the API. Access tokens expire after 1 hour and therefore need to be refreshed every hour. ## Using a Refresh Token If you set up your own **Box App** in step 2 of the Postman Quick Start guide then your Box environment in Postman should include a valid `client_id` and `client_secret`. These client credentials and the `refresh_token` can be used to create a new value for the `access_token`. To refresh the access token, select the **Refresh access token** API call within the **Authorization** folder of the Postman collection. Next, click the **Send** button to request a new `access_token`. At the end of this API call, your environment should have a new `access_token` and `refresh_token` value, and you should be able to make any of the other API calls. Refresh tokens are only valid once and need to be used within 60 days. If used within 60 days, a new one is created together with a new access token and the 60 day period starts again. A refresh token expires if not used within 60 days, after which a new refresh token and access token need to be requested by going through the Quick Start guide again. ## Automatically refreshing an access token The Postman collection can automatically detect an expired `access_token` value and request a new one by using the `refresh_token`. By default this feature is enabled but it can be turned off by setting the `enable_auto_refresh_access_token` Postman environment variable to `false`. To set this value, click the edit button in the top right of your Box Postman environment variables. Find the row in the table for the `enable_auto_refresh_access_token` variable and set the **Current Value** to `true`. Next, click **Update** to save your changes. From now on, any time you make any API call the **Postman collection** will check if your `access_token` has expired and try to refresh it automatically before making your desired API call. ## Re-authenticating your Postman collection In some cases you might need to re-authenticate your Postman collection by going through the [Quick Start](g://tooling/postman/quick-start) guide again. A common reason for this is because you haven't used the Postman collection in over 60 days and your `refresh_token` has expired. To re-authenticate, first remove your old **Box Postman environment**. To do so, click the little **gear** icon in the top right and select your environment from the list. Select **Delete** to delete the environment. Then, restart the Postman Quick Start guide again from the start. When re-importing the **Box Postman Collection** for a second time the Postman app might ask you to import the new collection as a copy or replace the old one. We recommend importing it as a copy in order to preserve any custom configuration you might have made to any of the APIs. **Reference:** https://developer.box.com/guides/tooling/postman/refresh/ --- ## Untitled *Type: guide | Category: Tooling * Make an API call With the Box Postman Collection loaded into the Postman App it is possible to make API calls to the Box API on behalf of… # Make an API call With the **Box Postman Collection** loaded into the **Postman App** it is possible to make API calls to the **Box API** on behalf of the user logged in as. The Box Postman Collection will require a valid `access_token` environment variable to make API calls. To get your Postman App set up with a valid Access Token we recommend following our Quick Start guide. ## Make an API request To make an API request, select a **Request** from the Box Postman Collection. In this example, we will use the **List items in folder** API which can be found in the **Folders** folder. By default the `folder_id` for this API endpoint is set to `0` which represents every user's root folder. You can leave this value as is or set it to the folder ID of a folder you want to inspect. Next, click the **Send** button in the top right to make the API request. The API call should return quickly and show you a list of items in your folder in the response **Body** tab in the bottom half of the screen. # Authentication error At this point Postman might return an error instead of a list. Often, this means your **Access Token** has expired. Check our guide on refreshing an access token in Postman for more details. **Reference:** https://developer.box.com/guides/tooling/postman/make-api-call/ --- ## Untitled *Type: guide | Category: Tooling * Salesforce Developer Toolkit The Box for Salesforce Developer Toolkit allows customers to programmatically customize the behavior of the Box… # Salesforce Developer Toolkit The Box for Salesforce Developer Toolkit allows customers to programmatically customize the behavior of the Box for Salesforce integration. The Toolkit consists of several global APEX methods that can be used to trigger and extend the default behavior. The global methods can update the internal Salesforce record to Box folder mapping and handle permission management. This functionality is part of the latest Box for [Salesforce package](https://support.box.com/hc/en-us/articles/360044195713-Installing-and-Configuring-Box-For-Salesforce). # What the Toolkit does NOT provide The Toolkit is not a full-featured APEX wrapper for the BOX Content API. If this is what you are looking for, have a look at the [Box SDK for Salesforce](https://github.com/box/box-salesforce-sdk). ## Authentication A solution for authentication is to allow API calls to use the service account credentials. This means that Salesforce Admins need to restrict access to the Toolkit global APEX class. As these methods allow direct modification of Box content and collaborations, Salesforce administrators should take appropriate precautions by restricting access to the global Toolkit APEX class. Toolkit methods that take `accessToken` as a parameter can use the service account credentials by sending `null` for the `accessToken` value. If a value is passed in `accessToken`, the API call to Box is done with the access token sent. It is up to the developer to ensure the token being passed is valid and the user associated with the token has permissions to perform the requested operation. **Reference:** https://developer.box.com/guides/tooling/salesforce-toolkit/ --- ## Untitled *Type: guide | Category: Tooling * UI Elements in Salesforce The Box for Salesforce managed package offers the following UI Elements as Lightning Components: content picker… # UI Elements in Salesforce The Box for Salesforce managed package offers the following [UI Elements](g://embed/ui-elements) as Lightning Components: [content picker](g://embed/ui-elements/picker), [explorer](g://embed/ui-elements/explorer), [preview](g://embed/ui-elements/preview), and [uploader](g://embed/ui-elements/uploader). You can use them on Lightning Pages, or in Flows. ## Content picker [Options](g://embed/ui-elements/picker/#options) available in picker with Box for Salesforce are: - `folderId` - if the lightning component is on a record page, it defaults to the record folder - `chooseButtonLabel` - `cancelButtonLabel` - `canSetShareAccess` - `canCreateNewFolder` - `canUpload` - `maxSelectable` ## Content explorer [Options](g://embed/ui-elements/explorer/#options) available in explorer with Box for Salesforce are: - `folderId` - if the lightning component is on a record page, it defaults to the record folder - `canSetShareAccess` - `canCreateNewFolder` - `canUpload` - `canPreview` - `canDownload` - `canDelete` - `canRename` - `canShare` ## Content preview [Options](g://embed/ui-elements/preview/#options) available in preview with Box for Salesforce are: - `fileId` ([find your file ID](g://files/get)) - **File Id API Field Name** - Salesforce specific; on a record page, you can use an API field name that holds the file ID to be displayed ## Content uploader [Options](g://embed/ui-elements/uploader/#options) available in uploader with Box for Salesforce are: - `folderId` - if the lightning component is on a record page, it defaults to the record folder - `fileLimit` **Reference:** https://developer.box.com/guides/tooling/salesforce-toolkit/ui-elements/ --- ## Untitled *Type: guide | Category: Tooling * Code Samples Generic Method The following code will create metadata on a Box folder in Salesforce using the generic Toolkit method: New… # Code Samples ## Generic Method The following code will create metadata on a Box folder in Salesforce using the generic Toolkit method: ``` // Instantiate the Toolkit object box.Toolkit toolkit = new box.Toolkit(); // Get the Salesforce record id associated with a Box folder String recordId = toolkit.getRecordIdByFolderId('{some folder id}'); // Construct an object containing all the metadata you want Map<String, Object> metadata = new Map<String, Object>{ 'salesforce_id' => recordId, 'salesforce_url' => System.URL.getSalesforceBaseUrl().toExternalForm() + '/' + recordId, 'salesforce_user_name' => UserInfo.getName(), 'salesforce_user_email' => UserInfo.getUserEmail() }; // Specify the Box API endpoint to call String endpoint = 'https://api.box.com/2.0/folders/' + '{some folder id}' + '/metadata/global/properties'; // Create a new HttpRequest object and set appropriate values HttpRequest request = new HttpRequest(); request.setMethod('POST'); request.setEndpoint(endpoint); request.setBody(JSON.serialize(metadata)); request.setHeader('content-type', 'application/json'); // Send the HttpRequest through the generic Toolkit method, which will handle the authentication details HttpResponse response = toolkit.sendRequest(request); ``` ## New Folder Association The following code will create a folder for a specific Salesforce record Id: ``` // Instantiate the Toolkit object box.Toolkit boxToolkit = new box.Toolkit(); // Create a folder and associate it with an account Id accountId = '001j000000FBozw'; String accountFolderId = boxToolkit.createFolderForRecordId(accountId, null, true); system.debug('new item folder id: ' + accountFolderId); // If there was an error, accountFolderId will be null. mostRecentError will contain the error message if(accountFolderId == null) { system.debug('most recent error: ' + boxToolkit.mostRecentError); } // ALWAYS call this method when finished.Since salesforce doesn't allow http callouts after dml operations, we need to commit the pending database inserts/updates or we will lose the associations created boxToolkit.commitChanges(); ``` ## Folder Templates The following code will create a folder for a record, create subfolders (folder template), and collaborate the current user: ``` // Instantiate the Toolkit object box.Toolkit boxToolkit = new box.Toolkit(); // Create a folder and associate it with an account Id accountId = '001j000000FBozz'; String accountFolderId = boxToolkit.createFolderForRecordId(accountId, null, true); system.debug('new item folder id: ' + accountFolderId); // Create two sub-folders in the newly created account folder String legalFolderId = boxToolkit.createFolder('Legal Documents', accountFolderId, null); system.debug('Legal Folder id: ' + legalFolderId); String pictureFolderId = boxToolkit.createFolder('Pictures', accountFolderId, null); system.debug('Picture Folder id: ' + pictureFolderId); // Collaborate the current user on the account folder. Note that we're sending false for the optCreateFolder param that shouldn't actually matter since the folder(s) already exists Id userId = UserInfo.getUserId(); box.Toolkit.CollaborationType collabType = box.Toolkit.CollaborationType.EDITOR; String collabId = boxToolkit.createCollaborationOnRecord(userId, accountId, collabType, false); system.debug('new collaboration id: ' + collabId); // ALWAYS call this method when finished. Since salesforce doesn't allow http callouts after dml operations, we need to commit the pending database inserts/updates or we will lose the associations created boxToolkit.commitChanges(); ``` ## Metadata The following code will get, add, delete, and update Box folder metadata and cascade policies. ``` // Get metadata & attributes types Box.toolkit tk = new Box.Toolkit(); Box.MetadataTemplate mdt = tk.getMetadataTemplateByName('enterprise', 'testtemplate'); System.debug(mdt); System.debug(mdt.getAttributeTypes()); //get Map<String, String> of values and types. // Get Metadata Box.Toolkit tk = new Box.Toolkit(); Box.FolderMetadata fmd = tk.getBoxMetadataByFolderId('193488737189', 'enterprise', 'testTemplate'); System.debug(tk.mostRecentError); System.debug(fmd); // Create Metadata Box.KeyValuePair kvp = new Box.KeyValuePair(); kvp.key = 'recordName'; kvp.value = 'Account Test Name'; Box.KeyValuePair kvp2 = new Box.KeyValuePair(); kvp2.key = 'recordUrl'; kvp2.value = 'https://speed-nosoftware-3605-dev- ed.scratch.lightning.force.com/lightning/r/Account/001DR00001PsY7YYAV/view'; List<Box.KeyValuePair> kvps = new List<Box.KeyValuePair>(); kvps.add(kvp); kvps.add(kvp2); Box.Toolkit tk = new Box.Toolkit(); Box.FolderMetadata newfmd = tk.createBoxMetadataByFolderId('193488737189', 'enterprise', 'testTemplate', kvps); System.debug(tk.mostRecentError); System.debug(newfmd); // Update Metadata List<Box.FolderMetadataUpdate> updates = new List<Box.FolderMetadataUpdate>(); Box.FolderMetadataUpdate up1 = new Box.FolderMetadataUpdate(); Box.FolderMetadataUpdate up2 = new Box.FolderMetadataUpdate(); up1.op = 'replace'; up1.path = '/recordName'; up1.value = 'Account Name Test 2'; updates.add(up1); up2.op = 'replace'; up2.path = '/recordUrl'; up2.value = 'https://speed-nosoftware-2356-dev- ed.scratch.lightning.force.com/lightning/r/Account/001DR00001PsY7YYAV/view'; updates.add(up2); Box.Toolkit tk = new Box.Toolkit(); Box.FolderMetadata fmd = tk.UpdateBoxMetadataByFolderId('193488737189', 'global', 'lobSalesforceRecord', updates); System.debug(fmd); // Delete Metadata Box.Toolkit tk = new Box.Toolkit(); Boolean fmd = tk.deleteBoxMetadataByFolderId('193488737189', 'global', 'lobSalesforceRecord'); System.debug(tk.mostRecentError); System.debug(fmd); // Get Cascade Policy List Box.Toolkit tk = new Box.Toolkit(); List<Box.MetadataCascadePolicy> mcp = tk.getMetadataCascadePoliciesByFolderId('193488737189', null, 0, null); System.debug(mcp); System.debug(tk.mostRecentError); // Get Cascade Policy Box.Toolkit tk = new Box.Toolkit(); Box.MetadataCascadePolicy mcp = tk.getMetadataCascadePolicyById('MTkzNDg4NzM3MTg5I2cjbG9iU2FsZXNmb3JjZVJlY29yZC0wMTIwMTI0ZC03YWUxLTQzNjItYjdlMC05Y2RiYzhkMzIzZjM'); System.debug(mcp); System.debug(tk.mostRecentError); // Create Cascade Policy Box.Toolkit tk = new Box.Toolkit(); Box.MetadataCascadePolicy mcp = tk.createMetadataCascadePolicy('193488737189', 'global', 'lobSalesforceRecord'); System.debug(mcp); System.debug(tk.mostRecentError); // Delete Cascade Policy Box.Toolkit tk = new Box.Toolkit(); Boolean mcp = tk.deleteMetadataCascadePolicyById('MTkzNDg4NzM3MTg5I2cjbG9iU2FsZXNmb3JjZVJlY29yZC0wMTIwMTI0ZC03YWUxLTQzNjItYjdlMC05Y2RiYzhkMzIzZjM'); System.debug(mcp); System.debug(tk.mostRecentError); ``` More examples: ``` // Get metadata example 1 Box.toolkit tk = new Box.Toolkit(); Box.FolderMetadata fmt = tk.getBoxMetadataByFolderId('205776356105', 'enterprise', 'testTemplate'); for(KeyValuePair kvp : fmt.keyValuePairs){ System.debug(kvp); } // Get metadata example 2 Box.toolkit tk = new Box.Toolkit(); System.debug(tk.getBoxMetadataByFolderId('205776356105', 'global', 'lobSalesforceRecord')); ``` ``` // Create metadata Box.toolkit tk = new Box.Toolkit(); List<Box.KeyValuePair> kvps = new List<Box.KeyValuePair>(); Box.KeyValuePair kvp1 = new Box.KeyValuePair(); kvp1.key = 'name'; kvp1.value = 'test'; kvps.add(kvp1); Box.KeyValuePair kvp2 = new Box.KeyValuePair(); kvp2.key = 'revenue'; kvp2.value = '5000'; kvps.add(kvp2); Box.KeyValuePair kvp3 = new Box.KeyValuePair(); kvp3.key = 'typeMulti'; kvp3.value = 'Customer;Other'; kvps.add(kvp3); System.debug(tk.createBoxMetadataByFolderId('205776356105', 'enterprise', 'testtemplate', kvps)); System.debug(tk.mostRecentError); ``` ``` // Update Metadata Box.toolkit tk = new Box.Toolkit(); System.debug(tk.getBoxMetadataByFolderId('205776356105', 'enterprise', 'mitchtemplate')); List<Box.FolderMetadataUpdate> fmus = new List<Box.FolderMetadataUpdate>(); Box.FolderMetadataUpdate fmu = new Box.FolderMetadataUpdate(); fmu.op = 'replace'; fmu.path = '/name'; fmu.value = 'Test 2'; fmus.add(fmu); Box.FolderMetadataUpdate fmu2 = new Box.FolderMetadataUpdate(); fmu2.op = 'replace'; fmu2.path = '/revenue'; fmu2.value = '3000'; fmus.add(fmu2); Box.FolderMetadataUpdate fmu3 = new Box.FolderMetadataUpdate(); fmu3.op = 'add'; fmu3.path = '/typeMulti'; fmu3.value = 'Customer'; fmus.add(fmu3); System.debug(tk.updateBoxMetadataByFolderId('205776356105', 'enterprise', 'testTemplate', fmus)); System.debug(tk.mostRecentError); ``` **Reference:** https://developer.box.com/guides/tooling/salesforce-toolkit/samples/ --- ## Untitled *Type: guide | Category: Uploads * Commit Upload Session The final step in a chunked upload is to commit the session. To commit a file upload session, call the POST /files… # Commit Upload Session The final step in a chunked upload is to commit the session. To commit a file upload session, call the [`POST /files/upload_sessions/:id/commit`](e://post_files_upload_sessions_id_commit) with a list of uploaded parts to commit. Additionally, any file `attributes` can be passed along with the `parts` to further add information to the file. See the [`POST /files/content`](e://post_files_content) API for more details. ## Response When successful, the API returns a HTTP `201 Created` status code with a [`File`](r://file) object. In some cases, creating the parts might not be ready yet and the API will return a `202 Accepted` status code instead. In this case the application should check the `retry-after` header and retry committing after the number of seconds specified. **Reference:** https://developer.box.com/guides/uploads/chunked/commit-session/ --- ## Untitled *Type: guide | Category: Uploads * Chunked Uploads The Chunked Upload API provides a way to reliably upload large files to Box by chunking them into a sequence of parts that… # Chunked Uploads The Chunked Upload API provides a way to reliably upload large files to Box by chunking them into a sequence of parts that can be uploaded individually. By using this API the application uploads a file in part, allowing it to recover from a failed request more reliably. It means an application only needs to retry the upload of a single part instead of the entire file. An additional benefit of chunked uploads is that parts can be uploaded in parallel, allowing for a potential performance improvement. ## Overview Chunked uploads require a sequence of API calls to be made. 1. **[Create an upload session](g://uploads/chunked/create-session)**: The application creates an upload session for a new file or new version of a file. The session defines the (new) name of the file, its size, and the parent folder. 2. **[Upload parts](g://uploads/chunked/upload-part)**: The application uploads the separate parts of the file as chunks. 3. **[Commit session](g://uploads/chunked/commit-session)**: The application commits the session, at which moment the integrity of the file is checked before it is placed in the location specified when the session was created. Most of [the Box SDKs support chunked uploads](g://uploads/chunked/with-sdks) out of the Box, removing the complexity from the application code. ## Restrictions The Chunked Upload API is intended for large files with a minimum size of 20MB. The API does not support uploads of files with a size smaller than this. This API does not support re-uploading or overwriting of parts in a session. Once a part has been uploaded, it is immutable. The lifetime of an upload session is 7 days. During this time, the client can upload parts at their own pace. To avoid wasting resources, and avoid potential data corruption, client should make sure that the underlying file has not been changed on disk since beginning the upload. **Reference:** https://developer.box.com/guides/uploads/chunked/ --- ## Untitled *Type: guide | Category: Uploads * Create Upload Session To create an upload session, call the POST /files/upload_sessions API with the desired file_name and folder_id to put… # Create Upload Session To create an upload session, call the [`POST /files/upload_sessions`](e://post_files_upload_sessions) API with the desired `file_name` and `folder_id` to put the file in, as well as the `file_size` of the file to be uploaded. To create a session for a new version of an existing file, call the [`POST /files/:id/upload_sessions`](e://post_files_id_upload_sessions) API instead. In this case, the `file_name` and `folder_id` are only required when renaming or moving the file in the process. ## Pre-flight Check Creating an upload session also performs a [preflight check](g://uploads/check), making it unnecessary to do so separately when working with chunked uploads. ## Response When a session is created successfully the response includes an Upload Session that includes a session ID, the number of parts, the part sizes, as well as links to the relevant next API endpoints to use. ``` { "id": "F971964745A5CD0C001BBE4E58196BFD", "type": "upload_session", "session_expires_at": "2012-12-12T10:53:43-08:00", "part_size": 1024, "total_parts": 1000, "num_parts_processed": 455, "session_endpoints": { "upload_part": "https://upload.box.com/api/2.0/files/upload_sessions/F971964745A5CD0C001BBE4E58196BFD", "commit": "https://upload.box.com/api/2.0/files/upload_sessions/F971964745A5CD0C001BBE4E58196BFD/commit", "abort": "https://upload.box.com/api/2.0/files/upload_sessions/F971964745A5CD0C001BBE4E58196BFD", "list_parts": "https://upload.box.com/api/2.0/files/upload_sessions/F971964745A5CD0C001BBE4E58196BFD/parts", "status": "https://upload.box.com/api/2.0/files/upload_sessions/F971964745A5CD0C001BBE4E58196BFD", "log_event": "https://upload.box.com/api/2.0/files/upload_sessions/F971964745A5CD0C001BBE4E58196BFD/log" } } ``` The upload session defines the size of the parts to use when uploading the individual parts. **Reference:** https://developer.box.com/guides/uploads/chunked/create-session/ --- ## Untitled *Type: guide | Category: Tooling * Methods and Operations Toolkit Details Class Name: box.Toolkit Instance Variables mostRecentError String to indicate the most recent error… # Methods and Operations ## Toolkit Details Class Name: `box.Toolkit` ## Instance Variables ### mostRecentError String to indicate the most recent error that occurred when calling instance methods. The presence of this string does not mean the operation did not succeed. It is possible the error was recoverable; however, the lack of a value in this string does indicate the operation was successful. ### Enum CollaborationType Enum to indicate the [type of collaboration](https://support.box.com/hc/en-us/articles/360044196413-Understanding-Collaborator-Permission-Levels). Possible values: `EDITOR`, `VIEWER`, `PREVIEWER`, `UPLOADER`, `COOWNER`, `OWNER`, `PREVIEWERUPLOADER`, or `VIEWERUPLOADER` ## Static Methods ### deleteServiceUserAssociation Method to clear association between Service Account and Box for Salesforce integration. This can be used to change the service account if an incorrect one is being used. Parameters: - None Returns: - `true` if the user's account existed and was deleted. - `false` if the user's account was not deleted for any reason (including because it didn't exist). ### deleteUserAssociation | Parameter | Type | Description | | --- | --- | --- | | userId | id | The Id of the user whose credentials are to be cleared. | Returns: - `true` if the user's account existed and was deleted. - `false` if the user's account was not deleted for any reason (including because it didn't exist). ## Instance Methods - Constructors, Destructors ### box.Toolkit() Parameters: - None ### commitChanges Treat this method as a destructor for the `box.Toolkit()` method. This method is critical. It must be called after all folder / collaboration operations are complete. Every time. No Exceptions. Since Salesforce doesn't allow callouts after database updates / inserts / deletes have occurred, the Toolkit class maintains some collections of objects to be inserted once all callout operations are complete. If this method is not called, those objects won't be in the database, and the tables that keep track of user / record / folder associations will be out of sync and will need some advanced debugging to fix. Parameters: - None Returns: - `Void` ### commitChanges with platform event Treat this method as a destructor for the `box.Toolkit()` method. This method is very similar to the `commitChanges` above. However, it uses a platform event to commit changes to the database to perform DML statements in a different transaction and avoid governor limits in some scenarios. | Parameter | Type | Description | | --- | --- | --- | | usePlatformEvent | boolean | true if you're using a platform event. false to call the original method. | Returns: - `Void` ## Generic Methods The Box for Salesforce Developer Toolkit provides a global method that accepts an [HttpRequest](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_restful_http_httprequest.htm) object as a parameter and returns an [HttpResponse](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_restful_http_httpresponse.htm#apex_classes_restful_http_httpresponse) object. This method will utilize the authentication details of the Service Account to make callouts to Box's APIs, allowing you to focus on the business logic of your integration. ### sendRequest | Parameter | Type | Description | | --- | --- | --- | | request | HttpRequest | An HttpRequest object with a set endpoint and method. | Returns: - An [HttpResponse](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_restful_http_httpresponse.htm#apex_classes_restful_http_httpresponse) object with the response details from calling Box's APIs. - `Toolkit.BoxApiException` if there is any missing information from the HttpRequest input. - `null` if there was an issue getting the authentication details for the Service Account. In this case, check `mostRecentError`. ## File Operations ### createFileFromAttachment Available in version 3.46 and above. Salesforce has a String length limit of 6 million characters. Due to string bloat in the base64 encoding/decoding process, this results in an effective file size limit of 4.3 megabytes for synchronous Apex and 8.6 megabytes for asynchronous Apex. | Parameter | Type | Description | | --- | --- | --- | | att | Attachment | The attachment to be converted into a File in Box. | | fileNameOverride | string | Optional - Name of the new file. If no value is passed in, the name of the attachment is used. | | folderIdOverride | string | Optional - Box folder id to place this attachment in. If no value is passed in, the file will be placed in the folder associated with the record that is the parentId of the attachment. If the record-specific folder doesn't exist, it will be created. | | accessToken | string | Optional - if accessToken is sent, that value is used for the Box API call. Otherwise, the default account credentials are used. | Returns: - `string` returned is the id of the Box file that was created. - `null` if there was an error. In this case, check `mostRecentError`. ### getObjectFolderByRecordId | Parameter | Type | Description | | --- | --- | --- | | recordId | id | Salesforce record id whose root folder id you want to get. | Returns: - `string` returned is the Box folder id of the object root folder for the record id passed in. ## Folder Operations ### getRootFolderId Parameters: - None Returns: - `string` returned is the Box folder id of the Salesforce root folder. ### getObjectFolderByRecordId | Parameter | Type | Description | | --- | --- | --- | | recordId | id | Salesforce record id whose root folder id you want to get. | Returns: - `string` returned is the Box folder id of the object root folder for the record id passed in. ### getFolderUrl - This method gets the embed widget URL for a particular record so customers can use their own embed logic if desired. - This method respects seamless login settings so the URL will automatically log the user in if seamless login is enabled. | Parameter | Type | Description | | --- | --- | --- | | recordId | id | Salesforce record id whose root folder id you want to get. | | isMobileContext | boolean | Boolean to indicate whether the URL should be mobile (true) or not (false). | Returns: - `string` returned is a URL that points to the folder associated with the Salesforce record Id passed. This URL is for the Box Embed Widget and can be embedded in any Visualforce page. ### createObjectFolderForRecordId | Parameter | Type | Description | | --- | --- | --- | | recordId | id | Salesforce record id whose root folder id you want to get. | Returns: - `string` returned is the Box folder Id of the root folder that was created. - If the root folder already existed, the value returned is the Box folder id of the root folder that already existed. ### createFolder | Parameter | Type | Description | | --- | --- | --- | | folderName | string | Name of the folder to be created. Folder names are subject to some restrictions. See here for more details. | | parentFolderId | string | Parent Box folder this folder will be created in. | | accessToken | string | Optional - If accessToken is sent, that value is used for the Box API call,; otherwise, the default service account credentials are used. | Returns: - `string` returned is the Box folder id of the folder that was created. - `null` is returned if a folder is not created. In this case, check `mostRecentError` for details. ### createFolderForRecordId | Parameter | Type | Description | | --- | --- | --- | | recordId | id | Salesforce record id that a Box folder will be created for. | | folderNameOverride | string | By default, the record's name will be the folder name. If you want to name it something else, send that value here. | | optCreateRootFolder | boolean | Boolean to indicate whether to create the object root folder if it doesn't exist. If false is sent and the root folder does not exist, the call will fail. | Returns: - `string` returned is the Box folder id of the folder that was created. - `null` is returned if a folder is not created. In this case, check `mostRecentError` for details. - If the Salesforce record was already associated with a Box folder, the existing Box folder id is returned. ### moveFolder | Parameter | Type | Description | | --- | --- | --- | | folderId | string | Box folder Id of the folder to be moved. | | newParentFolderId | string | Box folder Id of the folder that will be the new parent folder. | | accessToken | string | Optional - If accessToken is sent, that value is used for the Box API call. Otherwise, the default service account credentials are used. | Returns: - `true` if the folder was moved successfully. - `false` if the folder was not moved successfully. Check `mostRecentError` for details. ### getUrlForFolder | Parameter | Type | Description | | --- | --- | --- | | recordId | id | ID of the record. | Returns: - `pageReference` object with provided URL. - `null` if the parameters are incorrect. ### createFolderForRecordIdFromTemplate | Parameter | Type | Description | | --- | --- | --- | | recordId | id | Salesforce record ID. | | templateFolderId | string | Source folder which should be the template. | | folderNameOverride | string | Name override of the new folder. | | optCreateRootFolder | boolean | Flag that determines if a root folder is created if it doesn't exist. | Returns: - Newly created `folder Id`. - `null` if the parameters are incorrect. ## Folder Association Methods ### getFolderAssociationsByRecordId | Parameter | Type | Description | | --- | --- | --- | | recordId | id | Salesforce record id that the folder mapping entries returned are related to. | Returns: - List returned is a collection of all folder mapping entries associated with this record. - Generally, it will be an empty list if no folder mapping entries exist, but under some circumstances, it could be `null`. ### getFolderIdByRecordId | Parameter | Type | Description | | --- | --- | --- | | recordId | id | Salesforce record id whose folder id you wish to get. | Returns: - `string` returned is the Box folder id associated with the Salesforce record id passed in. ### getRecordIdByFolderId | Parameter | Type | Description | | --- | --- | --- | | folderId | string | Box folder id. | Returns: - `id` returned is the Salesforce record id associated with the Box folder id passed in. ### createFolderAssociation | Parameter | Type | Description | | --- | --- | --- | | recordId | id | Salesforce record Id that is being associated with a box folder. | | folderId | string | Box folder Id being associated with a Salesforce record. | Returns: - `box__FRUP__c` object - The FRUP object returned will be `null` if there was an error (check `mostRecentError`). Upon calling the `commitChanges` method, this FRUP entry will be inserted into the database. This method ensures consistency with other folder associations by not allowing the same folder to be associated with multiple records or vice versa. ## Collaboration Methods Collaborations created by the Box for Salesforce Developer Toolkit will not send collaboration emails to collaborators. Only the service account used for the Box for Salesforce integration will receive a collaboration email. ### createCollaboration | Parameter | Type | Description | | --- | --- | --- | | folderId | string | Box folder id to create a collaboration on. | | boxUserId | string | Box user id to be collaborated (either boxUserId or emailAddress is required but not both). | | emailAddress | box.Toolkit.CollaborationType | Email address of the box user to be. | | collabType | string | Type of collaboration (see the CollaborationType enum definition). | | accessToken | string | Optional - If sent, this value is used for authentication for the box API call; if null, the service account credentials are used. | Returns: - `string` returned is the id of the box collaboration that was created. - `null` returned if there was an error. In this case, check `mostRecentError`. ### createCollaborationOnRecord | Parameter | Type | Description | | --- | --- | --- | | userId | id | Salesforce user id to be collaborated. | | recordId | id | Salesforce record id of the record folder to be collaborated on. | | collabType | box.Toolkit.CollaborationType | Type of collaboration (see the CollaborationType enum definition). | | optCreateFolder | boolean | Boolean to indicate whether to create the Box folder associated for the Salesforce record id if it does not already exist. This also creates the root folder if it did not already exist. If set to false and the folder does not already exist, the call will fail. | Returns: - `string` returned is the id of the Box collaboration that was created. - `null` returned if there was an error. In this case, check `mostRecentError`. ### editCollaboration | Parameter | Type | Description | | --- | --- | --- | | collabId | string | Collaboration ID | | collabType | enum | Box.Toolkit.CollaborationType enum | | accessToken | string | | Returns: - Boolean based on the transaction success. - `false` if the parameters are incorrect. ### deleteCollaboration | Parameter | Type | Description | | --- | --- | --- | | collabId | string | Collaboration ID | | accessToken | string | | Returns: - Boolean based on the transaction success. - `false` if the parameters are incorrect. ## Metadata Check the `toolkit.mostRecentError` value for detailed error responses for all the methods. ### getBoxMetadataByFolderId This method calls the [get metadata instance on folder endpoint](e://get-folders-id-metadata-id-id). | Parameter | Type | Description | | --- | --- | --- | | folderId | string | The ID of the Box Folder for which you want to create metadata. | | scope | string | The scope of the metadata template. Value is one of [global, enterprise]. | | template_key | string | The name of the metadata template. | Returns: - `FolderMetadata` record associated with this folder, scope, and template key. You can find the custom values in `keyValuePairs` variable of this object. `null`, if: - the parameters are incorrect, - access to the folder is missing, - metadata cascade policy is not found. ### createBoxMetadataByFolderId This method calls the [create metadata instance on folder](e://post-folders-id-metadata-id-id) endpoint. | Parameter | Type | Description | | --- | --- | --- | | folderId | string | ID of the Box Folder for which you want to create metadata. | | scope | string | Scope of the metadata template. Value is one of [global, enterprise]. | | template_key | string | Name of the metadata template. | | keyValuePairs | List<KeyValuePair> | This class work as a map. Provide key/value pairs as a list, for the attributes to send to Box Metadata. The key/value mappings follow the same pattern as the API. Number types '3000'and multi select values such as 'Customer;Order'are represented as string inputs in the value field, as regular metadata values seen in the code samples. | Returns: - Newly created `FolderMetadata` object. `null`, if: - the parameters are incorrect, - access to the folder is missing, - metadata cascade policy is not found. ### updateBoxMetadataByFolderId Calls the [update metadata instance on folder](e://put-folders-id-metadata-id-id) endpoint. | Parameter | Type | Description | | --- | --- | --- | | folderId | string | ID of the Box folder for which you want to update metadata. | | scope | string | The scope of the metadata template. Value is one of [global, enterprise] | | template_key | string | The name of the metadata template. | | mdUpdates | List<FolderMetadataUpdate> | Metadata updates. Provide the operation, path, and value. The metadata update records follow the same pattern as the API. Number types (3000) and multi select values such as Customer;Order are represented as string inputs in the value field, just as the regular metadata values in the code samples. | Returns: - Updated `FolderMetadata` object. `null`, if: - the parameters are incorrect, - access to the folder is missing, - metadata cascade policy is not found. ### deleteBoxMetadataFolderId This method call the [delete metadata instance from folder](e://delete-folders-id-metadata-id-id) endpoint. | Parameter | Type | Description | | --- | --- | --- | | folderId | string | ID of the Box folder for which you want to update metadata. | | scope | string | The scope of the metadata template. Value is one of [global, enterprise] | | template_key | string | The name of the metadata template. | Returns: - Boolean based on the transaction success. - `false` can be returned if parameters are incorrect parameters or metadata is not found. ### getMetadataCascadePolicyById This method calls the [get metadata cascade policy from folder](e://get-metadata-cascade-policies-id) endpoint. As it requires an ID, you need to call the `getMetadataCascadePoliciesByFolderId` method first. | Parameter | Type | Description | | --- | --- | --- | | policyId | string | The ID of the cascade policy you want to retrieve. | Returns: - `MetadataCascadePolicy` object retrieved from Box. `null`, if: - the parameters are incorrect, - access to the folder is missing, - metadata cascade policy is not found. ### getMetadataCascadePoliciesByFolderId This method retrieves the cascade policies by providing a folder ID and calling the [get metadata cascade policies](e://get-metadata-cascade-policies) endpoint. | Parameter | Type | Description | Required | | --- | --- | --- | --- | | folderId | string | Specifies for which folder to return the policies. This can't be used on the root folder with ID 0. | Yes | | paginationMarker | string | Defines the position marker at which to begin returning results. This is used for marker-based pagination. | No | | Offset | integer | The offset of the item at which to begin the response. | No | | ownerEnterpriseId | string | Enterprise ID for which to find the metadata cascade policies. If not specified, it defaults to the current enterprise. | No | Returns: - List of `MetadataCascadePolicy` objects retrieved from Box. `null`, if: - the parameters are incorrect, - access to the folder is missing, - metadata cascade policy is not found. ### createMetadataCascadePolicy This method creates cascade policies by providing a Box folder ID, scope, template key, and by calling the [post metadata cascade policies](e://post-metadata-cascade-policies) endpoint. | Parameter | Type | Description | | --- | --- | --- | | folderId | string | ID of the Box folder for which you want to create the metadata cascade policy. | | scope | string | The scope of the metadata cascade policy. Value is one of [global, enterprise] | | template_key | string | The name of the template key. | Returns: - Newly generated `MetadataCascadePolicy`. `null`, if: - the parameters are incorrect, - access to the folder is missing, - metadata cascade policy details are not found. ### deleteMetadataCascadePolicy This method deletes the cascade policies by providing a cascade policy ID and calling the [delete metadata cascade policies ID](e://delete-metadata-cascade-policies-id) endpoint. | Parameter | Type | Description | | --- | --- | --- | | policyId | string | The ID of the cascade policy you want to delete. | Returns: - Boolean based on the transaction success. - `false` is returned if parameters are incorrect, access to the folder is missing, or the metadata cascade policy is not found. ### enableAppActivity This method enables the given folder for App Activities by applying metadata on the folder and cascading it down. | Parameter | Type | Description | | --- | --- | --- | | folderId | string | The ID of the Box folder for which you want to delete metadata. | Returns: - Boolean based on the transaction success. - `false` in case of incorrect parameters. ## Salesforce and Slack ### getIntegrationMappings This toolkit method calls the [get integration mappings](e://get-integration-mappings-slack) endpoint to get the existing mappings. | Parameter | Type | Description | | --- | --- | --- | | integration | String | Slack is currently the only supported value. | | partnerItemId | String | ID of the mapped item on the provided integration side. Example: a Slack channel ID. | Returns: - A list of `IntegrationMapping` objects, - `null` is returned if there are incorrect parameters, the access is missing, or the integration mappings is not found. ### createIntegrationMapping This toolkit method calls the [get integration mappings](e://get-integration-mappings-slack) endpoint to create the mappings. When you map to a Slack channel, `access_management_disabled` is set to `FALSE` by default. This causes an automatic removal of collaborators that are not part of the Slack channel member list. Depending on how your organization sets up sharing in Box, we recommend to either enable `access_ management_disabled` to `TRUE` by using the `setSlackChannelAccessManagementDisabled` method, or use [groups](https://support.box.com/hc/articles/360043694554-Creating-and-Managing-Groups). This ensures no users are removed, regardless of Slack settings. Collaborations are added or removed from Slack when a file is uploaded to a Slack channel. | Parameter | Type | Description | | --- | --- | --- | | integration | String | Slack is currently the only supported value. | | mapping | IntegrationMapping | Apex defined type IntegrationMapping. | Returns: - Boolean based on the transaction success. ### deleteIntegrationMapping This toolkit method calls the [delete integration mappings](e://delete-integration-mappings-slack-id) endpoint to delete a mapping. | Parameter | Type | Description | | --- | --- | --- | | integration | String | Slack is currently the only supported value. | | integrationMappingId | String | Retrieved from getIntegrationMappings. | Returns: - Boolean based on the transaction success. ### mapSfdcRecordToSlackChannel This toolkit method uses the above integration mapping methods and provides a wrapper with four different use cases: 1. If a mapping does not exist in Salesforce or Slack, it creates a folder under the Box for Salesforce folder structure, and an integration mapping to link it with the Slack channel. 2. If a mapping only exists from Salesforce, it continues to use the folder and does not change the location. Creates an integration mapping to link it with the Slack Channel. 3. If a mapping only exists from Slack, it continues to use the folder and create an FRUP record for the Salesforce record to use the existing folder. This folder is likely to be outside of the Salesforce root folder. 4. If Salesforce and Slack have existing mappings but are not related to each other, it throws an error through `Toolkit.mostRecentError` or within a flow action, stating that the mappings already exist. This method/invocable is used in a flow template provided in the Box for Salesforce package `Create Box Folder/Slack Channel Mapping`. When you map to a Slack channel, `access_management_disabled` is set to `FALSE` by default. This causes an automatic removal of collaborators that are not part of the Slack channel member list. Depending on how your organization sets up sharing in Box, we recommend to either enable `access_ management_disabled` to `TRUE` by using the `setSlackChannelAccessManagementDisabled` method, or use [groups](https://support.box.com/hc/articles/360043694554-Creating-and-Managing-Groups). This ensures no users are removed, regardless of Slack settings. Collaborations are added or removed from Slack when a file is uploaded to a Slack channel. | Parameter | Type | Description | | --- | --- | --- | | recordId | ID | Salesforce record ID. | | slackChanneld | String | | | slackWorkspaceOrOrgId | String | If Box for Slack is installed org-wide, provide the Org ID (for example E1234567), or the Workspace ID (e.g. T5555555). | Returns: - Boolean based on the transaction success. ### setSlackChannelAccessManagementDisabled This toolkit method calls the [put integration mappings](e://put-integration-mappings-slack-id) endpoint to update the access management deactivated setting. This method/invocable is used in a flow template provided in the Box for Salesforce package `Create Box Folder/Slack Channel Mapping`. | Parameter | Type | Description | | --- | --- | --- | | channelId | String | | | disabled | Boolean | Indicates whether or not a channel member access to the underlying Box item should be automatically managed. Depending on the type of the channel, access is managed through creating collaborations or shared links. | Returns: - Boolean based on the transaction success. **Reference:** https://developer.box.com/guides/tooling/salesforce-toolkit/methods/ --- ## Untitled *Type: guide | Category: Uploads * Upload All Files in Folder Sometimes an application might want to upload all files from a folder. To do so with the SDKs and the CLI… # Upload All Files in Folder Sometimes an application might want to upload all files from a folder. To do so with the SDKs and the CLI requires traversing the folder tree, finding every file and uploading it accordingly. ``` using System; using System.Collections.Generic; using System.Diagnostics; using System.Dynamic; using System.IO; using System.Linq; using System.Net; using System.Net.Sockets; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using Box.V2; using Box.V2.Auth; using Box.V2.Config; using Box.V2.Converter; using Box.V2.Exceptions; using Box.V2.JWTAuth; using Box.V2.Models; using Newtonsoft.Json; namespace BoxPlayground { public class Program { static void Main (string[] args) { ExecuteMainAsync ().Wait (); } const long CHUNKED_UPLOAD_MINIMUM = 200000; private static async Task ExecuteMainAsync () { var directoryName = "dotnetUploadFolder"; var parentFolderId = "0"; var files = Directory.EnumerateFiles (directoryName); using (FileStream fs = new FileStream ("./config.json", FileMode.Open)) { var session = new BoxJWTAuth (BoxConfig.CreateFromJsonFile (fs)); var client = session.AdminClient (session.AdminToken ()); var folderId = ""; try { var createdFolder = await client.FoldersManager.CreateAsync ( new BoxFolderRequest { Parent = new BoxRequestEntity { Id = parentFolderId }, Name = directoryName }); folderId = createdFolder.Id; } catch (BoxConflictException<BoxFolder> e) { folderId = e.ConflictingItems.FirstOrDefault ().Id; System.Console.WriteLine ($"Found existing folder: {folderId}"); } var fileUploadTasks = new List<Task<BoxFile>> (); foreach (var file in files) { fileUploadTasks.Add (Task.Run ( async () => { System.Console.WriteLine (file); var fileName = file.Split (Path.DirectorySeparatorChar) .Where ((item) => { return item != directoryName; }).FirstOrDefault (); System.Console.WriteLine (fileName); var fileInfo = new FileInfo (file); var preflightRequest = new BoxPreflightCheckRequest { Name = fileName, Size = fileInfo.Length, Parent = new BoxRequestEntity { Id = folderId } }; using (FileStream toUpload = new FileStream (file, FileMode.Open)) { try { var preflightCheck = await client.FilesManager.PreflightCheck (preflightRequest); if (toUpload.Length < CHUNKED_UPLOAD_MINIMUM) { using (SHA1 sha1 = SHA1.Create ()) { var fileUploadRequest = new BoxFileRequest { Name = fileName, Parent = new BoxRequestEntity { Id = folderId } }; var fileSHA = sha1.ComputeHash (toUpload); System.Console.WriteLine (fileSHA); return await client.FilesManager.UploadAsync (fileRequest: fileUploadRequest, stream: toUpload, contentMD5: fileSHA); } } else { return await client.FilesManager.UploadUsingSessionAsync (stream: toUpload, fileName: fileName, folderId: folderId); } } catch (BoxPreflightCheckConflictException<BoxFile> e) { if (toUpload.Length < CHUNKED_UPLOAD_MINIMUM) { using (SHA1 sha1 = SHA1.Create ()) { var fileSHA = sha1.ComputeHash (toUpload); return await client.FilesManager.UploadNewVersionAsync (fileName: e.ConflictingItem.Name, fileId: e.ConflictingItem.Id, stream: toUpload, contentMD5: fileSHA); } } else { await client.FilesManager.UploadFileVersionUsingSessionAsync (fileId: e.ConflictingItem.Id, stream: toUpload); return await client.FilesManager.GetInformationAsync (e.ConflictingItem.Id); } } } })); } var uploaded = await Task.WhenAll (fileUploadTasks); foreach (var file in uploaded) { System.Console.WriteLine (file.Id); } } } } } ``` ``` public class UploadAllFilesInFolder { public static final int CHUNKED_UPLOAD_MINIMUM = 20000; public static void main(String[] args) throws Exception { String directoryName = "javaUploadFolder"; Path configPath = Paths.get("config.json"); Path uploadFolderPath = Paths.get(directoryName); try (BufferedReader reader = Files.newBufferedReader(configPath, Charset.forName("UTF-8"))) { BoxConfig boxConfig = BoxConfig.readFrom(reader); BoxDeveloperEditionAPIConnection client = BoxDeveloperEditionAPIConnection.getAppEnterpriseConnection(boxConfig); String parentFolderId = "0"; String createdFolderId; BoxFolder createFolderInParentFolder = new BoxFolder(client, parentFolderId); try { BoxFolder.Info createdFolder = createFolderInParentFolder.createFolder(directoryName); System.out.println("Creating folder..."); System.out.println(createdFolder.getID()); createdFolderId = createdFolder.getID(); } catch (BoxAPIException e) { String existingFolderId = getIdFromConflict(e.getMessage()); System.out.println("Found existing folder..."); System.out.println(existingFolderId); createdFolderId = existingFolderId; } ArrayList < BoxFile.Info > uploadedFiles = new ArrayList < > (); try (DirectoryStream < Path > directory = Files.newDirectoryStream(uploadFolderPath)) { for (Path path: directory) { String fileName = path.getFileName().toString(); System.out.println(path); System.out.println(fileName); byte[] fileBytes = Files.readAllBytes(path); int fileSize = fileBytes.length; boolean useChunkedUpload = (fileSize > CHUNKED_UPLOAD_MINIMUM) ? true : false; uploadedFiles.add(uploadEachFile(client, createdFolderId, fileName, fileSize, fileBytes, useChunkedUpload)); } } for (BoxFile.Info file: uploadedFiles) { System.out.println(file.getID()); } } } private static BoxFile.Info uploadEachFile(BoxDeveloperEditionAPIConnection client, String folderId, String fileName, int fileSize, byte[] fileBytes, boolean useChunkedUpload) throws IOException, InterruptedException, NoSuchAlgorithmException { try { BoxFolder folder = new BoxFolder(client, folderId); folder.canUpload(fileName, fileSize); if (useChunkedUpload) { System.out.println("Using chunked upload..."); return folder.uploadLargeFile(new ByteArrayInputStream(fileBytes), fileName, fileSize); } else { System.out.println("Using normal upload..."); MessageDigest md = MessageDigest.getInstance("SHA-1"); try (Formatter formatter = new Formatter()) { for (byte b: md.digest(fileBytes)) { formatter.format("%02x", b); } String fileSHA = formatter.toString(); FileUploadParams fileUpload = new FileUploadParams(); fileUpload.setContent(new ByteArrayInputStream(fileBytes)); fileUpload.setSHA1(fileSHA); fileUpload.setName(fileName); return folder.uploadFile(fileUpload); } } } catch (BoxAPIException e) { if (e.getResponseCode() == 409) { // You can use the ID returned from the conflict error to continue String conflictId = getIdFromConflict(e.getResponse()); System.out.println("Found existing file: " + conflictId); BoxFile uploadFileVersion = new BoxFile(client, conflictId); if (useChunkedUpload) { System.out.println("Using chunked upload..."); return uploadFileVersion.uploadLargeFile(new ByteArrayInputStream(fileBytes), fileSize); } else { System.out.println("Using normal upload..."); MessageDigest md = MessageDigest.getInstance("SHA-1"); try (Formatter formatter = new Formatter()) { for (byte b: md.digest(fileBytes)) { formatter.format("%02x", b); } String fileSHA = formatter.toString(); uploadFileVersion.uploadVersion(new ByteArrayInputStream(fileBytes), fileSHA); return uploadFileVersion.getInfo(); } } } else { throw e; } } } private static String getIdFromConflict(String message) { String id = ""; Pattern p = Pattern.compile("\"id\":\"[0-9]+\""); Pattern p2 = Pattern.compile("[0-9]+"); Matcher m = p.matcher(message); if (m.find()) { String sub = m.group(); Matcher m2 = p2.matcher(sub); if (m2.find()) { id = m2.group(); } } return id; } } ``` ``` "use strict"; const fs = require("fs"); const path = require("path"); const box = require("box-node-sdk"); const crypto = require("crypto"); let configFile = fs.readFileSync("config.json"); configFile = JSON.parse(configFile); let session = box.getPreconfiguredInstance(configFile); let client = session.getAppAuthClient("enterprise"); const CHUNKED_UPLOAD_MINIMUM = 200000; const parentFolderId = "0"; const directoryName = "uploadFolder"; let files = []; fs.readdirSync(directoryName).forEach(file => { files.push({ fileName: file, content: fs.readFileSync(path.join(__dirname, directoryName, file)) }); }); client.folders .create(parentFolderId, directoryName) .then(createdFolder => { console.log(createdFolder); return processFiles(client, files, createdFolder.id); }) .catch(err => { let conflictId = handleFolderConflictError(err); if (conflictId) { console.log(`Found an existing folder: ${conflictId}`); return processFiles(client, files, conflictId); } else { throw err; } }) .then(results => { console.log(results); }) .catch(err => { console.log(err); }); function processFiles(client, files, folderId) { let fileUploadPromises = []; files.forEach(file => { fileUploadPromises.push( uploadAFile(client, folderId, file.fileName, file.content) ); }); return Promise.all(fileUploadPromises); } function uploadAFile(client, folderId, fileName, toUploadFile) { return client.files .preflightUploadFile(folderId, { name: fileName, size: toUploadFile.length }) .then(preflightResults => { console.log(preflightResults); if (toUploadFile.length < CHUNKED_UPLOAD_MINIMUM) { console.log("Using normal upload..."); let fileSha = crypto .createHash("sha1") .update(toUploadFile) .digest("hex"); client.setCustomHeader("content-md5", fileSha); return client.files.uploadFile(folderId, fileName, toUploadFile); } else { console.log("Using chunked upload..."); client.setCustomHeader("content-md5", null); return client.files .getChunkedUploader( folderId, toUploadFile.length, fileName, toUploadFile ) .then(uploader => { return new Promise((resolve, reject) => { uploader.on("error", err => { reject(err); }); uploader.on("chunkUploaded", part => { console.log("Part uploaded..."); console.log(part); }); uploader.on("uploadComplete", file => { console.log("File upload complete!"); resolve(file); }); console.log("Starting chunked uploader..."); uploader.start(); }); }); } }) .catch(err => { let conflictId = handleFileConflictError(err); if (conflictId) { console.log(`Found existing file with that name: ${conflictId}`); return uploadANewFileVersion(client, conflictId, toUploadFile); } else { throw err; } }); } function uploadANewFileVersion(client, conflictId, toUploadFile) { if (toUploadFile.length < CHUNKED_UPLOAD_MINIMUM) { console.log("Using normal upload..."); let fileSha = crypto .createHash("sha1") .update(toUploadFile) .digest("hex"); client.setCustomHeader("content-md5", fileSha); // You can optionally rename a folder while uploading a new version. // let newFileName = "ubuntu-no-gui.iso"; // let options = { // name: newFileName // } // return client.files.uploadNewFileVersion(conflictId, options, toUploadFile); return client.files.uploadNewFileVersion(conflictId, toUploadFile); } else { console.log("Using chunked upload..."); // You can optionally rename a folder while uploading a new version. // let newFileName = "ubuntu-no-gui.iso"; // let options = { // name: newFileName // } // return client.files.getNewVersionChunkedUploader(conflictId, toUploadFile.length, toUploadFile, options) client.setCustomHeader("content-md5", null); return client.files .getNewVersionChunkedUploader( conflictId, toUploadFile.length, toUploadFile, null ) .then(uploader => { return new Promise((resolve, reject) => { uploader.on("error", err => { reject(err); }); uploader.on("chunkUploaded", part => { console.log("Part uploaded..."); console.log(part); }); uploader.on("uploadComplete", file => { console.log("File upload complete!"); resolve(file); }); console.log("Starting chunked uploader..."); uploader.start(); }); }); } } function handleFileConflictError(e) { if (e && e.response && e.response.body) { let errorBody = e.response.body; if (errorBody.status === 409) { if ( errorBody.context_info && errorBody.context_info.conflicts && errorBody.context_info.conflicts ) { let conflict = errorBody.context_info.conflicts; if (conflict && conflict.id) { return conflict.id; } } } } } function handleFolderConflictError(e) { if (e && e.response && e.response.body) { let errorBody = e.response.body; if (errorBody.status === 409) { if ( errorBody.context_info && errorBody.context_info.conflicts && errorBody.context_info.conflicts.length > 0 ) { let conflict = errorBody.context_info.conflicts[0]; if (conflict && conflict.id) { return conflict.id; } } } } } ``` ``` box folders:upload ./folder_name_to_upload --parent-folder=$folder_id ``` ## Breakdown The scripts above use the Box SDKs and the CLI to upload an entire folder. For the SDK scripts, they start by creating a directory in Box to match the local folder. After the new directory is created, it uploads all files within the directory making sure to use all available Box features to make the uploads successful. Using the [Preflight](g://uploads/check) API the files are checked for conflicts and size restrictions before they are uploaded. If a naming conflict is found, the script instead uploads a new version of that file. Using the the `SHA` hash of the file the scripts add a `content-md5` header on upload to make sure the file is successfully uploaded to Box without any bytes lost or tampered with. Finally, if a file size exceeds 20MB`, the script uses the Chunked Upload feature to make sure uploads are more reliable for larger files. **Reference:** https://developer.box.com/guides/uploads/chunked/folder/ --- ## Untitled *Type: guide | Category: Uploads * Upload Part When you want to upload a large file, you can split it into smaller parts and upload them using the Upload Part API. Create… # Upload Part When you want to upload a large file, you can split it into smaller parts and upload them using the Upload Part API. ## Create Upload Session First, [create an upload session](g://uploads/chunked/create-session). The resulting object defines the size of each part and the number of parts to upload. ``` { "id": "F971964745A5CD0C001BBE4E58196BFD", "type": "upload_session", "session_expires_at": "2012-12-12T10:53:43-08:00", "part_size": 1024, "total_parts": 1000, "num_parts_processed": 455, "session_endpoints": { "upload_part": "https://upload.box.com/api/2.0/files/upload_sessions/F971964745A5CD0C001BBE4E58196BFD", "commit": "https://upload.box.com/api/2.0/files/upload_sessions/F971964745A5CD0C001BBE4E58196BFD/commit", "abort": "https://upload.box.com/api/2.0/files/upload_sessions/F971964745A5CD0C001BBE4E58196BFD", "list_parts": "https://upload.box.com/api/2.0/files/upload_sessions/F971964745A5CD0C001BBE4E58196BFD/parts", "status": "https://upload.box.com/api/2.0/files/upload_sessions/F971964745A5CD0C001BBE4E58196BFD", "log_event": "https://upload.box.com/api/2.0/files/upload_sessions/F971964745A5CD0C001BBE4E58196BFD/log" } } ``` ## Split File Split the file into parts to be uploaded. If you want to use the command line, use the `split` command: ``` split -b <PART_SIZE> <FILE_NAME> <YOUR_PART_NAME> ``` For example: ``` split -b 8388608 video.mp3 videopart ``` This will result in your file divided into several files. ## Get SHA Digest To get the value for the `SHA` digest, use the following openSSL command to encode the file part: ``` openssl sha1 -binary <FILE_PART_NAME> | base64 ``` For example: ``` openssl sha1 -binary videoparta | base64 ``` The result is a base-64 encoded message used to verify the upload. ## Upload Part Upload the bytes for the part you want to upload, specifying the byte range for the part and the `SHA` digest to ensure the content is uploaded correctly. ### Content Range Each part’s size must be exactly equal in size to the part size specified in the upload session that you created. One exception is the last part of the file, as this can be smaller. The `Content-Range` parameter definition follows this pattern: ``` -H "Content-Range: bytes <LOWER_BOUND>-<HIGHER_BOUND>/<TOTAL_SIZE>" ``` When providing the value for `Content-Range`, remember that: - The lower bound of each part's byte range must be a multiple of the part size. - The higher bound must be a multiple of the part size - 1. For example, if the part size is `8388608`, the content range for the first two parts will be: ``` -H "Content-Range: bytes 0-8388607/32127641" \ ## first part -H "Content-Range: bytes 8388608-16777215/32127641" \ ## second part ``` ## Response After each upload, the resulting response includes the `ID` and `SHA` of the part uploaded. ``` { "part_id": "6F2D3486", "offset": 16777216, "size": 3222784, "sha1": "134b65991ed521fcfe4724b7d814ab8ded5185dc" } ``` Keep all the JSON responses from all part uploads as they are needed to [commit the session](g://uploads/chunked/commit-session). ## Range Overlap If a part upload request fails with any error code `range_overlaps_existing_part` then the application made a mistake in cutting up the file into parts and tried to upload a part into a range that already had content uploaded for it. The application should assume that this last part was not persisted to the session. ## Parallel uploads Although you can upload the parts in parallel, try to upload them in order as much as is possible. Parts with a lower byte offset should be uploaded before parts with a higher byte offset. The recommended approach is to upload 3 to 5 parts in parallel from a queue of parts, ordered by byte offset. If a part upload fails, retry it before you upload further parts. **Reference:** https://developer.box.com/guides/uploads/chunked/upload-part/ --- ## Untitled *Type: guide | Category: Uploads * Upload File Version To upload a new version of a file to Box via direct upload, make an API call to the POST /files/:id/content API with the… # Upload File Version To upload a new version of a file to Box via direct upload, make an API call to the [`POST /files/:id/content`](e://post_files_id_content) API with the content of the file, the desired file name, and the folder ID. # Preflight check To prevent wasting time and bandwidth uploading a file that is going to be rejected it is recommended to perform a [pre-flight check](g://uploads/check) before uploading the file. ## Request Format The request body of this API uses a content type of `multipart/form-data`. This is used to transmit two parts, namely the file attributes and the file's actual content. The first part is called `attributes` and contains a JSON object with information about the file, including the name of the file and the `id` of the parent folder. The following is an example a `test.txt` being uploaded to the root folder of the user. ``` POST /api/2.0/files/123/content HTTP/1.1 Host: upload.box.com Authorization: Bearer [ACCESS_TOKEN] content-length: 343 content-type: multipart/form-data; boundary=------------------------9fd09388d840fef1 --------------------------9fd09388d840fef1 content-disposition: form-data; name="attributes" {"name":"test.txt", "parent":{"id":"0"}} --------------------------9fd09388d840fef1 content-disposition: form-data; name="file"; filename="test.txt" content-type: text/plain Test file text. --------------------------9fd09388d840fef1-- ``` The `attributes` JSON part of the multi-part body must come before the `file` part of the multipart form data. When out of order, the API will return a HTTP `400` status code with an error code of `metadata_after_file_contents`. ## Options To learn more about all the parameters available when uploading files, head over to the [reference documentation for this API call](e://post_files_id_content). These parameters include a `content-md5` that can be set to ensure a file is not corrupted in transit, and the ability to explicitly specify the file creation time at a different time than the upload time. For file versions an additional [`if-match` header](g://api-calls/ensure-consistency) can be passed along to prevent overwriting a file that has already been updated since the application last inspected the content. ## Restrictions Direct uploads are limited to a maximum file size of 50MB. For larger files, please use the [chunked upload APIs](g://uploads/chunked). Upload limits are dictated by the type of account of the authenticated user. More information can be found [in our community article on this topic](https://support.box.com/hc/en-us/articles/360043697314-Understand-the-Maximum-File-Size-You-Can-Upload-to-Box). **Reference:** https://developer.box.com/guides/uploads/direct/file-version/ --- ## Untitled *Type: guide | Category: Uploads * Chunked Upload with SDKs The Box SDKs make it possible to perform a chunked upload with the built-in SDK methods. The SDKs also support… # Chunked Upload with SDKs The Box SDKs make it possible to perform a chunked upload with the built-in SDK methods. The SDKs also support uploading new versions of files through similar methods. **Reference:** https://developer.box.com/guides/uploads/chunked/with-sdks/ --- ## Untitled *Type: guide | Category: Uploads * Direct Uploads The most straightforward way to upload a file to Box is using a direct upload. Direct uploads allow an application to upload… # Direct Uploads The most straightforward way to upload a file to Box is using a direct upload. Direct uploads allow an application to upload a file in one request. For file sizes over 50MB we recommend using the [chunked upload endpoint](g://uploads/chunked). The maximum file size limit for uploads to Box varies depending on your account type. For more details, please refer to our [pricing comparison page](https://www.box.com/pricing). - Free personal: 250 MB - Starter: 2 GB - Business: 5 GB - Business Plus: 15 GB - Enterprise: 50 GB - Digital Workplace Suite: 50 GB - Digital Workplace Global Suite: 50 GB - Digital Business Suite: 50 GB - Digital Business Global Suite: 50 GB - Enterprise Plus: 150 GB - Enterprise Advanced: 500 GB To confirm the file size limit for your account, log into Box. Click on the circle in the top right corner and select **Account Settings** from the dropdown menu. On the page that displays, scroll down to the **Account Details** section. Your **Max File Size** is listed here. ## Upload domain Uploads to Box happen via a different domain (`upload.box.com`) than regular API calls. This is something to keep in mind when writing your own upload code. All official Box SDKs will take care of choosing the right domain for each API call. **Reference:** https://developer.box.com/guides/uploads/direct/ --- ## Untitled *Type: guide | Category: Uploads * Upload New File To upload a file to Box via direct upload, make an API call to the POST /files/content API with the content of the file, the… # Upload New File To upload a file to Box via direct upload, make an API call to the [`POST /files/content`](e://post_files_content) API with the content of the file, the desired file name, and the folder ID. To upload files to the Archive folder, you need to first enable the Global Content Manager (GCM) scope in the Developer Console. # Preflight check To prevent wasting time and bandwidth uploading a file that is going to be rejected it is recommended to perform a [pre-flight check](g://uploads/check) before uploading the file. ## Request Format The request body of this API uses a content type of `multipart/form-data`. This is used to transmit two parts, namely the file attributes and the file's actual content. The first part is called `attributes` and contains a JSON object with information about the file, including the name of the file and the `id` of the parent folder. The following is an example a `test.txt` being uploaded to the root folder of the user. ``` POST /api/2.0/files/content HTTP/1.1 Host: upload.box.com Authorization: Bearer [ACCESS_TOKEN] content-length: 343 content-type: multipart/form-data; boundary=------------------------9fd09388d840fef1 --------------------------9fd09388d840fef1 content-disposition: form-data; name="attributes" {"name":"test.txt", "parent":{"id":"0"}} --------------------------9fd09388d840fef1 content-disposition: form-data; name="file"; filename="test.txt" content-type: text/plain Test file text. --------------------------9fd09388d840fef1-- ``` The `attributes` JSON part of the multi-part body must come before the `file` part of the multipart form data. When out of order, the API will return a HTTP `400` status code with an error code of `metadata_after_file_contents`. ## Options To learn more about all the parameters available when uploading files, head over to the [reference documentation for this API call](e://post_files_content). These parameters include a `content-md5` that can be set to ensure a file is not corrupted in transit, and the ability to explicitly specify the file creation time at a different time than the upload time. ## Restrictions Direct uploads are limited to a maximum file size of 50MB. For larger files, please use the [chunked upload APIs](g://uploads/chunked). Upload limits are dictated by the type of account of the authenticated user. More information can be found [in our community article on this topic](https://community.box.com/t5/Upload-and-Download-Files-and/Understand-the-Maximum-File-Size-You-Can-Upload-to-Box/ta-p/50590). **Reference:** https://developer.box.com/guides/uploads/direct/file/ --- ## Untitled *Type: guide | Category: Tooling * Install .NET SDK (Generated) You can use Box .NET SDK to call Box APIs in a .NET project. The SDK is available for both .NET Framework 4.… # Install .NET SDK (Generated) You can use Box .NET SDK to call Box APIs in a .NET project. The SDK is available for both .NET Framework 4.5 and .NET Core 1.0 or above. The installation of the SDK depends on the framework used. Learn more about .NET SDK on GitHub To install the .NET SDK in the .NET framework, run the following command using the [Nuget](https://www.nuget.org/) package manager. ``` PM> Install-Package Box.Sdk.Gen ``` **Reference:** https://developer.box.com/guides/tooling/sdks/dotnet-gen/ --- ## Untitled *Type: guide | Category: Tooling * Install Java SDK You can use Box Java SDK to call Box APIs in a Java project. To install Java SDK, add a Gradle dependency or Maven… # Install Java SDK You can use Box Java SDK to call Box APIs in a Java project. To install Java SDK, add a Gradle dependency or Maven dependency, or clone the source into a project. Additionally, you can download one of the precompiled JARs from the releases page on GitHub. Learn more about Java SDK on GitHub ## Gradle Add the following dependency to the `build.gradle` file. ``` compile 'com.box:box-java-sdk:2.32.0' ``` For the most up-to-date version number of the Java SDK, please refer to the [Java SDK Open Source page](http://opensource.box.com/box-java-sdk/). ## Maven Add the following to Maven dependency. ``` <dependency> <groupId>com.box</groupId> <artifactId>box-java-sdk</artifactId> <version>2.32.0</version> </dependency> ``` For the most up-to-date version number of the Java SDK, please refer to the [Java SDK Open Source page](http://opensource.box.com/box-java-sdk/). ## Installation from source Download the SDK source from the [Java SDK GitHub repository](https://github.com/box/box-java-sdk/tree/master/src/main/java/com/box/sdk) and add it to a project. ## Precompiled JAR Download one of the precompiled JARs for the Java SDK from the GitHub releases page. When using one of the precompiled JARs, it is important to also add the following additional dependencies to the project. | Dependency | | --- | | minimal-json v0.9.1 | | jose4j v0.4.4 | | bouncycastle bcprov-jdk15on v1.52 | | bouncycastle bcpkix-jdk15on v1.52 | | Java Cryptography Extension (JCE) | **Reference:** https://developer.box.com/guides/tooling/sdks/java/ --- ## Untitled *Type: guide | Category: Tooling * Install .NET SDK (Deprecated) You can use Box .NET SDK to call Box APIs in a .NET project. .NET SDK is currently in maintenance mode and… # Install .NET SDK (Deprecated) You can use Box .NET SDK to call Box APIs in a .NET project. [.NET SDK](https://github.com/box/box-windows-sdk-v2) is currently in maintenance mode and will be deprecated soon. This means only critical security updates and bug fixes will be implemented. It is recommended to use the [auto-generated .NET SDK](https://github.com/box/box-dotnet-sdk-gen). The SDK is available for both .NET Framework 4.5 and .NET Core 1.0 or above. The installation of the SDK depends on the framework used. Learn more about .NET SDK on GitHub ## .NET Framework To install the .NET SDK in the .NET framework, run the following command using the [Nuget](https://www.nuget.org/) package manager. ``` PM> Install-Package Box.V2 ``` ## .NET Core To install the .NET SDK in the .NET Core framework, run the following command using the [Nuget](https://www.nuget.org/) package manager. ``` PM> Install-Package Box.V2.Core ``` **Reference:** https://developer.box.com/guides/tooling/sdks/dotnet/ --- ## Untitled *Type: guide | Category: Tooling * Install Node SDK (Deprecated) You can use Box Node SDK to call Box APIs in a Node project. Node SDK is currently in maintenance mode and… # Install Node SDK (Deprecated) You can use Box Node SDK to call Box APIs in a Node project. [Node SDK](https://github.com/box/box-node-sdk) is currently in maintenance mode and will be deprecated soon. This means only critical security updates and bug fixes will be implemented. It is recommended to use the [auto-generated TypeScript SDK](g://tooling/sdks/typescript-gen). Learn more about Node SDK on GitHub ## NPM installation To install the Node SDK run the following command from your terminal window or command prompt using the [Node Package Manager](https://www.npmjs.com/). ``` npm install box-node-sdk --save ``` ## Yarn installation Similarly, the SDK can be installed using the [Yarn package](https://yarnpkg.com/) manager. ``` yarn add box-node-sdk ``` **Reference:** https://developer.box.com/guides/tooling/sdks/node/ --- ## Untitled *Type: guide | Category: Tooling * SDKs Box offers a set of SDKs you can use to build your application. The next generation .NET SDK is the newest addition to the set. It is… # SDKs Box offers a set of SDKs you can use to build your application. The next generation .NET SDK is the newest addition to the set. It is still a beta feature, but you can give them a try to explore all the features they bring along. The tables below list SDKs along with additional information telling you if the project is maintained and has API parity. **Maintained:** Fully maintained projects are actively developed by Box. They receive the latest security updates and new features. For support with these projects please visit GitHub or [our Platform support forum](https://support.box.com/hc/en-us/community/topics/360001932973-Platform-and-Developer-Forum). **API Parity**: Projects with full API parity are actively updated with all platform functionality as this becomes available on the Box Platform. Projects with partial API parity lack some functionality while we work on bringing these projects to full parity. ## Next generation SDKs The latest generation Box Python SDK, Box TypeScript SDK, .NET SDK, and Swift SDK are designed to elevate the developer experience and streamline your integration with the Box Content Cloud. Swift SDK is in a Public Beta phase. Here's what you can expect from the new SDKs: - **Full API Support**: New Box SDKs empower developers with complete coverage of the Box API ecosystem. You can access all the latest features and functionalities offered by Box and build feature-rich applications. - **Rapid API Updates**: The new auto-generation development approach allows for adding Box APIs to SDKs at a much faster pace (in a matter of days). This means you can leverage the most up-to-date features in your applications without delay. - **Embedded Documentation**: All objects and parameters are documented directly in the source code of the SDK so all the necessary information is stored in one place. - **Enhanced Convenience Methods**: The newly introduced convenience methods cover various aspects such as authentication, chunk uploads, exponential back-offs, automatic retries, type checkers which help to ensure that you’re using variables correctly, and much more. Download the **Box Platform** SDK in a programming language of your choice and get started: | Platform | Maintained? | API Parity | | --- | --- | --- | | Python Gen SDK | Yes | Full | | TypeScript Gen SDK | Yes | Full | | .NET Gen SDK | Yes | Full | | Swift Gen SDK (Beta) | Yes | Full | | Java Gen SDK (Beta) | Yes | Full | ## Legacy SDKs The table lists legacy Box SDKs that you can use when building your applications. For latest API support and features, use the next generation SDKs. | Platform | Maintained? | API Parity | | --- | --- | --- | | Java SDK | Yes | Full | | iOS Content SDK | Yes | Full | | Android Content SDK | No | Partial | | .NET SDK | Deprecated. Only critical security updates and bug fixes are implemented. | Full | | Python SDK | Deprecated. Only critical security updates and bug fixes are implemented. | Full | | Node SDK | Deprecated. Only critical security updates and bug fixes are implemented. | Full | As of May 31, 2023 Android SDK is no longer supported. You can still use your existing Android SDK applications with no impact, but you won't receive new features, updates, or bug fixes. If you would like to continue getting the latest and greatest Android features, use Java SDK to build apps on Android. Refer to [this](https://github.com/box/box-java-sdk/blob/main/doc/android.md) documentation for more details. **Reference:** https://developer.box.com/guides/tooling/sdks/ --- ## Untitled *Type: guide | Category: Tooling * Install Python SDK (Generated) You can use the auto-generated Box Python SDK to call Box APIs in a Python project. This next generation SDK… # Install Python SDK (Generated) You can use the **auto-generated** Box Python SDK to call Box APIs in a Python project. This [next generation SDK](g://tooling/sdks#next-generation-sdks) brings along new functionality to designed to elevate the developer experience and streamline your integration with the Box Content Cloud. Learn more about auto-generated Python SDK on GitHub ## Installation To install the auto-generated Python SDK run the following command from your terminal window or command prompt using [pip](https://pypi.org/project/pip/). ``` pip install box-sdk-gen ``` ## JWT application When working with a Box App that uses server-side [JWT authentication](g://authentication/jwt), install the following additional module: ``` pip install "box-sdk-gen[jwt]" ``` **Reference:** https://developer.box.com/guides/tooling/sdks/python-gen/ --- ## Untitled *Type: guide | Category: Tooling * Install Python SDK (Deprecated) You can use Box Python SDK to call Box APIs in a Python project. Python SDK is currently in maintenance mode… # Install Python SDK (Deprecated) You can use Box Python SDK to call Box APIs in a Python project. [Python SDK](https://github.com/box/box-python-sdk) is currently in maintenance mode and will be deprecated soon. This means only critical security updates and bug fixes will be implemented. It is recommended to use the [auto-generated Python SDK](g://tooling/sdks/python-gen). Learn more about Python SDK on GitHub ## Installation To install the Python SDK run the following command from your terminal window or command prompt using [pip](https://pypi.org/project/pip/). ``` pip install boxsdk ``` ## JWT application When working with a Box App that uses server-side [JWT authentication](g://authentication/jwt), install the following additional module: ``` pip install "boxsdk[jwt]" ``` **Reference:** https://developer.box.com/guides/tooling/sdks/python/ --- ## Untitled *Type: guide | Category: Tooling * Install Salesforce SDK The Salesforce SDK can be deployed directly to Sandbox or Developer organizations by using the Deploy to Salesforce… # Install Salesforce SDK The Salesforce SDK can be deployed directly to Sandbox or Developer organizations by using the [Deploy to Salesforce](https://githubsfdeploy.herokuapp.com/?owner=box&repo=box-salesforce-sdk) functionality. "Deploy to Salesforce" functionality is not owned or maintained by Box. The SDK is also distributed as an unmanaged package: - [Production/Developer Package](https://cloud.box.com/Box-Apex-SDK) - [Sandbox Package](https://cloud.box.com/Box-Apex-SDK-Sandbox) Unmanaged packages can't be upgraded once installed in a Salesforce org so future upgrades will have to be applied by cloning the repositories locally and updating classes from your IDE. **Reference:** https://developer.box.com/guides/tooling/sdks/salesforce/ --- ## Untitled *Type: guide | Category: Tooling * Install Swift SDK (Generated) You can use Box Swift SDK to call Box APIs in a Swift project. Swift SDK is in a Public Beta phase. Learn more… # Install Swift SDK (Generated) You can use Box Swift SDK to call Box APIs in a Swift project. Swift SDK is in a Public Beta phase. Learn more about Swift SDK on GitHub ## Swift Package Manager [Swift Package Manager](https://www.swift.org/documentation/package-manager/) is a tool for managing the distribution of Swift code. It is integrated with the Swift build system to automate the process of downloading, compiling, and linking dependencies. To add a dependency to your Xcode project: 1. Select **File** > **Add Package Dependency** in your Xcode project. 2. Click the **plus** icon > **Add Package Collection**. 3. Enter the following repository URL: `https://github.com/box/box-swift-sdk-gen.git` and click **Load**. Alternatively you can add a dependency to the dependencies value of your `Package.swift`. For detailed instructions, please see the official documentation for [Swift Package Manager](https://www.swift.org/documentation/package-manager/) and [XCode documentation](https://developer.apple.com/documentation/xcode/adding-package-dependencies-to-your-app). ## Carthage Carthage is a decentralized dependency manager which builds your dependencies and provides you with binary frameworks. To add a dependency to `BoxSdkGen`: 1. add the following line to your `Cartfile` : ``` git "https://github.com/box/box-swift-sdk-gen.git" ``` 1. Run: ``` carthage bootstrap --use-xcframeworks ``` 1. Drag the built `xcframework` from **Carthage/Build** into your project. For more detailed instructions, please see the [official documentation for Carthage](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application). **Reference:** https://developer.box.com/guides/tooling/sdks/swift-gen/ --- ## Untitled *Type: guide | Category: Users * Deprovision Users Part of regular Box enterprise maintenance is removing accounts for users that are no longer active in your enterprise… # Deprovision Users Part of regular Box enterprise maintenance is removing accounts for users that are no longer active in your enterprise. When removing a user from your enterprise, you'll need to move all content owned by the user into another account before deleting the user account. The delete user request will fail if the user account still has content in it. An optional `force` parameter is available in the API call, which will force delete the user account along with all content in the account. The standard best practice when decommissioning a user account is to move all content owned by that user into another admin level account or into the application service account. Once moved, you can transfer ownership of the content to a different user or collaborate a different user on the content if needed. ## Deprovisioning Example Use the following code samples to transfer a user's content and then delete the user. When content is being transferred, a new folder is created in the destination user's root folder following this pattern: `employee_email@email.com - employee_name's Files and Folders` ``` 'use strict' const box = require('box-node-sdk'); const fs = require('fs'); let configFile = fs.readFileSync('config.json'); configFile = JSON.parse(configFile); let session = box.getPreconfiguredInstance(configFile); let serviceAccountClient = session.getAppAuthClient('enterprise'); const transferUserID = '3278487052'; (async () => { let serviceAccount = await serviceAccountClient.users.get('me'); let transferredFolder = await serviceAccountClient.enterprise.transferUserContent(transferUserID,serviceAccount.id); console.log(transferredFolder); await serviceAccountClient.users.delete(transferUserID, null); console.log('Completed'); })(); ``` ``` Path configPath = Paths.get("config.json"); try (BufferedReader reader = Files.newBufferedReader(configPath,Charset.forName("UTF-8"))){ String transferUserId = "3277722534"; BoxConfig boxConfig = BoxConfig.readFrom(reader); BoxDeveloperEditionAPIConnection serviceAccountClient = BoxDeveloperEditionAPIConnection .getAppEnterpriseConnection(boxConfig); BoxUser destinationUser = new BoxUser(serviceAccountClient, BoxUser.getCurrentUser(serviceAccountClient).getID()); try { destinationUser.moveFolderToUser(transferUserId); } catch (BoxAPIException e) {} BoxUser removeUser = new BoxUser(serviceAccountClient, transferUserId); removeUser.delete(false, false); } ``` ``` using(FileStream fs = new FileStream("./config.json", FileMode.Open)) { var config = BoxConfig.CreateFromJsonFile(fs); var session = new BoxJWTAuth(config); var serviceAccountClient = session.AdminClient(session.AdminToken()); var transferUserId = "3276247601"; var serviceAccount = await serviceAccountClient.UsersManager.GetCurrentUserInformationAsync(); var moveAction = await serviceAccountClient.UsersManager.MoveUserFolderAsync(transferUserId,serviceAccount.Id); System.Console.WriteLine(moveAction.Name); await serviceAccountClient.UsersManager.DeleteEnterpriseUserAsync(transferUserId,false,false); } ``` ``` box users:transfer-content $transfer_from_user_id $transfer_to_user_id box users:delete $transfer_from_user_id --yes ``` **Reference:** https://developer.box.com/guides/users/deprovision/ --- ## Untitled *Type: guide | Category: Tooling * Install Typescript SDK (Generated) You can use the auto-generated Box TypeScript SDK to call Box APIs in a TypeScript project. This next… # Install Typescript SDK (Generated) You can use the **auto-generated** Box TypeScript SDK to call Box APIs in a TypeScript project. This [next generation SDK](g://tooling/sdks#next-generation-sdks) brings along new functionality to designed to elevate the developer experience and streamline your integration with the Box Content Cloud. Learn more about auto-generated TypeScript SDK on GitHub ## NPM installation To install the TypeScript SDK run the following command from your terminal window or command prompt using the [Node Package Manager](https://www.npmjs.com/). ``` npm install box-typescript-sdk-gen ``` ## Yarn installation Similarly, the SDK can be installed using the [Yarn package](https://yarnpkg.com/) manager. ``` yarn add box-typescript-sdk-gen ``` **Reference:** https://developer.box.com/guides/tooling/sdks/typescript-gen/ --- ## Untitled *Type: guide | Category: Users * Transfer Files & Folders As part of user account deprovisioning, a common requirement is to transfer all files and folders that are stored… # Transfer Files & Folders As part of user account deprovisioning, a common requirement is to transfer all files and folders that are stored within the user account to another user account or into a location for long term storage, such as into the service account. There are two general methods that are employed to accomplish this within Box: - Using the direct [transfer owned folders](e://put_users_id_folders_0) API, which will move all content from one user directly to another. - Using the collaboration transfer method to change ownership of one file or folder at a time from one user to another. Files owned by a user will be inaccessible while they are being transferred. This also means that any shared content owned by the user may be inaccessible during the move. Depending on the volume of content, this operation may take a significant amount of time. ## Transfer Owned Folders API Method The [transfer owned folders endpoint](e://put_users_id_folders_0) is designed to move the entirety of content owned by one user over to another user. The transfer owned folders API is performed as a synchronous process, which might lead to a slow response when the source user has a large number of items in all of its folders. To call the transfer endpoint, you will supply the user ID to transfer from and the user ID to transfer to. ## Collaboration Transfer Method The collaboration transfer method is a process that uses the [collaboration endpoint](e://post_collaborations) to change the ownership of a single file or folder from one user to another instantaneously. This method will perform an instantaneous transfer of ownership of a single file or folder, but **cannot** be used to transfer the root (all files and folders) from one user to another. The general process, between `transfer_from_user` to `transfer_to_user`, will follow these steps: ### Add Transfer To User as Co-Owner The first step is to add the `transfer_to_user` account as a collaborator with `co-owner` access on the file or folder that should be transferred. Making the call as the `transfer_from_user` account, add the `transfer_to_user` as a co-owner using the [add collaboration endpoint](e://post_collaborations). ### Fetch Collaboration ID as Transfer To User The next step is make a request to get the collaboration information, making the request as the `transfer_to_user` account. The collaboration object returned will include a collaboration ID, which is used for the last step. Making the call as the `transfer_to_user` account, get the collaboration on the file or folder ID being transferred, using the [get collaboration endpoint](e://get_collaborations_id). Capture the collaboration ID. ### Remove Transfer From User as Owner The final step is to remove the `transfer_from_user` account as an owner of the file or folder, which is accomplished using the [delete collaboration endpoint](e://delete_collaborations_id). Making call as the `transfer_to_user` account, remove the `transfer_from_user` as a collaborator on the file or folder. The file or folder is now owned by the `transfer_to_user` account, and the `transfer_from_user` account no longer has access. **Reference:** https://developer.box.com/guides/users/deprovision/transfer-folders/ --- ## Untitled *Type: guide | Category: Users * Provision Users When setting up a brand new Box user account, a common need is to have that new account be populated with standard folders… # Provision Users When setting up a brand new Box user account, a common need is to have that new account be populated with standard folders, collaborations, and group associations. Typically there are some common questions that may be asked about the user account to determine when standard setup may be needed for the account: - Will the user need access to standard company files or folders immediately? - Are collaborations associated individually or through groups? If through a group association, are there any standard groups that the user should be added to? - Should the user be assigned any tasks to complete? - Would any instructional comments on any files be helpful? # New User Password Resets and Email Confirmation You may experience some errors when creating users and immediately trying to take actions with that user through the API. For example, a common error to receive is `user_email_confirmation_required` or `password_reset_required`. These kinds of errors may block some actions within the API, but you can still add the user as a collaborator on folders, add the user to groups, etc. ## Sample Overview In this scenario we'll focus on provisioning a new [Box Managed User](page://platform/user-types/#managed-users), as there are very different considerations when provisioning Box App User accounts. We'll start with solving the most repeatable aspects of provisioning a user's account, creating a general folder and file structure that everyone will have on first login, using groups to control access to shared files and folders for users. **Reference:** https://developer.box.com/guides/users/provision/ --- ## Untitled *Type: guide | Category: Users * Create Architecture Skeleton Our first requirement is to copy general files and folders into each individual user's root folder on account… # Create Architecture Skeleton Our first requirement is to copy general files and folders into each individual user's root folder on account creation. This problem has been solved within standard Linux distributions through a directory called `/etc/skel`, which we'll emulate with a Box specific solution here. When adding a new user in Linux, the files and folders within `/etc/skel` are copied to the new user's home directory. When creating a [JWT-based Box application](g://authentication/jwt/jwt-setup), a [Service Account](page://platform/user-types/#service-account) is created within the Box Enterprise. A Service Account is similar in functionality to a co-admin within a Box Enterprise, and most useful to this use case, can own, copy, and collaborate other users on files and folders. More importantly, you don't have to use a Service Account strictly for developing platform applications for users, and instead, can use a Service Account in more of an automation capacity. # Box Platform Application Requirements When creating your JWT-based custom Box application for this recipe, you'll need to enable the following scopes: **Manage users**, **Manage groups**, **Perform Actions as Users**, and **Generate User Access Tokens**. See [JWT Application Setup](g://authentication/jwt/jwt-setup) for more information on creating a JWT-based Box application and the scopes in a Box application. We'll start by creating the `etc` and `skel` folders and granting ownership of the folders to the Service Account. ``` { "name": "etc", "parent": { "id": "0" }, "children": [ { "name": "skel", "children": [] } ] } ``` ``` [ { "name": "Market Research", "parent": { "id": "44884797174" }, "children": [ { "name": "Statistics", "children": [ { "name": "Computed", "children": [] } ] } ] }, { "name": "Sales Plays", "parent": { "id": "44884797174" }, "children": [ { "name": "Big Pharma", "children": [] } ] } ] ``` The code here can optionally be reused to build any folder structure formatted as the JSON objects above demonstrate. ``` "use strict"; const fs = require("fs"); const box = require("box-node-sdk"); class BoxFolderTreeCreator { constructor(boxClient, options) { options = options || {}; if (options.boxClient) { throw new Error("Must include a boxClient field."); } options.boxFolderTreeName = options.boxFolderTreeName || "tree.json"; this.boxClient = boxClient; this.boxFolderTree = JSON.parse(fs.readFileSync(options.boxFolderTreeName)); this.createdBoxFolders = []; } async createFolderTree(branch = null, parentFolderId = "0") { this.createdBoxFolders = []; if (Array.isArray(this.boxFolderTree)) { let folderTasks = []; this.boxFolderTree.forEach(folder => { folderTasks.push(this._createFolder(folder, "")); }); await Promise.all(folderTasks); return this.createdBoxFolders; } else if (typeof this.boxFolderTree === "object") { console.log("Is object"); await this._createFolders(this.boxFolderTree, ""); return this.createdBoxFolders; } else { throw new Error("Incorrectly formatted JSON folder tree."); } } async _createFolders(branch, parentFolderId = "0") { if (branch.parent != null && branch.parent.id != null) { parentFolderId = branch.parent.id; } let folder; try { folder = await this.boxClient.folders.create(parentFolderId, branch.name); } catch (e) { let existingFolderId = BoxFolderTreeCreator.handleFolderConflictError(e); folder = await this.boxClient.folders.get(existingFolderId); } this.createdBoxFolders.push(folder); if (branch.children.length <= 0) { console.log("No more folders to create..."); return; } else { let createFolderTasks = []; branch.children.forEach(child => { console.log("Creating folder..."); console.log(child.name); createFolderTasks.push(this._createFolders(child, folder.id)); }); return await Promise.all(createFolderTasks); } } static handleFolderConflictError(e) { if (e && e.response && e.response.body) { let errorBody = e.response.body; if (errorBody.status === 409) { if ( errorBody.context_info && errorBody.context_info.conflicts && errorBody.context_info.conflicts.length > 0 ) { let conflict = errorBody.context_info.conflicts[0]; if (conflict && conflict.id) { return conflict.id; } } } } } } let configFile = fs.readFileSync("config.json"); configFile = JSON.parse(configFile); let session = box.getPreconfiguredInstance(configFile); let serviceAccountClient = session.getAppAuthClient("enterprise"); let treeCreator = new BoxFolderTreeCreator(serviceAccountClient); (async () => { let createdFolders = await treeCreator.createFolderTree(); console.log(createdFolders); })(); ``` ``` package com.box; import java.io.BufferedReader; import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.box.sdk.BoxAPIException; import com.box.sdk.BoxConfig; import com.box.sdk.BoxDeveloperEditionAPIConnection; import com.box.sdk.BoxFolder; import com.eclipsesource.json.JsonObject; import com.eclipsesource.json.JsonValue; public class BoxFolderTreeCreator { private BoxDeveloperEditionAPIConnection _boxClient; private JsonValue _boxFolderTree; private ArrayList<BoxFolder.Info > _createdFolders; public BoxFolderTreeCreator(BoxDeveloperEditionAPIConnection boxClient) throws IOException { this(boxClient, "tree.json"); } public BoxFolderTreeCreator(BoxDeveloperEditionAPIConnection boxClient, String folderTreeFileName) throws IOException { this._boxClient = boxClient; try (BufferedReader tree = Files.newBufferedReader(Paths.get(folderTreeFileName))) { this._boxFolderTree = JsonValue.readFrom(tree); } this._createdFolders = new ArrayList<>(); } public ArrayList<BoxFolder.Info > createFolderTree() { if (this._boxFolderTree.isArray()) { for (JsonValue folder: this._boxFolderTree.asArray()) { System.out.println("Processing this folder: " + folder); _createFolders(folder.asObject(), null); } return this._createdFolders; } else { _createFolders(this._boxFolderTree.asObject(), null); return this._createdFolders; } } private void _createFolders(JsonObject branch, String parentFolderID) { if (parentFolderID == null && branch.get("parent") != null && branch.get("parent").asObject().get("id") != null) { System.out.println("Looking for parent folder id..."); System.out.println(branch.get("parent").asObject().get("id").asString()); parentFolderID = branch.get("parent").asObject().get("id").asString(); } System.out.println("Folder name:"); System.out.println(branch.get("name")); System.out.println("Parent Folder ID:"); System.out.println(parentFolderID); BoxFolder.Info createdFolder; try { BoxFolder parent = new BoxFolder(this._boxClient, parentFolderID); createdFolder = parent.createFolder(branch.get("name").asString()); } catch (BoxAPIException e) { if (e.getResponseCode() == 409) { // Use the ID returned from the conflict error to continue String conflictId = getIdFromConflict(e.getResponse()); createdFolder = new BoxFolder(this._boxClient, conflictId).getInfo(); } else { throw e; } } this._createdFolders.add(createdFolder); if (!branch.get("children").asArray().isEmpty()) { for (JsonValue child: branch.get("children").asArray()) { _createFolders(child.asObject(), createdFolder.getID()); } } else { return; } } private static String getIdFromConflict(String message) { String id = ""; Pattern p = Pattern.compile("\"id\":\"[0-9]+\""); Pattern p2 = Pattern.compile("[0-9]+"); Matcher m = p.matcher(message); if (m.find()) { String sub = m.group(); Matcher m2 = p2.matcher(sub); if (m2.find()) { id = m2.group(); } } return id; } public static void main(String[] args) throws Exception { Path configPath = Paths.get("config.json"); try (BufferedReader reader = Files.newBufferedReader(configPath, Charset.forName("UTF-8"))) { BoxConfig boxConfig = BoxConfig.readFrom(reader); BoxDeveloperEditionAPIConnection serviceAccountClient = BoxDeveloperEditionAPIConnection .getAppEnterpriseConnection(boxConfig); BoxFolderTreeCreator treeBuilder = new BoxFolderTreeCreator(serviceAccountClient, "etc_skel.json"); ArrayList<BoxFolder.Info > folders = treeBuilder.createFolderTree(); for (BoxFolder.Info folder: folders) { System.out.println(folder.getID()); } } } } ``` ``` using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Box.V2; using Box.V2.Config; using Box.V2.Exceptions; using Box.V2.JWTAuth; using Box.V2.Models; using Newtonsoft.Json.Linq; namespace BoxPlayground { public class Program { static void Main(string[] args) { ExecuteMainAsync().Wait(); } private static async Task ExecuteMainAsync() { using(FileStream fs = new FileStream("./config.json", FileMode.Open)) { var session = new BoxJWTAuth(BoxConfig.CreateFromJsonFile(fs)); var client = session.AdminClient(session.AdminToken()); var treeCreator = new BoxFolderTreeCreator(client, "etc_skel.json"); var createdFolders = await treeCreator.CreateFolderTree(); foreach(var folder in createdFolders) { System.Console.WriteLine(folder.Name); System.Console.WriteLine(folder.Id); } } } public class BoxFolderTreeCreator { public BoxClient BoxClient { get; set; } public JToken BoxFolderTree { get; set; } public List < BoxFolder > CreatedBoxFolders { get; set; } public BoxFolderTreeCreator(BoxClient boxClient, string boxFolderTreeFileName = "tree.json") { this.BoxClient = boxClient; this.BoxFolderTree = JToken.Parse(File.ReadAllText(boxFolderTreeFileName)); this.CreatedBoxFolders = new List < BoxFolder > (); } public async Task < List < BoxFolder >> CreateFolderTree(dynamic branch = null, string parentFolderId = "0") { this.CreatedBoxFolders = new List < BoxFolder > (); if (this.BoxFolderTree is JArray) { var folderTasks = new List < Task > (); foreach(JObject folder in this.BoxFolderTree) { folderTasks.Add(_createFolder(folder, String.Empty)); } await Task.WhenAll(folderTasks); return this.CreatedBoxFolders; } else if (this.BoxFolderTree is JObject) { System.Console.WriteLine("Is object"); await _createFolder(this.BoxFolderTree as JObject, String.Empty); return this.CreatedBoxFolders; } else { throw new Exception("Incorrectly formatted JSON folder tree."); } } private async Task _createFolder(dynamic branch, string parentFolderId = "0") { if (branch.parent != null && branch.parent.id != null) { parentFolderId = branch.parent.id; } BoxFolder createdFolder; try { createdFolder = await this.BoxClient.FoldersManager.CreateAsync( new BoxFolderRequest { Parent = new BoxRequestEntity { Id = parentFolderId }, Name = branch.name }); } catch(BoxConflictException < BoxFolder > e) { createdFolder = await this.BoxClient.FoldersManager.GetInformationAsync(e.ConflictingItems.FirstOrDefault().Id); } this.CreatedBoxFolders.Add(createdFolder); if (branch.children.Count <= 0) { System.Console.WriteLine("No more folders to create..."); return; } else { var createFolderTasks = new List < Task > (); foreach(var child in branch.children) { System.Console.WriteLine("Creating folder..."); System.Console.WriteLine(child.name); createFolderTasks.Add(_createFolder(child as JObject, createdFolder.Id)); } await Task.WhenAll(createFolderTasks); } } } } } ``` **Reference:** https://developer.box.com/guides/users/provision/architecture/ --- ## Untitled *Type: guide | Category: Users * Populate Content Once the architecture files have been defined through the etc/skel structure in your service account, you can now use the… # Populate Content Once the architecture files have been defined through the `etc/skel` structure in your service account, you can now use the following script to copy anything under the `skel` directly to the new user's root directory. ``` 'use strict' const box = require('box-node-sdk'); const fs = require('fs'); const skelFolderId = "45117847998"; const userID = "275111793"; let configFile = fs.readFileSync('config.json'); configFile = JSON.parse(configFile); let session = box.getPreconfiguredInstance(configFile); let serviceAccountClient = session.getAppAuthClient("enterprise"); (async () => { // The userID can be obtained when creating the user via the // API or by using the search users feature. // The skel folder ID shouldn't ever change unless it's deleted and recreated. await copySkelDirectoryForUser(userID, skelFolderId, serviceAccountClient); })(); async function copySkelDirectoryForUser(userID, skelFolderId, boxClient) { // Enable iterators in case there are more than the // default limit of items under the skel directory. boxClient._useIterators = true; // You collaborate the user temporarily on the skel directory // to copy all items into that user's root folder. let collabSkelFolder; try { collabSkelFolder = await boxClient.collaborations.createWithUserID(userID, skelFolderId, boxClient.collaborationRoles.EDITOR); } catch (e) { // Handle that the collaboration on the skel folder could already exist. if (e.response.body.code === 'user_already_collaborator') { let collaborationsIterator = await boxClient.folders.getCollaborations(skelFolderId); let collaborations = await autoPage(collaborationsIterator); let results = collaborations.filter((collaboration) => { return collaboration.accessible_by.id === userID; }); console.log(results); if (results.length > 0) { collabSkelFolder = results[0]; } else { throw new Error("Couldn't create new collaboration or located existing collaboration."); } } else { throw e; } } console.log(collabSkelFolder); // Switching context to make calls on behalf of the user. // To access this user's root folder, the boxClient needs // to be scoped to make API calls as the user. boxClient.asUser(userID); // Iterate over all the items under the skel directory. let skelFolderItemsIterator = await boxClient.folders.getItems(skelFolderId); let skelFolderCollection = await autoPage(skelFolderItemsIterator); console.log(skelFolderCollection); // Now, as the user, copy the folders and files into // the user's root folder -- folder ID '0'. let copyTasks = []; skelFolderCollection.forEach((item) => { if (item.type === 'folder') { copyTasks.push(boxClient.folders.copy(item.id, '0') .catch((e) => { let itemId = handleConflictError(e); if (itemId) { console.log(itemId); return boxClient.folders.get(itemId); } else { throw e; } })); } else if (item.type === 'file') { copyTasks.push(boxClient.files.copy(item.id, '0') .catch((e) => { let itemId = handleConflictError(e); if (itemId) { console.log(itemId); return boxClient.files.get(itemId); } else { throw e; } })); } else { console.log("Unable to resolve item type to known types..."); } }); let copiedItems = await Promise.all(copyTasks); console.log(copiedItems); // Switching the boxClient context back to that of the service account. boxClient.asSelf(); /* Since the service account owns the skel directory, boxClient needs to make API calls as the service account to remove the temporary collaboration on the skel directory. */ try { await boxClient.collaborations.delete(collabSkelFolder.id); console.log("Removed collaboration on skel..."); } catch (e) { console.log("Couldn't remove skel collaboration..."); console.log(e.respose.body); } function handleConflictError(e) { if (e && e.response && e.response.body) { let errorBody = e.response.body; if (errorBody.status === 409) { if (errorBody.context_info && errorBody.context_info.conflicts && errorBody.context_info.conflicts) { let conflict = errorBody.context_info.conflicts; if (conflict && conflict.id) { return conflict.id; } } } } } function autoPage(iterator, collection = []) { let moveToNextItem = async () => { let item = await iterator.next(); if (item.value) { collection.push(item.value); } if (item.done !== true) { return moveToNextItem(); } else { return collection; } } return moveToNextItem(); } } ``` ``` package com.box; import java.io.BufferedReader; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collection; import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.box.sdk.BoxAPIException; import com.box.sdk.BoxCollaboration; import com.box.sdk.BoxConfig; import com.box.sdk.BoxDeveloperEditionAPIConnection; import com.box.sdk.BoxFile; import com.box.sdk.BoxFolder; import com.box.sdk.BoxItem; import com.box.sdk.BoxUser; import com.eclipsesource.json.JsonObject; public class BoxPlayground { public static void main(String[] args) throws Exception { Path configPath = Paths.get("config.json"); try (BufferedReader reader = Files.newBufferedReader(configPath, Charset.forName("UTF-8"))) { String skelFolderId = "45117847998"; String userId = "275111793"; BoxConfig boxConfig = BoxConfig.readFrom(reader); BoxDeveloperEditionAPIConnection serviceAccountClient = BoxDeveloperEditionAPIConnection .getAppEnterpriseConnection(boxConfig); BoxDeveloperEditionAPIConnection userClient = BoxDeveloperEditionAPIConnection.getAppUserConnection(userId, boxConfig); BoxFolder skelFolder = new BoxFolder(serviceAccountClient, skelFolderId); BoxCollaboration.Info skelFolderCollaboration; try { skelFolderCollaboration = skelFolder.collaborate(new BoxUser(serviceAccountClient, userId), BoxCollaboration.Role.EDITOR); } catch (BoxAPIException e) { System.out.println("Searching for existing collaborator."); JsonObject errorMessage = JsonObject.readFrom(e.getResponse()); String code = errorMessage.get("code").asString().intern(); if (code == "user_already_collaborator") { System.out.println("Already collaborated..."); Collection<BoxCollaboration.Info > collaborations = skelFolder.getCollaborations(); System.out.println(collaborations.size()); Optional<BoxCollaboration.Info > results = collaborations.stream().filter(c -> { return c.getAccessibleBy().getID().intern() == userId; }).findFirst(); if (results.isPresent()) { skelFolderCollaboration = results.get(); } else { throw new Exception("Couldn't create new collaboration or find existing collaboration."); } } else { throw e; } } System.out.println(skelFolderCollaboration.getID()); BoxFolder collabedSkelFolder = new BoxFolder(userClient, skelFolderId); ArrayList<BoxItem.Info > copiedItems = new ArrayList<>(); for (BoxItem.Info itemInfo: collabedSkelFolder) { if (itemInfo instanceof BoxFile.Info) { BoxFile.Info fileInfo = (BoxFile.Info) itemInfo; BoxFile copyFile = new BoxFile(userClient, fileInfo.getID()); BoxFile.Info copiedFile; try { copiedFile = copyFile.copy(BoxFolder.getRootFolder(userClient)); } catch (BoxAPIException e) { System.out.println(e.getMessage()); String conflictId = getIdFromConflict(e.getMessage()); System.out.println(conflictId); copiedFile = new BoxFile(userClient, conflictId).getInfo(); } copiedItems.add((BoxItem.Info) copiedFile); } else if (itemInfo instanceof BoxFolder.Info) { BoxFolder.Info folderInfo = (BoxFolder.Info) itemInfo; BoxFolder copyFolder = new BoxFolder(userClient, folderInfo.getID()); BoxFolder.Info copiedFolder; try { copiedFolder = copyFolder.copy(BoxFolder.getRootFolder(userClient)); } catch (BoxAPIException e) { System.out.println(e.getMessage()); String conflictId = getIdFromConflict(e.getMessage()); System.out.println(conflictId); copiedFolder = new BoxFolder(userClient, conflictId).getInfo(); } copiedItems.add((BoxItem.Info) copiedFolder); } } System.out.println("Copied " + copiedItems.size() + " items from the skel directory."); BoxCollaboration tempSkelCollab = new BoxCollaboration(serviceAccountClient, skelFolderCollaboration.getID()); tempSkelCollab.delete(); System.out.println("Removed temporary skel directory collaboration."); } } private static String getIdFromConflict(String message) { String id = ""; Pattern p = Pattern.compile("\"id\":\"[0-9]+\""); Pattern p2 = Pattern.compile("[0-9]+"); Matcher m = p.matcher(message); if (m.find()) { String sub = m.group(); Matcher m2 = p2.matcher(sub); if (m2.find()) { id = m2.group(); } } return id; } } ``` ``` using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Box.V2; using Box.V2.Config; using Box.V2.Exceptions; using Box.V2.JWTAuth; using Box.V2.Models; using Newtonsoft.Json.Linq; namespace BoxPlayground { public class Program { static void Main(string[] args) { ExecuteMainAsync().Wait(); } private static async Task ExecuteMainAsync() { using(FileStream fs = new FileStream("./config.json", FileMode.Open)) { var skelFolderId = "45117847998"; var userId = "275111793"; var session = new BoxJWTAuth(BoxConfig.CreateFromJsonFile(fs)); var client = session.AdminClient(session.AdminToken()); var userClient = session.UserClient(session.UserToken(userId), userId); BoxCollaboration collabSkelFolder; try { collabSkelFolder = await client.CollaborationsManager.AddCollaborationAsync( new BoxCollaborationRequest { AccessibleBy = new BoxCollaborationUserRequest { Id = userId }, Item = new BoxRequestEntity { Id = skelFolderId, Type = BoxType.folder }, Role = BoxCollaborationRole.Editor.ToString() }); } catch(BoxException e) { var errorMessage = JObject.Parse(e.Message); if (errorMessage.GetValue("code").ToString() == "user_already_collaborator") { System.Console.WriteLine("Already a collaborator"); var collaborations = await client.FoldersManager.GetCollaborationsAsync(skelFolderId); var existingCollab = collaborations.Entries.Find((collaboration) = >{ return collaboration.AccessibleBy.Id == userId; }); if (existingCollab != null) { collabSkelFolder = existingCollab; } else { throw new Exception("Couldn't create new collaboration or find existing collaboration"); } } else { throw e; } } var items = await userClient.FoldersManager.GetFolderItemsAsync(skelFolderId, limit: 1000, autoPaginate: true); var copyTasks = new List < Task < BoxItem >> (); items.Entries.ForEach((item) = >{ if (item.Type == BoxType.folder.ToString()) { copyTasks.Add(userClient.FoldersManager.CopyAsync(new BoxFolderRequest { Id = item.Id, Parent = new BoxRequestEntity { Id = "0" } }).ContinueWith((folder) = >{ try { return (BoxItem) folder.Result; } catch(Exception e) { var errorMessage = JObject.Parse(e.InnerException.Message); if (errorMessage.GetValue("status").ToObject < int > () == 409) { System.Console.WriteLine("Conflict found"); System.Console.WriteLine(errorMessage.SelectToken("context_info.conflicts.id")); return (BoxItem) userClient.FoldersManager.GetInformationAsync(errorMessage.SelectToken("context_info.conflicts.id").ToString()).Result; } else { throw e; } } })); } else if (item.Type == BoxType.file.ToString()) { copyTasks.Add(userClient.FilesManager.CopyAsync(new BoxFileRequest { Id = item.Id, Parent = new BoxRequestEntity { Id = "0" } }).ContinueWith((file) = >{ try { return (BoxItem) file.Result; } catch(Exception e) { var errorMessage = JObject.Parse(e.InnerException.Message); if (errorMessage.GetValue("status").ToObject < int > () == 409) { System.Console.WriteLine("Conflict found"); System.Console.WriteLine(errorMessage.SelectToken("context_info.conflicts.id")); return (BoxItem) userClient.FilesManager.GetInformationAsync(errorMessage.SelectToken("context_info.conflicts.id").ToString()).Result; } else { throw e; } } })); } else { System.Console.WriteLine("Couldn't process this item..."); } }); var copiedItems = await Task.WhenAll(copyTasks); System.Console.WriteLine($ "Copied {copiedItems.Count()} items from the skel directory."); if (await client.CollaborationsManager.RemoveCollaborationAsync(collabSkelFolder.Id)){ System.Console.WriteLine("Removed temporary skel directory collaboration..."); System.Console.WriteLine("Complete!"); } else { System.Console.WriteLine("Something went wrong when removing skel directory collaboration."); } } } } } ``` **Reference:** https://developer.box.com/guides/users/provision/populate-content/ --- ## Untitled *Type: guide | Category: Webhooks * Delete Webhooks V1 webhooks cannot be fully deleted. Instead, the webhook can be set back to Developer Mode by support. Developers can also… # Delete Webhooks V1 webhooks cannot be fully deleted. Instead, the webhook can be set back to Developer Mode by [support](https://support.box.com/hc/en-us/requests/new). Developers can also remove the application from their account by revisiting the enablement URL and clicking **Remove**. **Reference:** https://developer.box.com/guides/webhooks/v1/delete-v1/ --- ## Untitled *Type: guide | Category: Webhooks * Create Webhooks V1 webhooks are created in the Developer Console by following the steps below. Navigate to your application in the Developer… # Create Webhooks V1 webhooks are created in the [Developer Console](https://app.box.com/developers/console) by following the steps below. 1. Navigate to your application in the [Developer Console](https://app.box.com/developers/console) 2. Select the **Webhooks** tab. 3. Click the **Create a new Webhook** button. 4. Fill in the form, including event triggers, an endpoint URL and one or more callback parameters. 5. Click **Save Webhook**. # Callback parameters Unlike the V2 Webhooks, these manual webhooks need to be configured with the data you'd like. This data will be sent as a query string either in the body or as a query parameter, for example `name=Contract.pdf&type=file`. ## Developer Mode By default V1 webhooks only work for users that are listed as application collaborators in the **General Settings** tab in the Developer Console. To enable a webhooks for all users, please [contact support](https://support.box.com). ## Enabling a webhook After creating a webhook, the application must be added to the user's account to begin use. To obtain the URL to add the app, follow the directions below for OAuth 2.0 authentication apps: 1. Navigate to the **Integrations** tab for the application in the [Developer Console](https://app.box.com/developers/console). 2. Click **Submit My App**. Do not worry, you will not be completing the submission process! 3. At the bottom of the page, click **Preview**. 4. Click **Add** For all other authentication types, you will need to contact support to obtain this URL. Webhooks will now trigger for any configured events that are occur in the user's account. **Reference:** https://developer.box.com/guides/webhooks/v1/create-v1/ --- ## Untitled *Type: guide | Category: Users * Setup Shared Folders As a final step to manage access to shared folders, we'll create the folder structures needed within the service… # Setup Shared Folders As a final step to manage access to shared folders, we'll create the folder structures needed within the service account. Then, groups will map to the needed permissions based on user types and level of access needed to those folders. We'll use a Market Department as an example. ``` { "name": "Marketing Department", "parent": { "id": "0" }, "children": [ { "name": "Projects", "children": [] }, { "name": "Newsletter", "children": [ { "name": "Drafts", "children": [] } ] } ] } ``` Working from this sample folder structure, we can use the folder tree creator code we used earlier to create the `etc/skel` structure. That code may be modified to make your own structure. Once created, we'll need the IDs of the folders that each group will need to access. For example, Marketing managers will likely have `editor` access to all folders within the Marketing Department. On the other hand, Marketing project managers will likely need `editor` access to only the `Projects` folder. We'll create two groups and give them these permissions. ``` "use strict"; const fs = require("fs"); const box = require("box-node-sdk"); let configFile = fs.readFileSync("config.json"); configFile = JSON.parse(configFile); let session = box.getPreconfiguredInstance(configFile); let serviceAccountClient = session.getAppAuthClient("enterprise"); const marketingDeptFolderID = "45765309069"; const marketingProjectsFolderID = "45765461670"; const marketingManagersGroupName = "Marketing Managers"; const marketingProjectManagersGroupName = "Marketing Project Managers"; (async () => { let marketingManagerGroup; try { marketingManagerGroup = await serviceAccountClient.groups.create( marketingManagersGroupName, { description: "For Marketing department leadership team.", invitability_level: "admins_only", member_viewability_level: "admins_only" } ); } catch (e) { marketingManagerGroup = await handleGroupConflictError( e, marketingManagersGroupName, serviceAccountClient ); } console.log(marketingManagerGroup); let marketingProjectManagerGroup; try { marketingProjectManagerGroup = await serviceAccountClient.groups.create( marketingProjectManagersGroupName, { description: "All team members who manage Marketing projects.", invitability_level: "admins_and_members", member_viewability_level: "admins_and_members" } ); } catch (e) { marketingProjectManagerGroup = await handleGroupConflictError( e, marketingProjectManagersGroupName, serviceAccountClient ); } console.log(marketingProjectManagerGroup); let collabMarketingManagers; try { collabMarketingManagers = await serviceAccountClient.collaborations.createWithGroupID( marketingManagerGroup.id, marketingDeptFolderID, serviceAccountClient.collaborationRoles.EDITOR ); } catch (e) { collabMarketingManagers = await handleFolderCollaborationConflictError( e, marketingDeptFolderID, marketingManagerGroup.id, serviceAccountClient ); } console.log(collabMarketingManagers); let collabMarketingProjectManagers; try { collabMarketingProjectManagers = await serviceAccountClient.collaborations.createWithGroupID( marketingProjectManagerGroup.id, marketingProjectsFolderID, serviceAccountClient.collaborationRoles.EDITOR ); } catch (e) { collabMarketingProjectManagers = await handleFolderCollaborationConflictError( e, marketingProjectsFolderID, marketingProjectManagerGroup.id, serviceAccountClient ); } console.log(collabMarketingProjectManagers); })(); async function autoPage(iterator, collection = []) { let moveToNextItem = async () => { let item = await iterator.next(); if (item.value) { collection.push(item.value); } if (item.done !== true) { return moveToNextItem(); } else { return collection; } }; return moveToNextItem(); } async function handleGroupConflictError(e, groupName, boxClient) { let storeIteratorSetting = boxClient._useIterators; if (e && e.response && e.response.body && e.response.body.status === 409) { boxClient._useIterators = true; let groupsIterator = await boxClient.groups.getAll({ name: groupName }); let groups = await autoPage(groupsIterator); let results = groups.filter(group => { return group.name === groupName; }); if (results.length > 0) { boxClient._useIterators = storeIteratorSetting; return results[0]; } else { throw new Error("Couldn't create group or find existing group."); } } else { throw e; } } async function handleFolderCollaborationConflictError( e, folderID, groupID, boxClient ) { let storeIteratorSetting = boxClient._useIterators; if (e && e.response && e.response.body && e.response.body.status === 409) { boxClient._useIterators = true; let collaborationsIterator = await boxClient.folders.getCollaborations( folderID ); let collaborations = await autoPage(collaborationsIterator); let results = collaborations.filter(collaboration => { return collaboration.accessible_by.id === groupID; }); console.log(results); if (results.length > 0) { boxClient._useIterators = storeIteratorSetting; return results[0]; } else { throw new Error( "Couldn't create new collaboration or located existing collaboration." ); } } else { throw e; } } ``` ``` package com.box; import java.io.BufferedReader; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collection; import java.util.Optional; import com.box.sdk.BoxAPIException; import com.box.sdk.BoxCollaboration; import com.box.sdk.BoxConfig; import com.box.sdk.BoxDeveloperEditionAPIConnection; import com.box.sdk.BoxFolder; import com.box.sdk.BoxGroup; import com.eclipsesource.json.JsonObject; public class BoxPlayground { public static void main(String[] args) throws Exception { Path configPath = Paths.get("config.json"); try (BufferedReader reader = Files.newBufferedReader(configPath, Charset.forName("UTF-8"))) { String marketingDeptFolderID = "45765309069"; String marketingProjectsFolderID = "45765461670"; String marketingManagersGroupName = "Marketing Managers"; String marketingProjectManagersGroupName = "Marketing Project Managers"; BoxConfig boxConfig = BoxConfig.readFrom(reader); BoxDeveloperEditionAPIConnection serviceAccountClient = BoxDeveloperEditionAPIConnection .getAppEnterpriseConnection(boxConfig); BoxGroup.Info marketingManagerGroup; try { marketingManagerGroup = BoxGroup.createGroup(serviceAccountClient, marketingManagersGroupName, null, null, "For Marketing department leadership team.", "admins_only", "admins_only"); } catch (BoxAPIException e) { JsonObject errorMessage = JsonObject.readFrom(e.getResponse()); int status = errorMessage.get("status").asInt(); if (status == 409) { marketingManagerGroup = handleGroupConflictError(marketingManagersGroupName, serviceAccountClient); } else { throw e; } } System.out.println(marketingManagerGroup.getID()); BoxGroup.Info marketingProjectManagerGroup; try { marketingProjectManagerGroup = BoxGroup.createGroup(serviceAccountClient, marketingProjectManagersGroupName, null, null, "All team members who manage Marketing projects.", "admins_and_members", "admins_and_members"); } catch (BoxAPIException e) { JsonObject errorMessage = JsonObject.readFrom(e.getResponse()); int status = errorMessage.get("status").asInt(); if (status == 409) { marketingProjectManagerGroup = handleGroupConflictError(marketingProjectManagersGroupName, serviceAccountClient); } else { throw e; } } System.out.println(marketingProjectManagerGroup.getID()); BoxFolder marketDeptFolder = new BoxFolder(serviceAccountClient, marketingDeptFolderID); BoxCollaboration.Info marketingDeptFolderCollaboration; try { marketingDeptFolderCollaboration = marketDeptFolder.collaborate( new BoxGroup(serviceAccountClient, marketingManagerGroup.getID()), BoxCollaboration.Role.EDITOR); } catch (BoxAPIException e) { System.out.println("Searching for existing collaborator."); JsonObject errorMessage = JsonObject.readFrom(e.getResponse()); int status = errorMessage.get("status").asInt(); if (status == 409) { marketingDeptFolderCollaboration = handleFolderCollaborationConflict(marketDeptFolder, marketingManagerGroup.getID()); } else { throw e; } } System.out.println(marketingDeptFolderCollaboration.getID()); BoxFolder marketingProjectsFolder = new BoxFolder(serviceAccountClient, marketingProjectsFolderID); BoxCollaboration.Info marketingProjectsFolderCollaboration; try { marketingProjectsFolderCollaboration = marketingProjectsFolder.collaborate( new BoxGroup(serviceAccountClient, marketingProjectManagerGroup.getID()), BoxCollaboration.Role.EDITOR); } catch (BoxAPIException e) { System.out.println("Searching for existing collaborator."); JsonObject errorMessage = JsonObject.readFrom(e.getResponse()); int status = errorMessage.get("status").asInt(); if (status == 409) { marketingProjectsFolderCollaboration = handleFolderCollaborationConflict(marketingProjectsFolder, marketingProjectManagerGroup.getID()); } else { throw e; } } System.out.println(marketingProjectsFolderCollaboration.getID()); } } private static BoxCollaboration.Info handleFolderCollaborationConflict(BoxFolder folder, String groupID) throws Exception { System.out.println("Already collaborated..."); Collection<BoxCollaboration.Info > collaborations = folder.getCollaborations(); Optional<BoxCollaboration.Info > results = collaborations.stream().filter(c -> { return c.getAccessibleBy().getID().intern() == groupID.intern(); }).findFirst(); if (results.isPresent()) { return results.get(); } else { throw new Exception("Couldn't create new collaboration or find existing collaboration."); } } private static BoxGroup.Info handleGroupConflictError(String groupName, BoxDeveloperEditionAPIConnection boxClient) throws Exception { Iterable<BoxGroup.Info > groups = BoxGroup.getAllGroupsByName(boxClient, groupName); BoxGroup.Info foundGroup = null; for (BoxGroup.Info group: groups) { if (group.getName().intern() == groupName) { foundGroup = group; break; } } if (foundGroup != null) { return foundGroup; } else { throw new Exception("Couldn't create group or find existing group."); } } } ``` ``` using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Box.V2; using Box.V2.Config; using Box.V2.Exceptions; using Box.V2.JWTAuth; using Box.V2.Models; using Box.V2.Models.Request; using Newtonsoft.Json.Linq; namespace BoxPlayground { public class Program { static void Main(string[] args) { ExecuteMainAsync().Wait(); } private static async Task ExecuteMainAsync() { using(FileStream fs = new FileStream("./config.json", FileMode.Open)) { var marketingDeptFolderId = "45765309069"; var marketingProjectsFolderId = "45765461670"; var marketingManagersGroupName = "Marketing Managers"; var marketingProjectManagersGroupName = "Marketing Project Managers"; var session = new BoxJWTAuth(BoxConfig.CreateFromJsonFile(fs)); var serviceAccountClient = session.AdminClient(session.AdminToken()); BoxGroup marketingManagerGroup; try { marketingManagerGroup = await serviceAccountClient.GroupsManager.CreateAsync(new BoxGroupRequest { Name = marketingManagersGroupName, InvitabilityLevel = "admins_only", MemberViewabilityLevel = "admins_only" }); } catch(BoxException e) { var errorMessage = JObject.Parse(e.Message); if (errorMessage.GetValue("status").ToObject < int > () == 409) { marketingManagerGroup = await HandleGroupConflictError(marketingManagersGroupName, serviceAccountClient); } else { throw e; } } System.Console.WriteLine(marketingManagerGroup.Id); BoxGroup marketingProjectsManagerGroup; try { marketingProjectsManagerGroup = await serviceAccountClient.GroupsManager.CreateAsync(new BoxGroupRequest { Name = marketingProjectManagersGroupName, InvitabilityLevel = "admins_and_members", MemberViewabilityLevel = "admins_and_members" }); } catch(BoxException e) { var errorMessage = JObject.Parse(e.Message); if (errorMessage.GetValue("status").ToObject < int > () == 409) { marketingProjectsManagerGroup = await HandleGroupConflictError(marketingProjectManagersGroupName, serviceAccountClient); } else { throw e; } } System.Console.WriteLine(marketingProjectsManagerGroup.Id); BoxCollaboration marketingManagerCollab; try { marketingManagerCollab = await serviceAccountClient.CollaborationsManager.AddCollaborationAsync( new BoxCollaborationRequest { AccessibleBy = new BoxCollaborationUserRequest { Id = marketingManagerGroup.Id, Type = BoxType.group }, Item = new BoxRequestEntity { Id = marketingDeptFolderId, Type = BoxType.folder }, Role = BoxCollaborationRole.Editor.ToString() }); } catch(BoxException e) { var errorMessage = JObject.Parse(e.Message); if (errorMessage.GetValue("status").ToObject < int > () == 409) { marketingManagerCollab = await HandleFolderCollaborationConflictError(marketingDeptFolderId, marketingManagerGroup.Id, serviceAccountClient); } else { throw e; } } System.Console.WriteLine(marketingManagerCollab.Id); BoxCollaboration marketingProjectsManagerCollab; try { marketingProjectsManagerCollab = await serviceAccountClient.CollaborationsManager.AddCollaborationAsync( new BoxCollaborationRequest { AccessibleBy = new BoxCollaborationUserRequest { Id = marketingProjectsManagerGroup.Id, Type = BoxType.group }, Item = new BoxRequestEntity { Id = marketingProjectsFolderId, Type = BoxType.folder }, Role = BoxCollaborationRole.Editor.ToString() }); } catch(BoxException e) { var errorMessage = JObject.Parse(e.Message); if (errorMessage.GetValue("status").ToObject < int > () == 409) { marketingProjectsManagerCollab = await HandleFolderCollaborationConflictError(marketingProjectsFolderId, marketingProjectsManagerGroup.Id, serviceAccountClient); } else { throw e; } } System.Console.WriteLine(marketingProjectsManagerCollab.Id); } } public async static Task < BoxGroup > HandleGroupConflictError(string groupName, BoxClient boxClient) { System.Console.WriteLine("Found conflict."); var groups = await boxClient.GroupsManager.GetAllGroupsAsync(autoPaginate: true); return groups.Entries.Find((group) = >{ return group.Name == groupName; }); } public async static Task < BoxCollaboration > HandleFolderCollaborationConflictError(string folderId, string groupId, BoxClient boxClient) { System.Console.WriteLine("Already a collaborator"); var collaborations = await boxClient.FoldersManager.GetCollaborationsAsync(folderId); var existingCollab = collaborations.Entries.Find((collaboration) = >{ return collaboration.AccessibleBy.Id == groupId; }); if (existingCollab != null) { return existingCollab; } else { throw new Exception("Couldn't create new collaboration or find existing collaboration"); } } } } ``` ``` box groups:create "Marketing Managers" --invite=admins_only --view-members=admins_only box groups:create "Marketing Project Managers" --invite=admins_and_members --view-members=admins_and_members ``` Once the groups are created, add the user to that group and they will have the prescribed access to the shared folders created within the service account. ``` 'use strict' const fs = require('fs'); const box = require('box-node-sdk'); let configFile = fs.readFileSync('config.json'); configFile = JSON.parse(configFile); let session = box.getPreconfiguredInstance(configFile); let serviceAccountClient = session.getAppAuthClient("enterprise"); const marketingManagerGroupID = "839790214"; const marketingManagerUserID = "275111793"; (async () => { let addedUser; try { addedUser = await serviceAccountClient.groups.addUser(marketingManagerGroupID, marketingManagerUserID, { role: serviceAccountClient.groups.userRoles.ADMIN }); } catch (e) { addedUser = await handleGroupMembershipConflictError(e, marketingManagerGroupID, marketingManagerUserID, serviceAccountClient); } console.log(addedUser); })(); async function autoPage(iterator, collection = []) { let moveToNextItem = async () => { let item = await iterator.next(); if (item.value) { collection.push(item.value); } if (item.done !== true) { return moveToNextItem(); } else { return collection; } } return moveToNextItem(); } async function handleGroupMembershipConflictError(e, groupID, userID, boxClient) { let storeIteratorSetting = boxClient._useIterators; if (e && e.response && e.response.body && e.response.body.status === 409) { boxClient._useIterators = true; let groupMembershipsIterator = await boxClient.groups.getMemberships(groupID); let groupMemberships = await autoPage(groupMembershipsIterator); let results = groupMemberships.filter((groupMembership) => { return groupMembership.user.id === userID; }); if (results.length > 0) { boxClient._useIterators = storeIteratorSetting; return results[0]; } else { throw new Error("Couldn't create group membership or find existing group membership."); } } else { throw e; } } ``` ``` package com.box; import java.io.BufferedReader; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import com.box.sdk.BoxAPIException; import com.box.sdk.BoxConfig; import com.box.sdk.BoxDeveloperEditionAPIConnection; import com.box.sdk.BoxGroup; import com.box.sdk.BoxGroupMembership; import com.box.sdk.BoxUser; import com.box.sdk.BoxGroupMembership.Role; import com.eclipsesource.json.JsonObject; public class BoxPlayground { public static void main(String[] args) throws Exception { Path configPath = Paths.get("config.json"); try (BufferedReader reader = Files.newBufferedReader(configPath, Charset.forName("UTF-8"))) { String marketingManagerGroupID = "839982796"; String marketingManagerUserID = "275111793"; BoxConfig boxConfig = BoxConfig.readFrom(reader); BoxDeveloperEditionAPIConnection serviceAccountClient = BoxDeveloperEditionAPIConnection .getAppEnterpriseConnection(boxConfig); BoxGroupMembership.Info marketingManagerMembership = null; BoxGroup marketingManagerGroup = new BoxGroup(serviceAccountClient, marketingManagerGroupID); try { marketingManagerMembership = marketingManagerGroup .addMembership(new BoxUser(serviceAccountClient, marketingManagerUserID), Role.ADMIN); } catch (BoxAPIException e) { JsonObject errorMessage = JsonObject.readFrom(e.getResponse()); int status = errorMessage.get("status").asInt(); if (status == 409) { System.out.println("Found existing membership"); Iterable<BoxGroupMembership.Info > memberships = marketingManagerGroup.getAllMemberships(); for (BoxGroupMembership.Info membership: memberships) { if (membership.getUser().getID().intern() == marketingManagerUserID) { marketingManagerMembership = membership; break; } } if (marketingManagerMembership == null) { throw new Exception("Couldn't add user to group or find existing membership"); } } else { throw e; } } System.out.println(marketingManagerMembership.getID()); System.out.println(marketingManagerMembership.getRole()); } } } ``` ``` using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Box.V2; using Box.V2.Config; using Box.V2.Exceptions; using Box.V2.JWTAuth; using Box.V2.Models; using Box.V2.Models.Request; using Newtonsoft.Json.Linq; namespace BoxPlayground { public class Program { static void Main(string[] args) { ExecuteMainAsync().Wait(); } private static async Task ExecuteMainAsync() { using(FileStream fs = new FileStream("./config.json", FileMode.Open)) { var marketingManagerGroupId = "839982796"; var marketingManagerUserId = "275111793"; var session = new BoxJWTAuth(BoxConfig.CreateFromJsonFile(fs)); var serviceAccountClient = session.AdminClient(session.AdminToken()); BoxGroupMembership marketingManagerMembership; try { marketingManagerMembership = await serviceAccountClient.GroupsManager.AddMemberToGroupAsync(new BoxGroupMembershipRequest { User = new BoxRequestEntity { Id = marketingManagerUserId }, Group = new BoxGroupRequest { Id = marketingManagerGroupId }, Role = "admin" }); } catch(BoxException e) { var errorMessage = JObject.Parse(e.Message); if (errorMessage.GetValue("status").ToObject < int > () == 409) { var groups = await serviceAccountClient.GroupsManager.GetAllGroupMembershipsForGroupAsync(marketingManagerGroupId, autoPaginate: true); marketingManagerMembership = groups.Entries.Find((group) = >{ return group.User.Id == marketingManagerUserId; }); if (marketingManagerMembership == null) { throw new Exception("Couldn't create new collaboration or find existing collaboration"); } } else { throw e; } } System.Console.WriteLine(marketingManagerMembership.Id); } } } } ``` ``` box groups:membership:add $user_id $group_id --role=admin ``` **Reference:** https://developer.box.com/guides/users/provision/shared-folders/ --- ## Untitled *Type: guide | Category: Webhooks * V1 Webhooks Webhooks created using the Developer Console monitor changes to all files and folders within a user's account. When creating one… # V1 Webhooks Webhooks created using the [Developer Console](https://app.box.com/developers/console) monitor changes to all files and folders within a user's account. When creating one of these webhooks it is not possible specify a specific object to bind the webhook to. To create a webhook for a specific file or folder, you will need to leverage [v2 webhooks](g://webhooks/v2). Webhooks created through this process will not show when listing all webhooks for a user via API call. All V1 webhooks are visible in the **Webhooks** tab in the [Developer Console](https://app.box.com/developers/console). **Reference:** https://developer.box.com/guides/webhooks/v1/ --- ## Untitled *Type: guide | Category: Webhooks * V2 Webhooks Flow When an event triggers a webhook for a file or a folder, it makes a HTTP call to the address specified when the webhook… # V2 Webhooks ## Flow When an event triggers a webhook for a file or a folder, it makes a HTTP call to the `address` specified when the webhook was created. The payload of this call contains some request headers, and a JSON body. ## Payload headers The payload sent by a webhook has the following Box-specific headers. | Header | Description | | --- | --- | | BOX-DELIVERY-ID | A unique ID assigned by Box that identifies the delivered webhook payload. When Box retries a webhook this ID will change, while the ID in the payload body remains the same. | | BOX-DELIVERY-TIMESTAMP | An RFC-3339 timestamp that identifies when the payload was sent. | | BOX-SIGNATURE-PRIMARY | A signature created using the primary signature key configured for this webhook. | | BOX-SIGNATURE-SECONDARY | A signature created using the secondary signature key configured for this webhook. | | BOX-SIGNATURE-VERSION | Value is always 1. | | BOX-SIGNATURE-ALGORITHM | Value is always HmacSHA256 . | For example: ``` BOX-DELIVERY-ID: 673a081b-bb4b-4d45-b4f1-4131a29c1d07 BOX-DELIVERY-TIMESTAMP: 2016-07-11T10:10:33-07:00 BOX-SIGNATURE-PRIMARY: isCeDp7mLR41/MjcSEFLag9bWmpJkgmN80Je4VIESdo= BOX-SIGNATURE-SECONDARY: 1UbiiKS7/2o5vNIlyMh7e5QGCHq8lflWFgEF+YWBugI= BOX-SIGNATURE-VERSION: 1 BOX-SIGNATURE-ALGORITHM: HmacSHA256 USER-AGENT: Box-WH-Client/0.1 ``` We recommend [setting up](g://webhooks/v2/signatures-v2) and [verifying signatures](g://webhooks/v2/signatures-v2) of the webhook payloads. HTTP header names are case insensitive. Your client should convert all header names to a standardized lowercase or uppercase format before trying to determine the value of a header. ## Payload body The body of a webhook payload is a JSON object that describes the file or folder (target) that triggered the webhook, as well as the event that has been triggered. | Field | Description | | --- | --- | | type | Value is always webhook_event. | | id | A unique ID assigned by Box that identifies an event. When Box retries a webhook this ID will not change, while the ID in the header changes between calls. | | created_at | The time/date when an event was triggered at. | | trigger | The name of the action that triggered an event, for example FILE.UPLOADED. | | webhook | The webhook ID for which an event triggered. | | created_by | The user that triggered an event. | | source | The item that triggered an event, for example the file that was uploaded to the target folder. | Example: ``` { "type": "webhook_event", "id": "eb0c4e06-751f-442c-86f8-fd5bb404dbec", "created_at": "2016-07-11T10:10:32-07:00", "trigger": "FILE.UPLOADED", "webhook": { "id": "53", "type": "webhook" }, "created_by": { "type": "user", "id": "226067247", "name": "John Q. Developer", "login": "johnq@dev.name" }, "source": { "id": "73835521473", "type": "file", "file_version": { "type": "file_version", "id": "78096737033", "sha1": "2c61623e86bee78e6ab444af456bccc7a1164095" }, "sequence_id": "0", "etag": "0", "sha1": "2c61623e86bee78e6ab444af456bccc7a1164095", "name": "Test-Image-3.png", "description": "", "size": 26458, "path_collection": { "total_count": 4, "entries": [ { "type": "folder", "id": "0", "sequence_id": null, "etag": null, "name": "All Files" }, { "type": "folder", "id": "2614853901", "sequence_id": "4", "etag": "4", "name": "Testing" }, { "type": "folder", "id": "8290186265", "sequence_id": "0", "etag": "0", "name": "Webhooks Base" }, { "type": "folder", "id": "8290188973", "sequence_id": "0", "etag": "0", "name": "Webhooks" } ] }, "created_at": "2016-07-11T10:10:32-07:00", "modified_at": "2016-07-11T10:10:32-07:00", "trashed_at": null, "purged_at": null, "content_created_at": "2016-06-08T11:14:04-07:00", "content_modified_at": "2016-06-08T11:14:04-07:00", "created_by": { "type": "user", "id": "226067247", "name": "John Q. Developer", "login": "johnq@dev.name" }, "modified_by": { "type": "user", "id": "226067247", "name": "John Q. Developer", "login": "johnq@dev.name" }, "owned_by": { "type": "user", "id": "226067247", "name": "John Q. Developer", "login": "johnq@dev.name" }, "shared_link": null, "parent": { "type": "folder", "id": "8290188973", "sequence_id": "0", "etag": "0", "name": "Webhooks" }, "item_status": "active" }, "additional_info": [] } ``` ## Retries Delivery of a webhook payload fails when Box does not receive a response with a HTTP status code in the `200` to `299` range within 30 seconds of sending the payload. Box will retry webhook deliveries up to 12 times over a period of 2 hours. These numbers could be subject to change. **Reference:** https://developer.box.com/guides/webhooks/v2/ --- ## Untitled *Type: guide | Category: Webhooks * Delete Webhooks You can delete a webhook using the Developer Console or API. Developer Console To delete a webhook follow the steps below… # Delete Webhooks You can delete a webhook using the [Developer Console](https://app.box.com/developers/console) or API. ## Developer Console To delete a webhook follow the steps below. 1. Navigate to the **Webhooks** tab in the [Developer Console](https://app.box.com/developers/console). 2. Select the webhook you want to delete by clicking on its ID. 3. Click the **Delete** button. 4. Confirm the action by clicking **Delete** under the warning message. ## API To remove a webhook from a file or folder, you need to use the [remove webhook endpoint](e://delete-webhooks-id) with the ID of the webhook. You can get this value using the [list all webhooks endpoint](e://get-webhooks). ## Automatic webhook deletion Using [this](e://delete-webhooks-id) endpoint is not the only way a webhook can be deleted. The following reasons can cause webhooks to be deleted. Deleting a Box application automatically deletes all webhooks associated with it. Deleting all active Access Tokens associated with a webhook automatically deletes the webhook. This includes Developer Tokens and password. The last successful notification was delivered 30 days ago to the set URL and the period between the last successful notification delivery and the last user trigger event date exceeds 14 days. Let's go through a scenario in which the user downloads a file. This action triggers the webhook to use the set URL to delete a shared link. The diagram illustrates this scenario, showing when the webhook will be deleted. - **User event trigger**: when the user initiated the event, for example downloaded a file. - **Notification trigger**: when the notification was sent to the webhook, saying that the file was downloaded. - **Last notification delivery**: when the webhook sent a message to a specific URL, for example to delete a shared link. In all of these cases Box sends a webhook payload with the `WEBHOOK.DELETED` event name to the notification URL. The body of the payload includes the following additional information. ``` "additional_info": { "reason": "auto_cleanup" } ``` **Reference:** https://developer.box.com/guides/webhooks/v2/delete-v2/ --- ## Untitled *Type: guide | Category: Webhooks * Limitations One webhook per item There's a limit of one webhook for each item (file or folder), each application and each authenticated user… # Limitations ## One webhook per item There's a limit of one webhook for each item (file or folder), each application and each authenticated user. Once a webhook is attached to an item, no second webhook can be attached, even if the second webhook would respond to a different trigger event. Example: a webhook is set up by `John Doe` to watch `FILE.UPLOADED` events in a folder with the name `Junk`, for an application named `CleanupApp`. At that point, no second webhook can be added to the `Junk` folder by the `CleanupApp` by `John Doe`, even if it is to trigger for an `FILE.DOWNLOADED` event. To listen to another event, [update](g://webhooks/v2/update-v2) the existing webhook or create a new application. ## 1000 webhooks limit There is a limit of 1000 webhooks for each application and each user. To create more webhooks for a user, create another application or [update existing webhooks](g://webhooks/v2/update-v2) to apply to higher levels in the folder tree. ## Notification URL restrictions The notification URL or `address` for a webhook must be a valid HTTPS URL that resolves to a valid IP address. It needs to have a certificate signed by a reputable certificate authority. Box does not support self-signed SSL certificates. The IP address of the server must be publicly accessible from the internet and cannot be a `(*.)box.com` address. The port used in the URL must be the standard HTTPS port (`443`). Notifications will not be delivered to other ports. The supported TLS protocol versions are `TLS 1.2` and `TLS 1.3` with FIPS-compliant cipher suites. ## No webhooks on root folder V2 webhooks cannot be created on the root folder, which is the folder with ID `0`. Instead, you will need to use a [v1 webhook](g://webhooks/v1). When the permissions on an item prevent an action from occurring, no notification is sent for the attempted action. ## NO_ACTIVE_SESSION is set in the webhook payload If the auth session (access token) for the app you used to create a webhook expires, that webhook no longer sends events with a full payload. In that case, the event trigger is `NO_ACTIVE_SESSION`. ### JWT Auth For webhooks created with the JWT Auth app, the session expires when you delete the app authorization for this app in the Admin Console. For more information, see [application authorization guide](https://support.box.com/hc/en-us/articles/360043697014-Authorizing-Apps-in-the-Box-App-Approval-Process). ### OAuth 2.0 For webhooks created with OAuth 2.0 Auth app, the session expires when both the access token and the refresh token for the user and app used for creating that webhook expire. ### Developer token As the developer token cannot be refreshed and expires after 1 hour, the event trigger `NO_ACTIVE_SESSION` is set in the webhook payload after 1 hour. ## Reasons for webhook deletion The following reasons can cause webhooks to be deleted. 1. Deleting a Box application automatically deletes all webhooks associated with it. 2. Deleting all active Access Tokens associated with a webhook automatically deletes the webhook. This includes Developer Tokens and password. 3. A webhook is automatically deleted if the last successful delivery was 30 days ago and the period between the last successful delivery and the last trigger date is more than 14 days. In all of these cases Box sends a webhook payload with the `WEBHOOK.DELETED` event name to the notification URL. The body of the payload includes the following additional information. ``` "additional_info": { "reason": "auto_cleanup" } ``` **Reference:** https://developer.box.com/guides/webhooks/v2/limitations-v2/ --- ## Untitled *Type: guide | Category: Webhooks * Create Webhooks V2 webhooks can monitor specific files or folders. They can be created in the Developer Console and with API. Developer… # Create Webhooks V2 webhooks can monitor specific files or folders. They can be created in the [Developer Console](https://app.box.com/developers/console) and with API. ## Developer console V2 webhooks can be created only when the scope **Manage Webhooks** is selected and the application is authorized. See more about [Required Access Scopes](g://applications) and [authorization](g://authorization). To create a webhook follow the steps below. 1. Navigate to your application in the [Developer Console](https://app.box.com/developers/console). 2. Select the **Webhooks** tab. 3. Click the **Create webhook** button. 4. Select **V2** from the drop-down list. 5. Fill in the form. 6. Click **Create webhook** button to save your changes. ### Required fields | Field name | Description | Required | | --- | --- | --- | | URL Address | URL address to be notified by the webhook. | Yes | | Content type | Type of content (file/folder) the webhook is configured for. | Yes | | Triggers | Different triggers that activate the webhook. | Yes | ## API This API requires the application to have the **Manage Webhooks** scope enabled. To attach a webhook to a file, call the [create webhook](e://post_webhooks) endpoint with the type of `file`, the ID of the file, a URL to send webhook notifications to, and a list of [triggers](g://webhooks/triggers). To attach a webhook to a folder, call the [create webhook](e://post_webhooks) endpoint with the type of `folder`, the ID of the folder, a URL to send webhook notifications to, and a list of [triggers](g://webhooks/triggers). Webhooks do cascade, so if a webhook is set on a parent folder, it will also monitor sub-folders for the selected triggers. ## Ownership It is best practice and strongly recommended to create webhooks with a [Service Account](page://platform/user-types/#service-account), or user that will not be deleted, to avoid potential issues with webhook delivery due to loss of access to content. Similar to files and folders, webhooks are owned by a user. If a user who owns a webhook is deleted, they will lose access to all files and folders that they previously had access to. Their webhooks will begin to fail validation, but the webhook service will continue to send events and require retries. ## Webhook address The notification URL specified in the `address` parameter must be a valid URL that you specify when you create a webhook. Every time one of the triggers is activated, this URL is called. The notification URL must use standard port `443` and should return an HTTP status in the range of `200` to `299` within 30 seconds of receiving the webhook payload. ## Webhook triggers The triggers are a list of strings that specify the events which cause the webhook to fire. For example, if you want the webhook to be triggered when a user uploads a file, use `FILE.UPLOADED`. You can find a list of available triggers [in this guide](g://webhooks/triggers). **Reference:** https://developer.box.com/guides/webhooks/v2/create-v2/ --- ## Untitled *Type: guide | Category: Webhooks * List Webhooks for a User To fetch all webhooks for the authenticated user, use the list all webhooks endpoint. This endpoint requires the… # List Webhooks for a User To fetch all webhooks for the authenticated user, use the [list all webhooks](endpoint://get_webhooks) endpoint. This endpoint requires the application to have the **Manage Webhooks** scope enabled. This API call will only list the webhooks for the authenticated user, not for any other users in the enterprise. **Reference:** https://developer.box.com/guides/webhooks/v2/list-v2/ --- ## Untitled *Type: guide | Category: Webhooks * Signature Verification Webhook signatures help ensure that a webhook payload was sent by Box and was not tampered with. Signatures greatly… # Signature Verification Webhook signatures help ensure that a webhook payload was sent by Box and was not tampered with. Signatures greatly reduce the likelihood of a successful man-in-the-middle or replay attacks. When signatures are configured, Box generates a cryptographic digest of the notification's body and attaches it to the header of the webhook payload. When your application receives the payload, verify the signatures by calculating the same digest and comparing it to the one received. If the digests do not match, the payload should not be trusted. You can achieve an extra level of protection by frequently changing the signature keys. To enable a smooth transition between the old and new keys, Box supports two simultaneous signature keys. ## Signature configuration In order to attach signatures to an application's notifications, you must first generate signature keys for the application. To configure your application's keys follow the steps below. 1. Navigate to the application in the developer console. 2. Click on the **Webhooks** tab. 3. Click the **Manage signature keys** button. 4. Click the **Generate Key** button to configure your keys. Once generating the primary or secondary key, copy the value. You will need it to verify the webhook payloads. Every webhook will now include a `BOX-SIGNATURE-PRIMARY` and `BOX-SIGNATURE-SECONDARY` header payload. ## Signature verification with SDKs Although it is possible to verify signatures manually, methods are provided for your convenience in the [official Box SDKs](g://tooling/sdks). ## Manual signature verification The following steps describe the basics of how to verify a signature. ### Timestamp validation Check if the timestamp in the `BOX-DELIVERY-TIMESTAMP` header of the payload is not older than ten minutes. ``` var timestamp = headers['BOX-DELIVERY-TIMESTAMP']; var date = Date.parse(timestamp); var expired = Date.now() - date > 10*60*1000; ``` ``` import dateutil.parser import pytz import datetime timestamp = headers["BOX-DELIVERY-TIMESTAMP"] date = dateutil.parser.parse(timestamp).astimezone(pytz.utc) now = datetime.datetime.now(pytz.utc) delta = datetime.timedelta(minutes=10) expiry_date = now - deltaMinutes expired = date >= expiry_date ``` ### Calculate HMAC signature Calculate the HMAC of the payload using either one of the two configured signatures for the application in the [Developer Console](https://app.box.com/developers/console). Ensure you append the bytes of the payload body first, and then the bytes of the timestamp found in the `BOX-DELIVERY-TIMESTAMP` header. ``` var crypto = require('crypto'); var primaryKey = '...'; var secondaryKey = '...'; var payload = '{"type":"webhook_event"...}'; var hmac1 = crypto.createHmac('sha256', primaryKey); hmac1.update(payload); hmac1.update(timestamp); var hmac2 = crypto.createHmac('sha256', secondaryKey); hmac2.update(payload); hmac2.update(timestamp); ``` ``` import hmac import hashlib primary_key = '...' secondary_key = '...' payload = "{\"type\":\"webhook_event\"...}" bytes = bytes(payload, 'utf-8') + bytes(timestamp, 'utf-8') hmac1 = hmac.new(primary_key, bytes, hashlib.sha256).digest() hmac2 = hmac.new(secondary_key, bytes, hashlib.sha256).digest() ``` ### Base64 Conversion Convert the HMAC to a `Base64` encoded digest. ``` var digest1 = hmac1.digest('base64'); var digest2 = hmac2.digest('base64'); ``` ``` import base64 digest1 = base64.b64encode(hmac1) digest2 = base64.b64encode(hmac2) ``` ### Signature comparison Compare the encoded digest with the value of the `BOX-SIGNATURE-PRIMARY` or `BOX-SIGNATURE-SECONDARY` headers. Compare the value of the `BOX-SIGNATURE-PRIMARY` header to the digest created with the primary key, and the value of the `BOX-SIGNATURE-SECONDARY` header to the digest created with the secondary key. Make sure to use a timing-safe comparison between signatures to prevent timing attacks. ``` const crypto = require('crypto'); function compareSignatures(expectedSignature, receivedSignature) { const expectedBuffer = Buffer.from(expectedSignature, 'base64'); const receivedBuffer = Buffer.from(receivedSignature, 'base64'); if (expectedBuffer.length !== receivedBuffer.length) { return false; } return crypto.timingSafeEqual(expectedBuffer, receivedBuffer); } const signature1 = headers['BOX-SIGNATURE-SECONDARY']; const signature2 = headers['BOX-SIGNATURE-PRIMARY']; const primarySignatureValid = compareSignatures(digest1, signature1) const secondarySignatureValid = compareSignatures(digest2, signature2) const valid = !expired && (primarySignatureValid || secondarySignatureValid) ``` ``` import hmac def compare_signatures(expected_signature: Optional[str], received_signature: Optional[str]) -> bool: if not expected_signature or not received_signature: return False if len(expected_signature) != len(received_signature): return False return hmac.compare_digest(expected_signature, received_signature) signature1 = headers["BOX-SIGNATURE-SECONDARY"] signature2 = headers["BOX-SIGNATURE-PRIMARY"] primary_sig_valid = compare_signatures(digest1, signature1) secondary_sig_valid = compare_signatures(digest2, signature2) valid = not expired and (primary_sig_valid or secondary_sig_valid) ``` HTTP header names are case insensitive. Your client should convert all header names to a standardized lowercase or uppercase format before trying to determine the value of a header. ## Rotate signatures When enabled, Box sends two signatures with every webhook payload. Your application can trust a payload as long as at least one of its signatures is valid. When updating one signature key at a time your application will always receive a payload with at least one valid signature. ### Rotation steps These instructions assume that you have already created a primary and secondary key in the [Developer Console](https://app.box.com/developers/console) and you are ready to replace either of them. By following these steps you can configure your application with two new keys without any conflicts. 1. Go to the **Webhooks** tab in the [Developer Console](https://app.box.com/developers/console). 2. Click the **Manage signatures keys**. 3. Click the **Reset** button to change the primary key. 4. Update your application with the new primary key. Your application can still receive notifications with the old primary key, but your webhooks should be processed correctly since the secondary key is still valid. 5. Once you are confident that no webhooks with the old primary key are in-flight, you can update the secondary key using the same process. **Reference:** https://developer.box.com/guides/webhooks/v2/signatures-v2/ --- ## Untitled *Type: guide | Category: Webhooks * Update Webhooks You can update a webhook using the Developer Console or API. Developer Console To update a webhook in the Developer Console… # Update Webhooks You can update a webhook using the [Developer Console](https://app.box.com/developers/console) or API. ## Developer Console To update a webhook in the [Developer Console](https://app.box.com/developers/console), follow the steps below. 1. Go to the **Webhooks** tab in the [Developer Console](https://app.box.com/developers/console) to display all webhooks. 2. Select the webhook you want to update by clicking on its ID. 3. Click the **Edit webhook** button. 4. Fill in the data you want to update. 5. Click the **Update** button to save your changes. The list of webhooks contains the following fields: **ID**, **Address**, **Content**, **Created by**, and **Created date**. ## API To update a webhook, use the [update webhook](e://put-webhooks-id) endpoint, which requires the webhook ID. To find the ID of the webhook, use the [list all webhooks](g://webhooks/v2/list-v2) endpoint. **Reference:** https://developer.box.com/guides/webhooks/v2/update-v2/ --- ## Untitled *Type: guide | Category: Metadata * Delete a metadata template Deleting a metadata template can be achieved by calling the DELETE /metadata_templates/enterprise/:templateKey… # Delete a metadata template Deleting a metadata template can be achieved by calling the [`DELETE /metadata_templates/enterprise/:templateKey/schema`](e://delete_metadata_templates_id_id_schema) API. This API returns a `204 No Content` API response with no response body when the template has been successfully deleted. This API also removes all the template instances from any files and folders. Only templates created within the `enterprise` scope can be deleted. # Admin permissions required Deleting metadata templates is restricted to users with admin permission. This means that only admins, or co-admins who have been granted rights to Create and edit metadata templates for your company by the admin can use the web app or the API to manage templates. **Reference:** https://developer.box.com/guides/metadata/templates/delete/ --- ## Untitled *Type: quick-start | Category: Mobile * Configure a Box Platform App To start making authenticated API calls to the Box API with the Box iOS SDK, an Access Token will be needed… # Configure a Box Platform App To start making authenticated API calls to the Box API with the **Box iOS SDK**, an **Access Token** will be needed. The simplest way to generate a valid token is to generate a new **Box Platform App** and manually generate a short lived developer token. The developer token is generated through the developer console UI and will be valid for one hour before having to be manually refreshed. ## Set up a Box app # Create a new Box app Create and configure a new Box JWT application from which a developer token may be generated. # Use an existing app Use one of your existing Box JWT applications from the Box developer console. # Create a new Box app To create a new Box application that may be used to generate a developer token, use the following steps. 1. Go to the [Developer Console](https://cloud.app.box.com/developers/console) > **My Platform Apps**. 2. Select **Create Platform App**. 3. Select **Platform App** as the type of application to create, and click **Next**. 4. Enter the **App Name**, an optional **Description** and **Purpose** of the App. Confirm with **Next**. 5. Select **User Authentication (OAuth 2.0)** as the authentication method, and click **Create App** 6. Optionally, scroll to the **Application Scopes** section of the same screen and select any additional permissions you want to enable for this application. 7. At the top of the page click the button to **Save Changes**. # Use an Existing JWT Box application If you have an existing JWT based Box application in your [developer console](https://cloud.app.box.com/developers/console) that you would like to use, skip to the next step. ## Generate a developer token Now that an application is available, we need to create a developer token that may be used to authenticate the Box iOS SDK to start making calls to the Box APIs. 1. Go to the [Developer Console](https://cloud.app.box.com/developers/console) 2. Load the application that you would like to use 3. In the left navigation menu, click on **Configuration** 4. Under **Developer Token**, click the **Generate Developer Token** button 5. Copy the token for the next step to make an API call ## Summary - You created a new, or are using an existing, Box app - You have generated and copied the developer token I have a developer token **Reference:** https://developer.box.com/guides/mobile/ios/quick-start/configure-box-app/ --- ## Untitled *Type: quick-start | Category: Mobile * Create an iOS App Before installing the Box iOS SDK you'll need to have an iOS application available to add the dependencies into. We'll… # Create an iOS App Before installing the Box iOS SDK you'll need to have an iOS application available to add the dependencies into. We'll create a new blank application now. ## Setup an iOS App Xcode will be the tool that we use to create and edit our iOS application during this quick-start guide. If you don't have Xcode, download it now from the [Apple developer site](https://developer.apple.com/xcode/). If this is the first application you're creating in Xcode or you already have application development experience in it, we'll start our integration of the Box iOS SDK with a new blank application. 1. Start Xcode 2. From the top menu, select **File** -> **New** -> **Project...** Select the option for **Single View App** Enter your application configuration information, including the `Product Name`, `Organization Identifier`, and `Organization Name`. 1. Select a local storage location for the application and click **Create** ## Summary - You created a new blank iOS application in Xcode I created a blank iOS application **Reference:** https://developer.box.com/guides/mobile/ios/quick-start/create-ios-app/ --- ## Untitled *Type: quick-start | Category: Mobile * Install the iOS SDK With an iOS application in place, you will need to import the required Box iOS SDK dependencies into your project using… # Install the iOS SDK With an iOS application in place, you will need to import the required Box iOS SDK dependencies into your project using one of the available package manager options. ## Select a package manager to use # Carthage Carthage is a decentralized dependency manager for Swift and Objective-C Cocoa projects. It is open-source and built with Swift. # CocoaPods CocoaPods is a centralized dependency manager for Swift and Objective-C Cocoa projects. It is open-source and was built with Ruby. # Swift Package Manager The Swift Package Manager is a tool for managing the distribution of source code, making it easy to share your code and reuse others’ code. # Install the iOS SDK using Carthage 1. From a terminal window, install Carthage: `brew install carthage`. Visit the [Carthage documentation](https://github.com/Carthage/Carthage#installing-carthage) for other installation methods. 2. At the root of your iOS application folder, where the `{APPNAME}.xcodeproj` is located, create a new file named `Cartfile`, without an extension. 3. Open the `Cartfile` and add the **Box iOS SDK** dependency with `git "https://github.com/box/box-ios-sdk.git" ~> 3.0`, then save and close the file. 4. From the terminal, in the folder where the `Cartfile` is present, update all dependencies: `carthage update --platform iOS`. A `Cartfile.resolved` file and a `Carthage` directory will be created in the directory. 5. Within a Finder or File Explorer window load the **Carthage** -> **Build** -> **iOS** directory. This should have a framework file named `BoxSDK.framework`. Keep this window open as we'll use it to add the framework to our project in the next step. 6. In your Xcode project, click on the name of your application at the top of the project navigator on the left. In the content that displays, click on the name of your project under the **TARGETS** option. Scroll down to **Frameworks, Libraries, and Embedded Content**. 7. Drag `BoxSDK.framework` from our Finder window over to the frameworks section. # Install the iOS SDK using CocoaPods 1. From a terminal window, install CocoaPods: `sudo gem install cocoapods`. 2. At the root of your iOS application folder, where the `APPNAME.xcodeproj` is located, run `pod init` to create a new `Podfile` with smart defaults. 3. Load `Podfile`, add the **Box iOS SDK** dependency under `# Pods for {APPNAME}` with `pod 'BoxSDK', '~> 3.0'`, then save and close. 4. From the terminal, in the folder where the `Podfile` is present, download all dependencies: `pod install`. 5. Open `.xcworkspace` in Xcode: `open {APPNAME}.xcworkspace` and build the project. # Install the iOS SDK using the Swift Package Manager 1. In your Xcode project, click on the name of your application at the top of the project navigator on the left. In the content that displays, click on the name of your project under the **PROJECT** option. 2. Click on **Swift Packages** and click on the `+` to add a package. 3. Enter the following repository URL `https://github.com/box/box-ios-sdk.git` and click next. 4. Leave the default settings and click next to finish importing. ## Summary You either selected to install the iOS dependencies using Carthage - You installed Carthage - You create a `Cartfile` with the Box iOS SDK dependency - You installed the iOS SDK dependencies - You manually imported the built dependencies into the Xcode project framework list. Or selected to use **CocoaPods** and - You installed CocoaPods - You created a new `Podfile` with the Box iOS SDK dependency - You installed the iOS SDK dependencies - You built the project in Xcode Or selected to use the **Swift Package Manager** and - You imported the iOS SDK `.git` repository into the Swift packages I've installed the Box iOS SDK dependencies **Reference:** https://developer.box.com/guides/mobile/ios/quick-start/install-ios-sdk/ --- ## Untitled *Type: quick-start | Category: Mobile * Learn to use the iOS SDK The Box iOS SDK is a Swift language wrapper for adding the Box API into your iOS project. Requirements To begin… # Learn to use the iOS SDK The Box iOS SDK is a [Swift language](https://developer.apple.com/swift/) wrapper for adding the Box API into your iOS project. ## Requirements To begin working with the Box iOS SDK and this quick start, the following are required: - iOS 11.0+ / Mac OS X 10.13+ - Xcode 10.0+ ## Overview This guide will take you through the following steps. 1. [Create a new iOS app](g://mobile/ios/quick-start/create-ios-app) in Xcode. 2. [Install the iOS SDK](g://mobile/ios/quick-start/install-ios-sdk) into your project. 3. [Configure a Box App](g://mobile/ios/quick-start/configure-box-app) so that the iOS SDK can access the Box API. 4. [Make an API call](g://mobile/ios/quick-start/make-api-call) to the Box API with the iOS SDK. I am ready to get started **Reference:** https://developer.box.com/guides/mobile/ios/quick-start/ --- ## Untitled *Type: quick-start | Category: Mobile * Make an API call With our Box and iOS applications created and configured with the Box iOS SDK we can now make our first call to Box APIs… # Make an API call With our Box and iOS applications created and configured with the Box iOS SDK we can now make our first call to Box APIs. Using our blank iOS application, we will create a button to trigger a request to Box to fetch the name of the currently authenticated user. Using a blocking actions as we use in these examples is slow. In a production app these blocking actions would need to be replaced with proper task delegation and non-blocking actions. ## Create a button Within your Swift application in Xcode, load `ContentView.swift`. At the top of the file you will see a `struct` for `ContentView`, within which is a basic string that will be output to your iOS application if you run the app in an emulator. ``` import SwiftUI struct ContentView: View { var body: some View { Text("Hello, World!") } } ``` We'll first replace the `Text` output line with a button to be able to trigger off the call to get the current user. Replace that line with the below button. ``` Button(action: { // Perform some action }) { Text("Click to get current user") } .padding(10) .cornerRadius(20) .foregroundColor(.white) .background(Color.blue) ``` Our next step is to add an an action for the button which will fetch a user's details from Box. ## Add an API call button action When a user clicks the button, we want to fetch the user's details. To achieve this we need to do two things, add the import for the **Box iOS SDK** and add the button action to make the call. At the top of the `ContentView.swift` file, add `import BoxSDK` with the other import statement. Next, within the button action, where we currently have a comment placeholder, add a call to the iOS SDK to fetch the current user. When the API call completes it will print an authentication message to the developer console. For ease of implementation, we have a blocking `sleep(5)` call in place in order to test that the iOS SDK can make calls from our iOS SDK by providing enough time for the request to complete. Replace `{{YOUR DEVELOPER TOKEN}}` with your developer token. ``` let client = BoxSDK.getClient(token: "{{YOUR DEVELOPER TOKEN}}") client.users.getCurrent(fields:["name", "login"]) { (result: Result<User, BoxSDKError>) in guard case let .success(user) = result else { print("Error getting user information") return } print("Authenticated as \(user.name)") } sleep(5) ``` Build and run your sample application in the iOS emulator. If you run this code an hour or more after you created the developer token in the last step, you will need to revoke and generate a new developer token using the same method in the [previous step](g://mobile/ios/quick-start/configure-box-app) as the developer token will only persist for 60 minutes. Once the application loads in the emulator you should see the button we created. Click it to start the API request. Within the Xcode developer console you should see the API request and response, with the last line showing the user print statement that we specified. ``` ◁ Status code: 200: no error ◁ Headers: ◁ Cache-Control, Value: no-cache, no-store ◁ BOX-REQUEST-ID, Value: 1c55151238444132eca16b4c2346d005 ◁ Transfer-Encoding, Value: Identity ◁ content-type, Value: application/json ◁ Connection, Value: keep-alive ◁ Strict-Transport-Security, Value: max-age=31536000 ◁ Body: {"type":"user","id":"123456789","name":"Test User","login":"testuser@test.com"} Authenticated as Optional("Test User") ``` If you don't see the Xcode developer console, from the menu click **View** -> **Debug Area** -> **Activate Console** Congratulations, you've now configured the **Box iOS SDK** and have made your first call to the Box API. ## Summary - You added a button to your blank iOS application - You added a request to fetch the current user using the iOS SDK I made an API call **Reference:** https://developer.box.com/guides/mobile/ios/quick-start/make-api-call/ --- ## Untitled *Type: quick-start | Category: Mobile * Next steps You've reached the end of this Quick Start guide. By now you should have taken the following steps. Created a new iOS app in… # Next steps You've reached the end of this Quick Start guide. By now you should have taken the following steps. 1. [Created a new iOS app](g://mobile/ios/quick-start/create-ios-app) in Xcode. 2. [Installed the iOS SDK](g://mobile/ios/quick-start/install-ios-sdk) into your project. 3. [Configured a Box App](g://mobile/ios/quick-start/configure-box-app) so that the iOS SDK can access the Box API. 4. [Made your first API call](g://mobile/ios/quick-start/make-api-call) to the Box API with the iOS SDK. ## Next things to do To take the next step with your application, the following resources are recommended. - [Token Downscoping](g://authentication/tokens/downscope): In this quick start guide you used a developer token to make your first call. To implement a scalable solution you should have a server-side solution to generate downscoped tokens to replace that implementation. - [Official JWT sample application](https://github.com/box/box-ios-sdk/tree/master/SampleApps/JWTSampleApp): Bundled with the iOS SDK, this sample application will get you started quickly with a properly structured Box JWT application, which doesn't require user login. - [Official OAuth 2 sample application](https://github.com/box/box-ios-sdk/tree/master/SampleApps/OAuth2SampleApp): Bundled with the iOS SDK, this sample application will get you started quickly with a properly structured Box OAuth 2 application, which requires user login. **Reference:** https://developer.box.com/guides/mobile/ios/quick-start/next-steps/ --- ## Untitled *Type: quick-start | Category: Tooling * Configure a Box App To use the Postman Collection the Postman application needs to authenticate to the Box API using an Access Token. The… # Configure a Box App To use the **Postman Collection** the Postman application needs to authenticate to the Box API using an **Access Token**. The simplest way to get an Access Token is by logging into Box using a **Box App**. A **Box App** is an application that can be used for making API calls. When using the **Postman Collection** you can choose to either set up your own Box App or use our preconfigured one. The key benefit of setting up your own Box App is that you won't need to go through login every hour, but it does require a few extra steps to set up. ## Select a Box App to use # Create new Box App We can set up a Box App for you right here from the documentation. With a few clicks you will be ready to go. # Use existing Box app If you've already created a Box App before that you want to use, then we can use the credentials for that application. # Create a Box app To use your own **Box App** you will need to create a new Box App in the **Box Developer Console**. Click the button below and we will set it up for you. At the end you will have a **Client ID** and **Client Secret**. Create an app We will use these credentials to authenticate your application in the next step. # Use existing Box app If you have already created a Box App before you can use that as well. We require a few settings to be set for this to work. 1. Go to the [Developer Console](https://cloud.app.box.com/developers/console) 2. Select your application 3. Go to the app's configuration section 4. Make sure your application uses **Standard OAuth 2.0** as the authentication method 5. Scroll down to the **OAuth 2.0 redirect URI** configuration and set the **Redirect URI** to the value `https://developer.box.com/auth/callback`. It is important to note, if you visited this tutorial at `box.dev` your **Redirect URI** should be `https://box.dev/auth/callback`. 6. Scroll down to the **Application Scopes** section to select your desired [permissions](g://api-calls/permissions-and-errors/scopes). **Your application must have at least one or more** **of the following scopes:** manage users, read all files and folders stored in Box, read and write all files and folders in Box. 7. At the top of the page click the button to **Save Changes** Next, copy the values for the Client ID and Client Secret into these 2 fields. Client ID Client Secret We will use these credentials to authenticate your application in the next step. # Security notice Your API credentials are now stored in the browser cache. We highly recommend clearing out this storage by clicking the **Reset** button later in this guide. ## Summary You either selected to create a new **Box App** - Signed up for a developer account (Optional) - Had us create **Platform App** for you that uses **OAuth 2.0** authentication - Had us set up the **redirect URL** for the application Or you selected to use an **existing Box App** I have configured a Box app **Reference:** https://developer.box.com/guides/tooling/postman/quick-start/configure-box-app/ --- ## Untitled *Type: quick-start | Category: Tooling * Use Postman to make API calls Postman is a tool that lets you build and test HTTP requests in an easy-to-use interface without configuring a… # Use Postman to make API calls [Postman](https://getpostman.com) is a tool that lets you build and test HTTP requests in an easy-to-use interface without configuring a full development environment. The Box Postman Collection is a set of preconfigured requests that make it possible to get started with the Box API from Postman without having to manually configure the requests. ## Overview This guide will take you through the following steps. 1. [Install Postman](g://tooling/postman/quick-start/install-postman) on your device. 2. [Configure a Box App](g://tooling/postman/quick-start/configure-box-app) so that Postman can access the Box API. 3. [Log in](g://tooling/postman/quick-start/log-in-to-box) to the Box App to get the right API credentials. 4. [Fork Box's Postman collection](g://tooling/postman/quick-start/load-postman-collection) into Postman. 5. [Make a first API call](g://tooling/postman/quick-start/make-api-call) to the Box API with Postman. I am ready to get started **Reference:** https://developer.box.com/guides/tooling/postman/quick-start/ --- ## Untitled *Type: quick-start | Category: Tooling * Install Postman To use the Box Postman Collection you will need to have the Postman app installed on your device. Postman is available for… # Install Postman To use the **Box Postman Collection** you will need to have the [Postman](https://getpostman.com) app installed on your device. Postman is available for Windows, Mac, and Linux environments. [Download the appropriate version for you operating system](https://www.postman.com/downloads/) Next, install Postman on your machine and (optionally) [register for a Postman account and log in](https://identity.getpostman.com/signup). ## Summary - You installed Postman - You created a Postman account (optional) - You logged into the Postman application with your Postman account I have installed Postman **Reference:** https://developer.box.com/guides/tooling/postman/quick-start/install-postman/ --- ## Untitled *Type: quick-start | Category: Tooling * Fork a collection Now that you are logged in we can fork the Box Postman Collection into the Postman App that we installed previously. When… # Fork a collection Now that you are logged in we can fork the **Box Postman Collection** into the **Postman App** that we installed previously. When we fork the Postman Collection we will also automatically load your API credentials as a Postman environment. ## Forking a collection and environment By clicking the button below you will fork the Box Postman Collection into your Postman application. In the same click it will also load your **Access Token**, **Refresh Token**, **Client ID** and Client Secret into a Postman environment. We recommend to fork the Box Postman Collection - you will be asked if you want to update the collection any time Box makes changes to it. You can also copy the collection, but you might miss important updates. ## Exploring the collection When you clicked the button above it would have asked you to fork the collection into the Postman application. Once imported, the collection should appear within the app in the left-hand sidebar. You can click on the collection to open it up and explore our over 170 API endpoints. ## Summary - You forked the Postman collection into Postman - You additionally loaded your Box Postman environment into Postman # Incomplete previous step Complete the previous steps to select and log in to a **Box App**. # Incomplete previous step Complete the previous steps to select and log in to a **Box App**. I have forked the collection **Reference:** https://developer.box.com/guides/tooling/postman/quick-start/load-postman-collection/ --- ## Untitled *Type: quick-start | Category: Tooling * Log in to Box In this step, we will use the Client ID and Client Secret from the previous step to log you in and create an Access Token for… # Log in to Box In this step, we will use the **Client ID** and **Client Secret** from the previous step to log you in and create an **Access Token** for your user. ## The reason to log in Currently you have provided us with the following information. Client ID Client Secret These **credentials** allow any program or piece of code to authenticate itself to the **Box API**. It represents the **Box App** you created but it doesn't tell the API who you, the **User**, are. To authenticate yourself you will need to send your browser to the Box login screen to authorize your **Box App** to access to your **User** account. Setting this flow up can be hard, which is why we have made this straightforward for you with the button below. ## Log into your Box app ## You are now logged in We just sent your browser to the [Box Authorization](e://get-authorize) screen where you granted your application access to your user account. After you granted it access the browser redirected back to this site with a `code`. We then [exchanged](e://post-oauth2-token) this short-lived `code` for a longer lived **Access Token** and **Refresh Token**. These tokens represent you, the **User**. Your name Access Token Refresh Token # Security notice Your API credentials are now stored in the browser cache. We highly recommend clearing out this storage by clicking the **Reset** button later in this guide. # Incomplete previous step Please complete the previous step to set up the **Box App** you want to use. ## Summary - You logged into your **Box account** using your own **Box App** or our preconfigured app - You granted the **Box App** access to your account - You are able to see your account's **Access Token** and **Refresh Token** on this page I have logged in to Box **Reference:** https://developer.box.com/guides/tooling/postman/quick-start/log-in-to-box/ --- ## Untitled *Type: quick-start | Category: Tooling * Next steps You've reached the end of this Quick Start guide. By now you should have taken the following steps. Installed Postman on your… # Next steps You've reached the end of this Quick Start guide. By now you should have taken the following steps. 1. [Installed Postman](g://tooling/postman/quick-start/install-postman) on your device. 2. [Configured a Box App](g://tooling/postman/quick-start/configure-box-app) in order to be able to authenticate a Postman App for accessing Box. 3. [Logged in](g://tooling/postman/quick-start/log-in-to-box) to the Box App to get the right API credentials. 4. [Loaded Box's Postman collection](g://tooling/postman/quick-start/load-postman-collection) into Postman, and with it all the credentials needed to make API calls. 5. [Made your first API call](g://tooling/postman/quick-start/make-api-call) to the Box API with Postman. ## Next things to do We recommend the following resource for anyone who wants to learn more about using Postman with the Box API. - [The official Postman Get Started guide](https://learning.getpostman.com/getting-started/) from the Postman team. - [Learn how to refresh your access token](g://tooling/postman/refresh) within Postman. **Reference:** https://developer.box.com/guides/tooling/postman/quick-start/next-steps/ --- ## Untitled *Type: quick-start | Category: Tooling * Make an API call With the Box Postman Collection forked into the Postman App it is now possible to make API calls to the Box API on behalf… # Make an API call With the **Box Postman Collection** forked into the **Postman App** it is now possible to make API calls to the **Box API** on behalf of the user you logged in as. ## First: Reset browser storage Now that you've imported the Box API credentials into Postman you should take a moment to remove these credentials from your browser's storage. Clear credentials Removing your API credentials from the browser storage ensures that no other script can read your **Client ID**, **Client Secret**, **Access Token** or **Refresh Token**. ## Select an environment Before an API call can be made it is important to select the right Postman environment to use when making API calls. When you imported the **Box Postman collection** it automatically imported a `Box` Postman environment for you to use. The collection will automatically recognize and use the variables in this environment for making API calls. To select the Box Postman environment, select **Box** from the dropdown in the top-right of Postman. You can inspect the environment by clicking the **eye** icon to the right of the dropdown. ## Make an API request To make an API request, select a **Request** from the Box Postman Collection. In this example, we will use the **List items in folder** API which can be found in the **Folders** folder. By default the `folder_id` for this API endpoint is set to `0` which represents every user's root folder. You can leave this value as is or set it to the folder ID of a folder you want to inspect. Next, click the **Send** button in the top right to make the API request. The API call should return quickly and show you a list of items in your folder in the response **Body** tab in the bottom half of the screen. # Authentication error At this point Postman might return an error instead of a list. Often, this means your **Access Token** has expired. Check our guide on refreshing an access token in Postman for more details. ## Summary - You selected the Postman environment to use for making API calls to Box - You made your first API call to Box, requesting the folder items for your user's root folder. I made an API call **Reference:** https://developer.box.com/guides/tooling/postman/quick-start/make-api-call/ --- ---