Skip to main content
A claims system collects evidence into a case folder: photos, police reports, repair estimates, medical records. Someone has to decide whether that evidence is complete and acceptable. That decision then has to be recorded. This tutorial hands the review to Box. When your app calls POST /reviews with selected evidence file IDs, the service tags those files with the claim ID and begins a Box Automate workflow. Box Automate assigns the approval task, waits for the decision, then runs the approved or rejected branch.

Clone the working sample

Prefer to start from running code? The complete app built in this tutorial is on GitHub. Clone it, add your Box credentials, and run.
The Box Automate workflow endpoints used in this tutorial are Beta and are not available on the Free Developer Plan. Every request must include the box-version: 2026.0 header. Beta endpoints can change before general availability.

What you are building

By the end of this tutorial, you have a working Python service that:
  • Exposes a POST /reviews endpoint your claims system uses to start a review on selected evidence files.
  • Writes the claim ID onto each selected evidence file as Box metadata, so the workflow can read it as a variable.
  • Looks up the published Automate workflow attached to the claims folder and starts it on the selected files.
  • Receives a webhook when the reviewer approves or rejects, and patches reviewStatus on each file to approved or rejected.
Box Automate owns the review itself. Your service never polls for task state or implements approval logic. A V2 webhook closes the loop by writing the decision back to the file’s metadata.
Why a webhook updates review status. Box Automate assigns the approval task and runs approved or rejected branches, but it does not write file metadata when the task completes. Outbound custom HTTPS actions from Automate to your API are blocked for many URLs. A V2 webhook on TASK_ASSIGNMENT.UPDATED lets your service receive the decision and patch reviewStatus through the Metadata API.
Why metadata carries the claim ID. During the Beta, the start endpoint does not accept fields at runtime, so you cannot pass a claim ID in the request body. Writing the claim ID to each file as metadata makes it available as a workflow variable. When fields at start become available, you can pass the claim ID directly and drop this step.

Prerequisites

Before you start, make sure you have the following:
  • A Box Enterprise Advanced account with Box Automate enabled. See Enabling Box Automate.
  • A Box platform application configured with Client Credentials Grant authentication. Enable Read and write all files and folders stored in Box and Manage Webhooks. Set application access to App + Enterprise Access, and turn on Generate user access tokens. After you change these settings, re-authorize the app in the Admin Console.
  • The user ID of a managed user who can build and start Automate workflows (for testing, use your own user ID). The Automate list and start endpoints return workflows that user can start. They do not return results for the enterprise service account.
  • Admin access to create a metadata template, or a Box administrator who can create one for you.
  • Permission to build and publish workflows in the Box Automate builder.
  • Python 3.11 or higher.
  • A coding agent with installed. Run npx skills add box/box-for-ai in your project, or install the Cursor, Codex, or Claude Code plugin. See for setup.
Automate and metadata calls require root_readwrite, the OAuth scope behind Read and write all files and folders stored in Box. Webhook registration requires Manage Webhooks. If a scope or feature is missing from the Developer Console, contact Box Support with the user or app context you plan to use.

Configure Box once

These steps happen in the Box Admin Console and Automate builder. An agent cannot do them for you. Complete them before you build the service.
  1. Create a Claim metadata template in the Admin Console under Metadata. Add two text fields: Claim ID and Review status. Select Save, select the template again, then copy the template key. Confirm the generated field keys with GET /2.0/metadata_templates/enterprise/<TEMPLATE_KEY>/schema. This tutorial assumes claimId and reviewStatus.
  2. Configure Client Credentials Grant for a managed user. In Developer Console > your CCG app > Configuration, enable Read and write all files and folders stored in Box and Manage Webhooks. Set application access to App + Enterprise Access, enable Generate user access tokens, and save. Re-authorize the app in the Admin Console. Copy the managed user’s ID (Admin Console > Users & Groups, or GET /2.0/users/me with that user’s token). You pass this ID as BOX_USER_ID so the service authenticates as that user, not as the enterprise service account.
    A service-account token can often read the claims folder and write metadata, yet GET /2.0/automate_workflows still returns an empty entries list, and start returns 400 Action not found. Authenticate as a managed user who can run Automate.
  3. Create a Claims Review folder in Box as that managed user (or invite them as an Editor), note its folder ID from the URL, and upload two or three sample PDFs. Without folder access for the user your app acts as, file and metadata calls return 404.
  4. Build and publish a Manual Start workflow in the Box Automate builder. For a full walkthrough of the builder UI, see Creating workflows in Box Automate.
    1. Open Automate, select New+Workflow, and name it Claims evidence review.
    2. Drag Manual Start onto the canvas and scope it to the Claims Review folder.
    3. Add a Task Action outcome. Set the type to Approval, assign the file to Trigger: File, and for testing set both complete and manage to Workflow owner. Optionally include the Claim ID metadata field in the task message.
    4. Branch on Approved and Rejected, and add Send Notification on each branch.
    5. Select Activate (not only Save).
    The Manual Start folder must match the folder_id you query later. An unpublished draft, or a different folder, returns an empty entries list rather than an error. BOX_CLAIMS_WORKFLOW_NAME must match the published workflow name exactly. The folder display name is unrelated.
  5. Optional: inspect the workflow IDs so you recognize the response shape later. Use an access token for the same managed user your app will act as:
