Skip to main content
When someone uploads a product image, logo, or campaign file to a Box intake folder, this service extracts brand metadata and writes those values onto the file. You can advance the file through the entire lifecycle, from Draft to Retired. External agencies upload into Draft only, while regional teams find approved files in a Box Hub. helps you store, find, share, and retrieve files in one library with access controls. Most of those files are unstructured, so models cannot use them until you attach metadata. See .

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.
Copy the metadata template key, the six lifecycle folder IDs, and the enterprise ID. The agent prompt and your .env file use them.
The metadata template lists the fields Box AI extracts and the lifecycle status your service writes. Create it once. Every processed file then returns values in this shape.
This step requires Admin access. If you do not have access, contact your Box administrator.
  1. Open the Box Admin Console and select Metadata.
  2. Select New and name it Brand Asset.
  3. Add the following fields:
  1. 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.
Box derives each field’s key from its display name in camel case. Asset Type becomes assetType. If you rename a field, confirm its key in the Admin Console.For a detailed walkthrough, see .
Box AI Extract reads what is visible in the file: layout, on-image text, and OCR on JPEG, PNG, TIFF, and WEBP. It does not read camera EXIF. Box AI can still fill 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.
Create this folder tree in Box. Each lifecycle stage is its own folder so you can grant access per stage. Vendors see Draft. Reviewers see their stage. The Hub indexes Published only.
  1. In Box, create a folder called Brand DAM with this structure:
  1. Note each folder ID from the URL. For example, if the URL is https://app.box.com/folder/123456789, the folder ID is 123456789.
  2. 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.
Without this share, API calls return 404 Not found.Find the service account email in the Developer Console. Open your app, select the Configuration tab, and copy the Service Account ID from the App Details sidebar. It looks like AutomationUser_xxxxx_xxxxxx@boxdevedition.com.Invite that email as a collaborator on Brand DAM with the Editor role so child folders inherit access.
The same template and folder pattern can hold owner’s manuals, warranty PDFs, or maintenance docs. Add a dropdown value such as 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.
Choose your stack and copy the prompt. The prompt lists these as already created. The agent reads them from environment variables and does not try to create them. Replace the <TEMPLATE_KEY> and <ENTERPRISE_ID> placeholders if you want the agent to pre-fill .env. Otherwise leave them and fill .env yourself after scaffolding.
Python + Flask

Review generated code before using it in production. Never paste Box credentials into your coding agent.

When the agent finishes, copy .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

Clone the Flask app, add your Box credentials, and run.
  1. Open your terminal and create a new project directory:
  1. Install dependencies:
