Before you start
Complete these Box setup steps before you build. You need:- A , or a with Box AI enabled. Box Hubs is required only for the publish step. is optional and requires Enterprise Advanced.
- A Box application configured with Client Credentials Grant authentication, authorized in the Admin Console, with these scopes:
- Read and write all files and folders stored in Box
- Manage AI
- Manage webhooks
- Python 3.11+ for the Build by hand path. For the agent path, a coding agent such as Codex, Claude Code, or Cursor, and a runtime for the stack you choose. Installing helps the agent use current Box APIs.
.env file use them.
1. Create the brand asset metadata template
1. Create the brand asset metadata template
- Open the Box Admin Console and select Metadata.
- Select New and name it
Brand Asset. - Add the following fields:
- Select Save, then reopen the template and copy the template key from under the Template Name. You need it for the prompt and for
.env.
Asset Type becomes assetType. If you rename a field, confirm its key in the Admin Console.For a detailed walkthrough, see .captureDate from visible content, so that value is not necessarily the camera capture date. The optional local EXIF step replaces it when the file has EXIF data.2. Create the brand lifecycle folders
2. Create the brand lifecycle folders
- In Box, create a folder called
Brand DAMwith this structure:
- Note each folder ID from the URL. For example, if the URL is
https://app.box.com/folder/123456789, the folder ID is123456789. - Share every folder in this tree with your application’s service account as Editor. A CCG app runs as a service account. That account has no access to your content until you invite it.
Support document if you extend the template. This tutorial covers brand assets only.Build with an agent
Gather these values from Before you start:- Metadata template key. Copy it from the template you created.
- Six lifecycle folder IDs. Copy them from the folder URLs.
- Enterprise ID. Copy it from the Developer Console using the icon in the top-right.
<TEMPLATE_KEY> and <ENTERPRISE_ID> placeholders if you want the agent to pre-fill .env. Otherwise leave them and fill .env yourself after scaffolding.
Review generated code before using it in production. Never paste Box credentials into your coding agent.
.env.example to .env and fill in your client ID, client secret, enterprise ID, metadata template key, and folder IDs. Then skip ahead to Run and verify.
Prefer to write the code yourself? See Build by hand below.
Build by hand
Use this path to write the code yourself, or as a reference if the agent drifts. Complete Before you start first, then follow the steps in order. Samples are Python. You can also clone a working sample if you prefer to start from running code:Python working sample
1. Set up the development environment
1. Set up the development environment
- Open your terminal and create a new project directory:
- Install dependencies:
(.venv) at the beginning. Every time you open a new terminal window or tab, re-activate the virtual environment with source .venv/bin/activate from the project directory. If you see ModuleNotFoundError, the venv is usually not activated.- Create a
.envfile and add the following content. Replace the placeholder values with credentials from the Box Developer Console and the folder IDs from Before you start:
.env file stores sensitive values. Your code reads them by name. When you copy the code in the following steps, keep the quoted variable names exactly as shown. Do not replace them with your actual credentials.2. Authenticate the Box client
2. Authenticate the Box client
3. Build the extraction function
3. Build the extraction function
4. Write metadata back to the file
4. Write metadata back to the file
409 Conflict on Metadata Instance. This function then sends a JSON-Patch update. You can re-run the service on a file you already processed, including duplicate webhook deliveries.Use the add operation, not replace. add sets a value whether or not the field is already present. The update still succeeds when Box AI returns fewer fields than the previous run.5. Advance the brand asset lifecycle
5. Advance the brand asset lifecycle
6. Invite an external vendor to Draft
6. Invite an external vendor to Draft
expires_at depends on the Automatically remove invited collaborators: Allow folder owners to extend the expiry date setting in the Admin Console Enterprise Settings. If that setting is off, omit expires_at. The enterprise setting can also override the date you send, so check the expiration on the new collaboration in Box. External email invites can also require the vendor domain on the .For role definitions, see .7. Create the webhook listener
7. Create the webhook listener
Run and verify
Test extraction locally without a public URL or webhook. The request body matches what Box sends when a new file arrives.-
Terminal 1. Start the server. Make sure you are in the
brand-damdirectory:The server listens on port 5000. Leave this terminal running. -
Terminal 2. Send a test request. Upload a JPEG or PNG product image to the Draft folder in Box. Copy the file ID from the file URL. For
https://app.box.com/file/123456789, the file ID is123456789. -
Check the result. Switch back to Terminal 1. The extracted fields print, then a confirmation that metadata was applied:
Open the file in Box and select the Metadata tab to verify the values.
-
Advance the file. After creative and legal sign-off, move it to Published.
Confirm the file is in the Published folder and
lifecycleStatusisPublished.
Register a webhook for production
The local curl test matches what Box sends. For production, Box needs a public HTTPS endpoint.<DRAFT_FOLDER_ID> with your Draft folder ID and set address to your HTTPS URL with /webhook appended.
Once registered, a supported image uploaded to Draft triggers extraction and metadata writes.
Publish approved assets to a Box Hub
After files reach Published, add that folder to a Hub. Regional teams and agency partners then search and ask questions against files approved for external use, not drafts. Box Hubs inherit permissions from the source files. Create a file calledpublish_hub.py:
.env loaded:
Hub ready: Brand asset library (ID: …). Open https://app.box.com/hubs/<HUB_ID>. Files you later move to Published with POST /lifecycle show up in this hub. Do not run the script again for each file; that creates another hub.
The CCG service account owns the hub. Hubs in the Box web app lists hubs for the signed-in user, so this hub does not appear there until you your managed user onto it (for example as co-owner).
Hub endpoints require the box-version: 2025.0 header. The v10 SDK methods above send it. See and the to ask questions against hub content.
Optional: parse camera EXIF locally
Box does not expose camera EXIF through the metadata or AI APIs. Box AI Extract covers visual and OCR fields. If you need capture date or camera model, download the file, parse EXIF locally, and merge those values into the same metadata instance.-
From your project directory, activate the virtual environment and install Pillow:
Install into this venv, not the system Python on your machine.
-
Create
exif.py: -
If you built by hand: in
app.py, importenrich_exifand merge its return value intoextractedafter Box AI extract and beforeapply_metadata:Insidehandle_webhook, after you setlifecycleStatustoDraft:If you used the agent path, your project may already include these lines. Searchapp.pyforenrich_exif. You still need Pillow in the venv. -
Test with the same local
POST /webhookrequest from Run and verify. Use a camera or phone JPEG that contains EXIF, not a logo PNG with no camera data. In the server log, look forcameraMake,cameraModel, andcaptureDatealongside the Box AI fields. Confirm the same values on the file Metadata tab in Box. Logos and exports without EXIF leave those three fields empty. That is expected.
Optional: retire large-format media to Box Archive
The Retired folder keeps superseded campaigns out of the Hub. Use when large video or print masters should be owned by the enterprise, not the previous user. Archive APIs require Enterprise Advanced. They also require the Global Content Manager (GCM) scope, which Box Support enables. They are not available on the Free Developer Plan. To archive a retired file, create an archive with , then by settingparent.id to the archive ID.
Availability, scopes, and which endpoints your app can call depend on your Box plan and enterprise configuration. Confirm those in the guides below before you extend this service:
Scaling to production
Respond before processing
Respond before processing
2xx response to each webhook delivery. If it does not get one, it retries the delivery. Box AI extraction can take several seconds per image, and longer on large files, so the inline handler in app.py can time out and trigger retries that call Box AI again.In production, validate the payload, add the file ID to a job queue, and return 200 immediately. A separate worker runs extraction and writes metadata. See for delivery and retry behavior.Handle duplicate deliveries
Handle duplicate deliveries
apply_metadata keeps the write safe, but a duplicate still calls Box AI a second time. To skip that work, check for an existing instance with GET /2.0/files/:id/metadata/enterprise/:template at the top of the handler. Return early if lifecycleStatus is already set.If a vendor replaces a draft in place, let extraction run so metadata matches the latest version.Keep vendor access off published masters
Keep vendor access off published masters
Brand DAM to vendors. If an agency needs a specific published file, collaborate on that file or on a Hub, not the whole tree.Process at scale with event streams
Process at scale with event streams
Troubleshooting
ModuleNotFoundError: No module named '...'
ModuleNotFoundError: No module named '...'
source .venv/bin/activate from the project directory before running any python3 commands. Each new terminal tab needs its own activation.invalid_client: The client credentials are invalid
invalid_client: The client credentials are invalid
.env file:- Verify
BOX_CLIENT_IDandBOX_CLIENT_SECRETmatch Developer Console > Configuration. - Confirm
BOX_ENTERPRISE_IDis your enterprise ID (Admin Console > Account & Billing, or Developer Console > icon in the top-right > Copy Enterprise ID). - Ensure your app is authorized and the app type is Client Credentials Grant.
404 Not Found
404 Not Found
Brand DAM.metadata_template must have both scope and template_key
metadata_template must have both scope and template_key
BOX_METADATA_TEMPLATE_KEY value in your .env file is missing or empty. Add the template key you noted in Before you start.409 Conflict on Metadata Instance
409 Conflict on Metadata Instance
Unknown lifecycle status
Unknown lifecycle status
POST /lifecycle only accepts the six dropdown values from the template, including the space in Creative review and Legal review. Check the JSON body against those strings exactly.The agent tried to create the template or folders
The agent tried to create the template or folders