The top-level id is the action ID, not the workflow ID. Passing the action ID in the path returns a 404.

Build the service

Choose how to scaffold the service. Both paths produce the same app; continue to Run and verify when you finish.
With Box configured and installed, paste the following prompt into your coding agent. Replace the placeholders only if you want the agent to pre-fill .env; otherwise leave them and fill credentials yourself after scaffolding.
When the agent finishes, copy .env.example to .env and fill in your credentials, user ID, folder ID, and template key. Put each variable on its own line.

Run and verify

  1. Terminal 1 — start the server. Make sure you are in the claims-evidence-review directory and the virtual environment is activated:
You should see:
Leave this terminal running.
  1. Terminal 2 — start a review. Open a new terminal tab or window. Use file IDs from the sample PDFs in Claims Review, not the folder ID:
A successful response looks like:
  1. Confirm in Box:
    • Open an evidence file → Metadata tab shows claim CLM-1042 and status in_review.
    • As the task assignee, open the approval task and confirm the claim ID appears in the message.
    • Approve or reject from the file’s Activity sidebar panel, and confirm the matching branch runs.
reviewStatus stays in_review after this step. Continue to Update review status after the decision to patch metadata when the task completes.

Update review status after the decision

The workflow assigns the task and runs the approved or rejected branch, but it does not write back to the file’s metadata. To close the loop, add a that listens for TASK_ASSIGNMENT.UPDATED and patches reviewStatus to approved or rejected.

Add webhook signature keys

  1. In the Developer Console, open your app and select WebhooksManage signature keys. Generate a primary and secondary key.
  2. Add them to .env:
Use the keys generated for your Box app. Generic test values cannot validate signatures from Box.

Expose the app for webhook delivery

Box must reach your endpoint over HTTPS. For local testing, use a tunnel such as ngrok:
Copy the https:// forwarding URL. You need it for the registration step.
A tunnel forwards HTTPS traffic to your local Flask app at /webhooks/task-updated. A webhook delivered to a different public URL does not run this handler unless you forward the request or reimplement the metadata patch there.

Register the webhook

Run register_webhook.py once with the public URL of your endpoint. From the project directory with the virtual environment activated:
The script creates a V2 webhook on the claims folder for TASK_ASSIGNMENT.UPDATED. Register the webhook once per environment. If you create it in the Box UI instead, skip the script. If you create the webhook in the Box UI, set Content Type to Folder, select the Claims Review folder, and select Task Assignment Updated. In this form, content type means the target item type, not the HTTP Content-Type header.
Your app must have the Manage Webhooks scope enabled in the Developer Console. If the scope is unavailable, contact Box Support.

Add the webhook handler

If you used the agent prompt, the handler is already in app.py. If you built by hand, add the endpoint from step 6 of Build by hand. The handler:
  1. Verifies the webhook signature using the SDK’s WebhooksManager.validate_message.
  2. Reads the decision from source.resolution_state and the file ID from source.item.id.
  3. Normalizes the decision to approved or rejected and patches reviewStatus on the file.
If you process webhooks outside Flask, verify the Box signature, read source.resolution_state and source.item.id, normalize the decision to lowercase, and call PUT /2.0/files/:id/metadata/enterprise/:templateKey with a JSON Patch replace on /reviewStatus.

Verify the full loop

  1. Start a review with POST /reviews.
  2. Open the file’s Metadata tab and confirm that reviewStatus is in_review.
  3. Approve or reject the task from the file’s Activity sidebar.
  4. Confirm that the webhook endpoint returns 200.
  5. Refresh the file’s Metadata tab. reviewStatus now displays approved or rejected.
The gap is closed only when the metadata value changes. A delivered webhook does not update metadata unless your handler calls the Metadata API.

Troubleshooting

Your 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 the values in Developer Console > Configuration.
  • Ensure the app is authorized and its type is Client Credentials Grant.
Your app is requesting a user token, but the Developer Console is not set up for it. Confirm App + Enterprise Access and Generate user access tokens, then re-authorize the app in the Admin Console. Verify BOX_USER_ID is the managed user’s ID (digits only, no spaces).
The request succeeded, but no workflow matched for this caller. Check each of the following:
  • You are authenticated as a managed user who can run Automate, not as the enterprise service account. A user token that returns the workflow while the app returns entries: [] usually means the app still uses enterprise_id instead of user_id.
  • The workflow is published, not saved as a draft.
  • The workflow has a Manual Start trigger.
  • The trigger’s folder is the folder whose ID you passed as folder_id.
  • The managed user can access that folder.
