Skip to main content
Legal teams review the same handful of clauses in every non-disclosure agreement (NDA) and master service agreement (MSA): what law governs it, how long it lasts, how either side can walk away, how long confidentiality survives, and whether liability is capped. Reading each document by hand is slow, and asking a model open-ended questions (“What are the risky terms in this contract?”) returns different answers every time. This tutorial takes a more structured approach: extract, don’t prompt. You define a fixed extraction schema, so Box AI returns the same clauses in the same shape for every document. You then turn that predictable output into a Deal Risk Summary and save it back to Box as a companion file that reviewers can open next to the original.

What you are building

By the end of this tutorial, you have a working Python project that:
  • Authenticates to Box and reads an NDA stored in a Box folder.
  • Defines a clause-extraction schema for governing law, term, termination, confidentiality period, and liability cap.
  • Calls the POST /2.0/ai/extract_structured endpoint with confidence scores and source references enabled.
  • Applies a simple, transparent rule set to score each clause and assemble a Deal Risk Summary in JSON and markdown.
  • Uploads the summary back to Box as a companion file, so the analysis lives alongside the source document.
This tutorial focuses on an NDA, but the same schema and workflow apply to a master service agreement (MSA) or most other commercial contracts. Adjust the fields in schema.py to match the clauses you care about.

Why extract instead of prompting

A free-form prompt asks the model to decide what to return and how to format it, so the response shape drifts between documents and runs. Structured extraction inverts that: you declare the fields, and Box AI fills them in. Because the output is predictable, you can score it with plain code and trust that the summary looks the same for every contract. Enabling confidence scores and source references on the extraction call also lets you flag clauses that need a human’s eyes.
The risk scoring in this tutorial is illustrative and intended for triage only. It is not legal advice, and it does not replace review by a qualified professional. Treat the summary as a starting point that routes attention, not as a decision.

Prerequisites

Before you start, make sure you have the following:
  • A free , which gives you access to the Box AI API.
  • A Box application configured with Client Credentials Grant authentication.
  • Python 3.11 or higher.
  • An NDA uploaded to Box. You can download a and upload it to a Box folder. Note its file ID from the URL. For example, if the URL is https://app.box.com/file/123456789, the file ID is 123456789. Because a Client Credentials Grant app authenticates as a separate service account, add that service account as a collaborator with the Editor role on the file (or its parent folder) so it can read the NDA. You find the service account email in the Authenticate the Box client step.
  • A Box folder where the service account can write the summary, with the same service account added as an Editor collaborator. Note its folder ID from the URL.
  • The following scopes enabled on your app:
    • Read and write all files and folders stored in Box
    • Manage AI

Step-by-step process

This tutorial uses two Box Platform capabilities: This tutorial uses the default Extract Agent to keep the setup simple, since you don’t configure an agent. Confidence scoring is available either way: the default Extract Agent runs on Gemini 2.5 Flash and the Enhanced Extract Agent runs on Gemini 2.5 Pro, and both are confidence-capable Gemini models. If your NDAs have complex or scanned layouts, see Scaling to production for how to switch to the Enhanced Extract Agent without losing confidence scores.
Prefer not to build the project step by step? Clone the for this tutorial and run it directly.
1

Set up the development environment

  1. Open your terminal and create a new project directory:
  1. Create and activate a Python virtual environment:
After activation, your terminal prompt shows (.venv) at the beginning. This confirms you are working inside the virtual environment.
Every time you open a new terminal window or tab, re-activate the virtual environment by running source .venv/bin/activate from the project directory. If you see ModuleNotFoundError when running commands, it usually means the venv is not activated.
  1. Install the required packages:
Installing boxsdk gives you the box_sdk_gen module used throughout this tutorial. Confidence scores and source references are available directly on the SDK’s create_ai_extract_structured method through the include_confidence_score and include_reference parameters.
  1. Create a .env file to store your credentials, then add the following content. Replace the placeholder values with your actual credentials from the Box Developer Console:
Never commit .env files to version control. Add .env to your .gitignore.
2

Authenticate the Box client

In your project, create a file called box_client.py and add the following code. It builds an authenticated client that you use to call Box AI and the uploads API through the SDK.
A CCG application acts as a separate service account user that does not automatically have access to your content. Invite the service account email (found in the Developer Console under General Settings) as a collaborator with the Editor role on both the folder that contains your NDA and the output folder. Editor access is required because the app writes the summary file back to Box. Without access, API calls return 404 Not Found.
3