After activation, your terminal prompt shows (.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.
  1. Create a .env file and add the following content. Replace the placeholder values with credentials from the Box Developer Console and the folder IDs from Before you start:
Never commit .env files to version control. Add .env to your .gitignore.
The .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.
Create the Box client module in your project directory:
Client Credentials Grant is recommended for server-to-server automations where no end user is present. For other authentication options, see .
Create the extraction module. It takes a file ID, calls Box AI with your metadata template, and returns structured brand fields. Structured extract runs OCR on JPEG, PNG, TIFF, and WEBP, so you do not convert product shots or logos to PDF first. See .
The template tells Box AI which fields to look for. Product name, campaign lettering, and brand marks come back as keys, not a free-text caption.
Create the metadata module. After extraction, this function attaches the values to the file as a metadata instance.
Creating metadata succeeds only the first time. If the file already has an instance of the template, Box returns 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.
After metadata is attached, the fields are searchable and visible in the Box web app. Use to find External-rights logos for a campaign, or to list files still in Legal review.
Create the lifecycle module. Each allowed status maps to a folder. The function updates status and moves the file in one step so current files do not sit in the wrong folder.
Creative and legal teams can still review in Box with comments, versions, tasks, or a approval workflow. This function records which folder holds the current file after that decision.
Agencies and freelancers must not see Legal review or Published masters. Invite them to Draft only, as Viewer Uploader, with an expiration date.
Do not put unpublished brand files in a folder that already has an open shared link, or that already has external collaborators outside Draft. Invite named users. Set an expiration on the collaboration. Add only the Published folder to a Hub.
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 .
Create the HTTP application. It receives webhook notifications when new files arrive in Draft. It also exposes a lifecycle endpoint your review app can call.
In production, verify webhook signatures to confirm that requests originate from Box. See .
This handler runs Box AI extraction before it responds, which keeps the local test simple. Box expects a 2xx response within 30 seconds and retries the delivery if it does not get one. Extraction can take long enough to miss that window. In production, return 200 right away and process the file from a queue. See Respond before processing in Scaling to production.
After scaffolding, your project directory contains these files:
When you finish scaffolding, continue to Run and verify.

Run and verify

Test extraction locally without a public URL or webhook. The request body matches what Box sends when a new file arrives.
Open two terminal windows at the same time. Terminal 1 runs the server and must stay running. Terminal 2 sends a test request.
  1. Terminal 1. Start the server. Make sure you are in the brand-dam directory:
    The server listens on port 5000. Leave this terminal running.
  2. 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 is 123456789.
    Use the file ID, not the folder ID.
  3. 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.
  4. Advance the file. After creative and legal sign-off, move it to Published.
    Confirm the file is in the Published folder and lifecycleStatus is Published.

Register a webhook for production

The local curl test matches what Box sends. For production, Box needs a public HTTPS endpoint.
Replace <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 called publish_hub.py:
Run the script once from the project directory, with your virtual environment active and .env loaded:
A successful run prints 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.
Creating a hub as the CCG service account requires Box Hubs to be enabled for that account. If create fails, enable Generate user access tokens and authenticate as a managed user who can create hubs. The answer bank tutorial shows that auth path.

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.
  1. From your project directory, activate the virtual environment and install Pillow:
    Install into this venv, not the system Python on your machine.
  2. Create exif.py:
  3. If you built by hand: in app.py, import enrich_exif and merge its return value into extracted after Box AI extract and before apply_metadata:
    Inside handle_webhook, after you set lifecycleStatus to Draft:
    If you used the agent path, your project may already include these lines. Search app.py for enrich_exif. You still need Pillow in the venv.
  4. Test with the same local POST /webhook request 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 for cameraMake, cameraModel, and captureDate alongside 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 setting parent.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

Box waits 30 seconds for a 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.
Box webhook delivery can produce duplicates. The 409 fallback in 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.
Collaborate external users on Draft only. Do not cascade Editor access from Brand DAM to vendors. If an agency needs a specific published file, collaborate on that file or on a Hub, not the whole tree.
For high-volume drop folders, use rather than webhooks. Enterprise events are a polling stream that holds events until you consume them. That works for batch processing thousands of files.

Troubleshooting

Your Python virtual environment is not activated. Run source .venv/bin/activate from the project directory before running any python3 commands. Each new terminal tab needs its own activation.
Check your .env file:
  • Verify BOX_CLIENT_ID and BOX_CLIENT_SECRET match Developer Console > Configuration.
  • Confirm BOX_ENTERPRISE_ID is 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.
The service account does not have access to the file or folder. Invite the service account email (Developer Console > your app > Configuration > App Details sidebar) as a collaborator with Editor on Brand DAM.
The BOX_METADATA_TEMPLATE_KEY value in your .env file is missing or empty. Add the template key you noted in Before you start.
The file already has an instance of the template. Add the 409 fallback shown in 4. Write metadata back to the file. To retest with a clean file, remove the instance from the file’s Metadata tab, or delete it through the API:
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 template, folders, and enterprise ID come from Before you start. An agent cannot create the Admin Console template. Re-paste the prompt with the prerequisites block intact, or follow Build by hand.

Next steps

Sales RFP answer bank

Query a curated Hub with Box AI after you publish approved assets.

Extract API reference

See the full API specification for structured extraction, including image formats.
Last modified on October 16, 2026