Several causes produce a 404 here:
  • Box Automate is not enabled for your enterprise. Ask your admin to enable Box Automate.
  • Your account is on the Free Developer Plan, where these endpoints are unavailable.
  • The box-version: 2026.0 header is missing.
  • You passed the action ID in the URL path. The path takes entries[].workflow.id; the body takes entries[].id.
The workflow and action IDs can be valid for a different actor. Hardcoding IDs from a successful user-token curl does not help if the app still authenticates as the service account. Switch the client to BOX_USER_ID, then confirm that file_ids contains at least one ID, at most 20 IDs, and that every file is within the Manual Start folder scope.
The request reached the workflow but the payload was rejected. Confirm that file_ids contains at least one ID, that it holds no more than 20 IDs, and that every file is within the folder scope configured on the Manual Start trigger.
The workflow variable is not resolving to your metadata field. Confirm that the field keys in tag_evidence match the fields[].key values returned by GET /2.0/metadata_templates/enterprise/:key/schema, and that the task message references the Claim ID field from that template.
The list call succeeded, but find_workflow_action returned no match for BOX_CLAIMS_WORKFLOW_NAME. Confirm that the published workflow name matches the value in .env exactly. Rename the workflow in the Automate builder and activate it again, or set BOX_CLAIMS_WORKFLOW_NAME to the name shown in the error response. The folder display name is unrelated.
The app is missing a required scope. Enable Read and write all files and folders stored in Box for metadata and Automate calls, and Manage Webhooks for webhook registration. Depending on your authentication method and enterprise settings, the app may need admin authorization or reauthorization in the Admin Console before a scope change takes effect.
Confirm that BOX_WEBHOOK_PRIMARY_KEY and BOX_WEBHOOK_SECRET_KEY in .env match the keys shown in the Developer Console under WebhooksManage signature keys. If you recently rotated a key, the in-flight delivery may still carry the old signature. See .
Open the file’s Activity sidebar and confirm that the task has a final decision. Inspect the webhook body and confirm that source.resolution_state is approved or rejected and source.item.id is the evidence file ID. Box may send the decision in uppercase in the webhook body. Normalize it to lowercase before you compare or write metadata. If the webhook never arrives, verify that the webhook is registered and that your handler is reachable from the internet. An external processor must call the Metadata API to change reviewStatus.
The Automate endpoints are Beta and the Box Python SDK v10 does not yet provide an Automate workflows manager. Re-paste the prompt and emphasize client.make_request with box-version: 2026.0, or select the Build by hand tab in Build the service for the tested modules.

Scaling to production

The sample calls GET /2.0/automate_workflows on every review to resolve the workflow by name. That list call is optional in production. The IDs you need are stable for a given published workflow:Resolve them once at startup, or store them as configuration after a successful list call:
Then call start with those IDs and skip find_workflow_action. Keep the list lookup as a fallback if start begins failing (for example after you replace the workflow in the builder).
Caching IDs only skips the list call. Start still requires a managed user who can run Automate. Hardcoding IDs from a successful user-token curl does not work if the app authenticates as the enterprise service account. That case returns 400 Action not found, not an empty list.
A double-submitted request starts the workflow twice and can create duplicate approval tasks on the same evidence. You can use reviewStatus as a guard: skip the start if the value is already in_review. The webhook handler resets it to approved or rejected after the decision, so the field accurately reflects the current state.Alternatively, catch duplicates in your claims system. For example, ignore a second request with the same claim_id and file_ids while a review is still open. You can also list tasks on the file and skip start if an incomplete approval task already exists.
POST /reviews needs authentication in production. Verify a signed request from your claims system, or place the service behind your existing gateway. Keep credentials in a secret manager rather than a .env file, and never expose user or app tokens to a browser client. Prefer a dedicated managed user with the least Automate and folder access your flow needs, rather than a personal admin account.
Tagging metadata before the run is a workaround for a Beta limitation, and it costs one API call per file. When the start endpoint accepts fields at runtime, pass the claim ID directly in the start request and keep metadata only where you want the claim ID to persist on the file for search and reporting. Isolating the workaround in claims_metadata.py makes that a single-module change.
A single start request accepts 20 files at most. For larger evidence sets, split the files into batches and start one run per batch (each batch can create its own approval tasks), or restructure the workflow to trigger on the case folder rather than on individual files.

Next steps

Box Agent Skills

Install skills so your coding agent can scaffold more Box integrations from natural language.

Start Automate workflow

See the full API specification for the manual start endpoint.
Last modified on September 14, 2026