Define the clause schema

Create a file called schema.py and add the following code. Each field tells Box AI exactly what to look for. The prompt on each field adds context that improves accuracy on dense legal language. The fields are CreateAiExtractStructuredFields objects that you pass directly to the SDK’s extraction call in the next step.
A clear, specific prompt and description improve both the extracted value and its confidence score. See for how confidence is calculated.
4

Extract the clauses

Create a file called extract.py and add the following code. It uses the SDK’s create_ai_extract_structured method to send the schema to Box AI and asks for confidence scores and source references alongside the extracted values.
The response includes three parts you use in the next step:
  • answer holds the extracted value for each field.
  • confidence_score holds a level (LOW, MEDIUM, or HIGH) and a numeric score between 0 and 1 for each field.
  • reference holds the source text and its location in the document for each field.
reference returns the exact snippet and location Box AI used for each field. Its contents are passed straight through into your summary file in a later step so a reviewer can trace every value back to the source. For background, see the changelog entry.
5

Score risk and build the summary

Create a file called risk.py and add the following code. It applies a small, transparent rule set to each clause, assigns a severity, and assembles the Deal Risk Summary as a dictionary. It also renders a markdown version for humans.
Keep the rules in assess_clause obvious and easy to edit. Adjust APPROVED_JURISDICTIONS, the confidence threshold, or the clause-specific checks to match your organization’s playbook.
6

Save the summary back to Box

Create a file called app.py and add the following code. It runs the extraction, builds the summary, and uploads both the JSON and markdown versions to your output folder as companion files.
At this point, your project directory should contain the following files:
Uploading the summary as a new, separate file keeps the original NDA untouched. If you would rather version the source document itself, use client.uploads.upload_file_version(file_id, ...) instead, but note that replacing a PDF with a markdown or JSON body changes the file’s type.
7

Run the extraction

Make sure you are in the nda-risk-summary directory with the virtual environment activated, then run the script:
Box AI returns the extracted clauses together with a confidence score for each field. Your raw extraction looks similar to this (the reference object is omitted here for brevity):
The script then prints the Deal Risk Summary. Because the liability cap was extracted with low confidence, it is raised to medium risk, which sets the overall risk for the document:
Finally, open your output folder in Box. You should see two new files: NDA-123456789-risk-summary.json and NDA-123456789-risk-summary.md, sitting alongside your other content and ready for review.

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.
  • Confirm BOX_ENTERPRISE_ID is your enterprise ID.
  • Ensure your app is authorized in the Developer Console and uses Client Credentials Grant.
The service account does not have access to the file or folder. Invite the service account email (found in Developer Console > General Settings) as a collaborator with the Editor role on both the NDA’s folder and the output folder.
Confirm your extraction call sets include_confidence_score and include_reference to True, as shown in extract.py. Confidence estimation also depends on the model behind the extraction agent - see .
app.py uploads the summary using a deterministic name derived from the file ID (for example, NDA-123456789-risk-summary.json), so running the script again against the same output folder tries to create files that are already there. Delete the previous -risk-summary.json and -risk-summary.md files from the output folder, or point OUTPUT_FOLDER_ID at a different folder, before re-running.

Scaling to production

Instead of running the script by hand, register a webhook on your NDA folder so every upload triggers extraction and write-back. See for an end-to-end webhook pattern, and to validate incoming requests.
Use the confidence score to build a review queue: automatically accept high-confidence clauses and flag anything below your threshold for a person. See for suggested thresholds and patterns.
For multi-page contracts, dense tables, or scanned PDFs, switch to the for better accuracy. It runs on Gemini 2.5 Pro, which supports confidence scoring, so you keep confidence scores and references when you switch. Add the agent to your extraction call:
You can confirm which model processed a request by checking ai_agent_info.models in the response.
In addition to the companion file, write the top-level clause values back to the NDA as a Box . You can then filter contracts by governing law, overall risk, or confidentiality period across your whole repository.

Next steps

Supplier agreement extraction

Extract grouped and repeating structured data from a contract using the struct and table field types.

Extract API reference

See the full API specification for structured extraction.
Last modified on July 16, 2026