Box API Integration
Integrates with the Box Content Cloud API to manage files, folders, collaborations (permissions), metadata, signatures (Box Sign), content hubs, retention policies, and AI-powered content insights using the official boxsdk Python library.
TL;DR for Code Generation
- Use
boxsdk.Client(oauth)for user auth orboxsdk.CCGClient(client_id, client_secret, enterprise_id)for server auth (Client Credentials Grant) - The SDK is generated from Box's OpenAPI spec — methods follow the REST resource hierarchy
- Always paginate collections with
.items()or.next_marker— Box uses marker-based pagination - Use the
as_user(user_id)context manager for impersonation (admin/service account only) - Box Sign requests use the
boxsdk.sign_templatesandboxsdk.sign_requestsresources - Handle
boxsdk.exception.BoxAPIExceptionfor API-level errors withstatusandcodefields
When to Use
Use this skill when:
- Uploading, downloading, moving, copying, or deleting files and folders
- Managing share links, collaborations (user/group permissions), and access levels
- Applying metadata templates (custom key-value schemas) to files and folders
- Creating and sending Box Sign signature requests
- Searching across content with full-text and metadata queries
- Setting up webhooks for file and folder change notifications
- Using Box AI to extract insights from documents (text generation, question answering)
- Managing retention policies and legal holds for compliance
When NOT to Use
- Real-time collaborative document editing (use Box web app or Office Online integration)
- High-throughput file processing (Box has rate limits — use upload sessions for large files)
- Anonymous file access (Box requires authentication for every API request)
- Replacing a CDN for public content delivery (use Box shared links with appropriate settings)
Core Workflow
Create a Box Application — Go to
https://developer.box.comand create a new app. Choose "Client Credentials Grant (Server Auth)" for automated access, or OAuth 2.0 for user delegation. Checkpoint: Verify the app exists in the Box Developer Console and has the correct scopes.Authenticate — For server auth:
auth = CCGAuth(client_id, client_secret, enterprise_id)thenclient = Client(auth). For user auth: use OAuth 2.0 flow withJWTAuthorOAuth2. Checkpoint: Callclient.users().get(user_id="me")for user auth, orclient.users()[:1]for service account.Choose a Resource — Files:
client.file(file_id). Folders:client.folder(folder_id). Users:client.user(user_id). Search:client.search.query(...). Checkpoint: Verify the resource exists by calling.get()on the resource object.Perform Operations — Upload with
client.folder(folder_id).upload(file_path), download withfile.content(), move withfile.move(parent_folder), collaborate withclient.folder(folder_id).add_collaborator(...). Checkpoint: Check the response object'stypeandidfields.Handle Pagination — Use
.items()for folder listing,.next_markerfor search and metadata query results. Iterate withfor item in client.folder(folder_id).items():. Checkpoint: Ensure the loop terminates — pagination returns empty when exhausted.Handle Errors — Wrap in
try/except BoxAPIException. Checke.statusfor 401 (unauthorized), 403 (forbidden), 404 (not found), 409 (conflict), 429 (rate limit). Checkpoint: Loge.code(e.g.,item_name_in_use,trashed_item) for debugging.
Implementation Patterns
Pattern 1: Upload and Share a File
import os
from boxsdk import Client, CCGAuth
from boxsdk.exception import BoxAPIException
auth = CCGAuth(
client_id=os.environ["BOX_CLIENT_ID"],
client_secret=os.environ["BOX_CLIENT_SECRET"],
enterprise_id=os.environ["BOX_ENTERPRISE_ID"],
)
client = Client(auth)
def upload_and_share(folder_id: str, file_path: str) -> dict:
"""Upload a file to Box and create a shared link."""
try:
folder = client.folder(folder_id)
uploaded_file = folder.upload(file_path)
shared_link = uploaded_file.create_shared_link(
access="open",
unshared_at=None,
permissions={"can_download": True, "can_preview": True},
)
return {
"id": uploaded_file.id,
"name": uploaded_file.name,
"shared_url": shared_link["url"],
}
except BoxAPIException as e:
print(f"Box API error {e.status}: {e.code} — {e.message}")
raise
result = upload_and_share("123456789", "/tmp/report.pdf")
print(f"File shared: {result['shared_url']}")
Pattern 2: List Folder Contents
def list_folder_contents(folder_id: str) -> list[dict]:
"""List all files and subfolders in a Box folder."""
items = []
try:
folder = client.folder(folder_id)
for item in folder.items():
items.append({
"id": item.id,
"name": item.name,
"type": item.type, # "file" or "folder"
"size": getattr(item, "size", None),
"created_at": getattr(item, "created_at", None),
})
except BoxAPIException as e:
print(f"Error listing folder: {e}")
return items
contents = list_folder_contents("0") # root folder
for item in contents:
print(f"[{item['type']}] {item['name']} ({item['id']})")
Pattern 3: Apply Metadata to a File
def apply_metadata(file_id: str, template_key: str, values: dict) -> dict:
"""Apply or update a metadata template on a Box file."""
from boxsdk.object.metadata import Metadata
try:
file = client.file(file_id)
metadata = file.metadata(template_key)
return metadata.update(values)
except BoxAPIException as e:
if e.status == 409:
print(f"Metadata already exists — updating instead.")
return client.file(file_id).metadata(template_key).update(values)
raise
metadata_values = {
"documentType": "Invoice",
"clientName": "Acme Corp",
"amount": 12500.00,
"status": "Pending Approval",
}
apply_metadata("987654321", "documentFlow", metadata_values)
Pattern 4: Send Box Sign Request
def send_sign_request(
file_id: str,
signer_email: str,
signer_name: str,
) -> dict:
"""Send a Box Sign request for a file."""
file = client.file(file_id)
sign_request = client.create_sign_request(
signers=[{"email": signer_email, "name": signer_name}],
source_files=[file],
parent_folder=client.folder("0"),
is_prepare=False,
)
return {
"id": sign_request.id,
"status": sign_request.status,
"signers": [s.email for s in sign_request.signers],
}
result = send_sign_request("987654321", "signer@example.com", "Jane Signer")
print(f"Sign request {result['id']} sent to {result['signers']}")
Pattern 5: BAD vs GOOD — Error Handling
# ❌ BAD — no error differentiation, unreliable
file = client.file("nonexistent")
content = file.content()
# ✅ GOOD — typed error handling with status-based recovery
from boxsdk.exception import BoxAPIException
def safe_download_file(file_id: str) -> bytes | None:
"""Download a file with resilient error handling."""
try:
file = client.file(file_id).get()
return file.content()
except BoxAPIException as e:
if e.status == 404:
print(f"File {file_id} not found or trashed.")
return None
if e.status == 403:
print(f"No access to file {file_id}.")
return None
if e.status == 429:
print("Rate limited.")
return None
raise
Constraints
MUST DO
- Use CCGAuth for server-to-server apps — avoid storing user refresh tokens
- Always call
.get()on a resource before accessing its fields (SDK uses lazy loading) - Use marker-based pagination (
.items()iterator) for folder listing - Set
as_user()for admin operations acting on behalf of managed users - Validate file types and sizes before upload (Box has per-file size limits based on plan)
MUST NOT DO
- Hardcode OAuth client credentials in application code
- Assume folder IDs are stable across environments — use folder names or metadata to find them
- Use
as_user()without admin privileges — it will raise 403 - Poll folders for changes — use webhooks and event streams instead
- Upload files without checking for name conflicts (use
preflight_checkbefore upload)
Output Template
Every integration function should expose:
- Authentication —
CCGAuthorOAuth2initialization with credentials from env - Resource Selection —
client.file(),client.folder(),client.user(), orclient.search.query() - Operation — Upload, download, share, collaborate, or metadata mutation
- Pagination —
.items()iterator for collections,.next_markerfor search - Error Handling —
try/except BoxAPIExceptionwithe.statusande.codedifferentiation
Related Skills
| Skill | Purpose | |