> ## Documentation Index
> Fetch the complete documentation index at: https://developer.box.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Build your company brain

> Turn scattered company content into governed knowledge. Review new files with Box AI and publish approved content to Box Hubs, allowing humans and AI agents to  work from the same trusted source.

export const RelatedLinks = ({title, items = []}) => {
  const getBadgeClass = badge => {
    if (!badge) return "badge-default";
    const badgeType = badge.toLowerCase().replace(/\s+/g, "-");
    return `badge-${badge === "ガイド" ? "guide" : badgeType}`;
  };
  if (!items || items.length === 0) {
    return null;
  }
  return <div className="my-8">
      {}
      <h3 className="text-sm font-bold uppercase tracking-wider mb-4">{title}</h3>

      {}
      <div className="flex flex-col gap-3">
        {items.map((item, index) => <a key={index} href={item.href} className="py-2 px-3 rounded related_link hover:bg-[#f2f2f2] dark:hover:bg-[#111827] flex items-center gap-3 group no-underline hover:no-underline border-b-0">
            {}
            <span className={`px-2 py-1 rounded-full text-xs font-semibold uppercase tracking-wide flex-shrink-0 ${getBadgeClass(item.badge)}`}>
              {item.badge}
            </span>

            {}
            <span className="text-base">{item.label}</span>
          </a>)}
      </div>
    </div>;
};

export const SignupCTA = ({children}) => {
  return <div className="flex flex-wrap items-center gap-4 p-5 rounded-lg border border-gray-200 dark:border-gray-700 my-6" style={{
    background: "linear-gradient(135deg, rgba(0, 97, 213, 0.06), rgba(0, 97, 213, 0.02))"
  }}>
      <div className="flex-1 text-sm leading-relaxed text-gray-700 dark:text-gray-300" style={{
    minWidth: "280px"
  }}>
        {children}
      </div>
      <div className="flex flex-col items-center gap-2">
        <a href="https://account.box.com/signup/developer#ty9l3" className="signup-cta-button inline-flex items-center whitespace-nowrap px-5 py-2 text-sm font-semibold text-white no-underline">
          {translate("Get started for free")}
        </a>
        <a href="https://account.box.com/developers/console" className="signup-cta-login text-xs text-gray-500 dark:text-gray-400 no-underline whitespace-nowrap">
          {translate("Already have an account? Log in")}
        </a>
      </div>
    </div>;
};

export const Link = ({href, children, className, ...props}) => {
  const localizedHref = localizeLink(href);
  return <a href={localizedHref} className={className} {...props}>
      {children}
    </a>;
};

A personal AI knowledge base - a folder of notes, research, and strategy docs that an agent can read and act on - works well for one person. Startups and enterprises, however, need a shared source of truth that multiple people and multiple agents can use safely. Teams have to answer questions like:

* Who can access specific information, and who can update it?
* Which version is current?
* How do you review agent-generated changes?
* Can an answer be traced back to the source material it came from?

The answer is a **shared company brain**: a governed repository of company context, product knowledge, operating rules, and generated outputs that humans and agents use to operate the business. This provides the same trusted content whether it is reached by a developer's local coding agent, a cloud agent over MCP, or a person in the Box web app.

This tutorial builds that knowledge layer with Box:

1. You organize content into a **domain-based structure** - one curated knowledge base per domain - with a staging area for new files and an approved area for trusted ones.
2. When a file lands in staging, you use **Box AI** to review it: summarize it, score its content health, suggest a domain and tags, and flag issues like missing owners or stale guidance.
3. You record that review as **Box metadata** so every file carries its governance status, and you promote approved content into a **Box Hub** - the trusted destination that agents query.
4. Humans and agents query the brain through **Box AI**, getting answers grounded in approved, permission-controlled content, with citations back to the source.

## What you are building

By the end of this tutorial, you have a working Python project that:

* Provisions a governed folder structure - the company brain - organized by domain, each with a staging and an approved area.
* Uses a dedicated **service account** as the shared, governed identity that both your automation and your agents act through.
* Reviews staged files with Box AI, producing a summary, a suggested domain, tags, and a content health score, then flags quality issues.
* Writes the review back to each file as a governance metadata instance, so status and ownership are searchable and auditable.
* Publishes approved content to a domain Box Hub and answers natural-language questions grounded in that trusted content.
* Gives your local and cloud agents a safe, permission-aware way to work from the same source of truth.

## Prerequisites

Before you start, make sure you have the following:

* A <Link href="https://account.box.com/signup/developer#ty9l3">free Box developer account</Link>, or a <Link href="https://www.box.com/pricing">Box Enterprise account</Link> with **Box AI** and **Box Hubs** enabled. See <Link href="https://docs.box.com/en/box-hubs/box-hubs-for-admins/configuring-box-hubs">Configuring Box Hubs</Link>.
* A Box platform application configured with **Client Credentials Grant** authentication.
* **Admin access** to create a metadata template (Step 2), or a Box administrator who can create one for you.
* Python 3.11 or higher.
* The following scopes enabled on your platform app:
  * Read and write all files and folders stored in Box
  * Manage AI
  * Manage enterprise properties (required to create and write metadata)

## Step-by-step process

A company brain is built from four Box Platform capabilities working together. Each one maps to a requirement that a shared, trusted knowledge layer must meet.

| Component                   | Purpose                                                                   | API                                             |
| --------------------------- | ------------------------------------------------------------------------- | ----------------------------------------------- |
| **Folders**                 | Organize content by domain, and separate staged files from approved ones  | `GET /2.0/folders/:id/items`                    |
| **Box AI Extract**          | Review staged content: summarize, score health, suggest a domain and tags | `POST /2.0/ai/extract_structured`               |
| **Metadata**                | Record governance state (domain, owner, status, health) on each file      | `POST /2.0/files/:id/metadata/:scope/:template` |
| **Hubs** and **Box AI Ask** | Publish approved content and answer questions grounded in it              | `POST /2.0/hubs`, `POST /2.0/ai/ask`            |

<Info>
  Box Hubs and Box AI inherit permissions from the underlying source files. Both people and agents only ever see answers derived from content they already have access to in Box. The company brain does not add a separate access-control layer - it reuses the one you already trust.
</Info>

<Steps>
  <Step title="Design your company brain structure">
    The best knowledge bases are domain-based, not generic. Rather than one giant brain, create a focused knowledge base per domain - security, legal, product, and so on - and let each domain team own its content.

    Within each domain, separate content that has been reviewed from content that has not. New files land in a `_staging` area; only approved files move to the `approved` area that agents are pointed at.

    Create a folder structure in Box similar to the following:

    ```
    Company Brain/
    ├── security/
    │   ├── _staging/        # new or updated files awaiting review
    │   └── approved/        # trusted, published knowledge
    ├── legal/
    │   ├── _staging/
    │   └── approved/
    ├── product/
    │   ├── _staging/
    │   └── approved/
    ├── operating-rules/     # rules that govern how agents behave
    │   └── agent-operating-rules.md
    └── outputs/             # content generated by agents, saved back
    ```

    Note the **folder ID** of the top-level `Company Brain` folder, and the ID of one domain's `_staging` folder (for example, `security/_staging`). You use these in later steps. The folder ID is the number at the end of the folder's URL: `https://app.box.com/folder/123456789` means the folder ID is `123456789`.

    <Tip>
      The `operating-rules` folder is part of the brain, not an afterthought. Storing your agent operating rules alongside the knowledge they govern means every agent - local or cloud - reads the current rules from the same place, instead of each teammate copying yesterday's version into their own workspace.
    </Tip>

    <Note>
      This tutorial works one domain end to end (for example, `security`). Once the pattern works, repeat it per domain. Adding a domain is easy: a new folder pair and a new Hub, owned by the team closest to the work.
    </Note>
  </Step>

  <Step title="Create the knowledge governance metadata template">
    Governance is what separates a trusted knowledge base from a folder of files. A metadata template lets every file carry the same governance fields - which domain it belongs to, who owns it, whether it is approved, and how healthy its content is - so status is explicit, searchable, and auditable.

    <Tip>
      This step requires Admin access. If you do not have access, contact your Box administrator.
    </Tip>

    1. Open the [Box Admin Console](https://app.box.com/master) and select **Metadata**.
    2. Click **New** and name the template `Knowledge Governance`.
    3. Add the following fields:

    | Field name           | Type   | Description                                                           |
    | -------------------- | ------ | --------------------------------------------------------------------- |
    | Domain               | Text   | The domain this file belongs to, for example `security`               |
    | Owner                | Text   | The person or team accountable for keeping this file current          |
    | Status               | Text   | `staged`, `approved`, or `rejected`                                   |
    | Content Health Score | Number | A 0-100 score for how usable this content is as knowledge             |
    | Summary              | Text   | A short, agent-readable summary of the file                           |
    | Tags                 | Text   | Comma-separated tags for retrieval                                    |
    | Review Notes         | Text   | Issues flagged during review, for example missing owner or stale date |

    4. Click **Save**.
    5. Click back into your newly created template and copy the **template key** shown under the template name. You need it in a later step.

    <Note>
      Box derives each field's **key** from its display name in camel case - `Content Health Score` becomes `contentHealthScore`. The code in Step 6 uses these keys. If you rename a field, confirm its key in the Admin Console and update the code to match. For a detailed walkthrough, see <Link href="https://docs.box.com/en/box-admin-tools/managing-content/metadata/customizing-metadata-templates">Customizing Metadata Templates</Link>.
    </Note>
  </Step>

  <Step title="Set up the development environment">
    1. Open your terminal and create a new project directory:

    ```bash theme={null}
    mkdir company-brain && cd company-brain
    ```

    2. Create and activate a Python virtual environment:

    ```bash theme={null}
    python3 -m venv .venv
    source .venv/bin/activate
    ```

    After activation, your terminal prompt shows `(.venv)` at the beginning. This confirms you are working inside the virtual environment.

    <Note>
      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.
    </Note>

    3. Install the required packages:

    ```bash theme={null}
    pip install box-sdk-gen python-dotenv
    ```

    4. 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:

    ```bash theme={null}
    BOX_CLIENT_ID=your_client_id
    BOX_CLIENT_SECRET=your_client_secret
    BOX_ENTERPRISE_ID=your_enterprise_id
    BOX_METADATA_TEMPLATE_KEY=your_template_key
    STAGING_FOLDER_ID=your_domain_staging_folder_id
    APPROVED_FOLDER_ID=your_domain_approved_folder_id
    ```

    <Warning>
      Never commit `.env` files to version control. Add `.env` to your `.gitignore`.
    </Warning>

    <Note>
      **Understanding environment variables:** The `.env` file stores sensitive values (your actual credentials). Your Python code reads these values by referencing their **names** using `os.getenv("VARIABLE_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.
    </Note>
  </Step>

  <Step title="Authenticate as a service account">
    A company brain is shared, so it should be reached through a shared, governed identity rather than any one person's account. Client Credentials Grant gives your automation - and the agents you connect later - their own **service account** in Box. That service account is the security boundary: it can only touch content you explicitly share with it.

    Create a file called `box_client.py` and add the following code:

    ```python theme={null}
    import os
    from dotenv import load_dotenv
    from box_sdk_gen import BoxClient, BoxCCGAuth, CCGConfig

    load_dotenv()

    def get_box_client() -> BoxClient:
        config = CCGConfig(
            client_id=os.getenv("BOX_CLIENT_ID"),
            client_secret=os.getenv("BOX_CLIENT_SECRET"),
            enterprise_id=os.getenv("BOX_ENTERPRISE_ID"),
        )
        auth = BoxCCGAuth(config=config)
        return BoxClient(auth=auth)
    ```

    <Warning>
      **Share the company brain with the service account.** A CCG app acts as a separate service account user that does not automatically have access to your content. Invite the service account email as a collaborator on the top-level `Company Brain` folder with the **Editor** role - Editor is required so the app can write metadata and move approved files.

      To find the email: go to the [Developer Console](https://app.box.com/developers/console), open your app, and look in the **App Details** sidebar for the **Service Account ID** (it looks like `AutomationUser_xxxxx_xxxxxx@boxdevedition.com`). Without this, API calls return `404 Not found`.
    </Warning>

    <Tip>
      Sharing only the `Company Brain` folder - not your whole account - is the point. The service account gets exactly the access the brain needs and nothing more. This is the same boundary you rely on later when an agent authenticates as this identity.
    </Tip>
  </Step>

  <Step title="Review staged content with Box AI">
    If an agent can search everything, it can also find the wrong thing - an old scope deck, an unreviewed draft, a template with outdated assumptions. For a person that is annoying; for an agent it is a trust problem. So before a file becomes part of the trusted layer, it goes through an intake review.

    Instead of prompting Box AI with an open-ended question, you define a fixed review schema so every file comes back in the same shape. Create a file called `review.py`:

    ```python theme={null}
    from box_sdk_gen import (
        AiItemBase,
        CreateAiExtractStructuredFields,
        CreateAiExtractStructuredFieldsOptionsField,
    )
    from box_client import get_box_client

    REVIEW_FIELDS = [
        CreateAiExtractStructuredFields(
            key="summary",
            display_name="Summary",
            type="string",
            prompt="A two to three sentence summary of what this document contains.",
        ),
        CreateAiExtractStructuredFields(
            key="suggested_domain",
            display_name="Suggested domain",
            type="enum",
            prompt="The single domain this content best belongs to.",
            options=[
                CreateAiExtractStructuredFieldsOptionsField(key="security"),
                CreateAiExtractStructuredFieldsOptionsField(key="legal"),
                CreateAiExtractStructuredFieldsOptionsField(key="product"),
                CreateAiExtractStructuredFieldsOptionsField(key="other"),
            ],
        ),
        CreateAiExtractStructuredFields(
            key="suggested_tags",
            display_name="Suggested tags",
            type="string",
            prompt="Three to six comma-separated topic tags for retrieval.",
        ),
        CreateAiExtractStructuredFields(
            key="content_health_score",
            display_name="Content health score",
            type="float",
            prompt=(
                "A score from 0 to 100 for how usable this is as trusted knowledge. "
                "Lower the score for stale dates, missing ownership, unclear status, "
                "or content that reads like an unreviewed draft."
            ),
        ),
        CreateAiExtractStructuredFields(
            key="review_notes",
            display_name="Review notes",
            type="string",
            prompt=(
                "Concise notes on any issues an owner should resolve before approval, "
                "such as missing owner, stale dates, duplicate guidance, or draft assumptions."
            ),
        ),
    ]

    def review_file(file_id: str) -> dict:
        client = get_box_client()

        response = client.ai.create_ai_extract_structured(
            items=[AiItemBase(id=file_id)],
            fields=REVIEW_FIELDS,
        )

        return response.to_dict()["answer"]
    ```

    The fixed schema is what makes the review dependable: the same fields, in the same shape, for every file - so the intake step behaves consistently no matter who uploaded the document or how it is formatted.

    <Tip>
      Treat the content health score as a signal, not a verdict. A low score means "an owner should look at this before agents rely on it," which is exactly what the approval step in Step 6 is for.
    </Tip>
  </Step>

  <Step title="Record the review as governance metadata">
    A review is only useful if it stays attached to the file. Writing the result back as a metadata instance means every file carries its own governance record - domain, owner, status, and health - that both people and agents can see, search, and audit.

    Create a file called `intake.py`. It reviews every file in a domain's staging folder and stamps each one with governance metadata:

    ```python theme={null}
    import os
    from dotenv import load_dotenv
    from box_sdk_gen import BoxClient, CreateFileMetadataByIdScope

    from box_client import get_box_client
    from review import review_file

    load_dotenv()

    def build_governance(review: dict) -> dict:
        return {
            "domain": review.get("suggested_domain", "other"),
            "owner": "unassigned",
            "status": "staged",
            "contentHealthScore": review.get("content_health_score", 0),
            "summary": review.get("summary", ""),
            "tags": review.get("suggested_tags", ""),
            "reviewNotes": review.get("review_notes", ""),
        }

    def apply_governance(client: BoxClient, file_id: str, governance: dict):
        template_key = os.getenv("BOX_METADATA_TEMPLATE_KEY")
        client.file_metadata.create_file_metadata_by_id(
            file_id=file_id,
            scope=CreateFileMetadataByIdScope.ENTERPRISE,
            template_key=template_key,
            request_body=governance,
        )

    def process_staging_folder(client: BoxClient, folder_id: str):
        items = client.folders.get_folder_items(folder_id).entries

        for item in items:
            if item.type != "file":
                continue

            print(f"\nReviewing: {item.name} ({item.id})")
            review = review_file(item.id)
            governance = build_governance(review)

            print(f"  Domain:  {governance['domain']}")
            print(f"  Health:  {governance['contentHealthScore']}")
            print(f"  Summary: {governance['summary']}")
            if governance["reviewNotes"]:
                print(f"  Notes:   {governance['reviewNotes']}")

            apply_governance(client, item.id, governance)
            print("  Governance metadata applied.")

    def main():
        client = get_box_client()
        staging_folder_id = os.getenv("STAGING_FOLDER_ID")
        process_staging_folder(client, staging_folder_id)

    if __name__ == "__main__":
        main()
    ```

    Upload a document or two to your domain's `_staging` folder in Box, then run:

    ```bash theme={null}
    python3 intake.py
    ```

    For each staged file you see the suggested domain, a health score, a summary, and any review notes - and each file is stamped with a `staged` governance record.

    <Note>
      The intake step sets `status` to `staged` and `owner` to `unassigned` on purpose. A file being uploaded does not make it trusted knowledge. A human still decides what gets approved and who owns it - the next step.
    </Note>

    <Tip>
      To run intake automatically whenever a file lands in staging, trigger it from a folder event instead of running it by hand. See <Link href="/guides/box-automate/index">Box Automate</Link> for event-driven routing and approval tasks, or register a <Link href="/guides/webhooks/v2/signatures-v2">webhook</Link> on the staging folder.
    </Tip>
  </Step>

  <Step title="Approve content and publish it to a domain Hub">
    Once a domain owner is satisfied with a file, it becomes trusted knowledge and needs a destination where both people and agents can find it. A Box Hub is that destination: a curated, permission-inheriting knowledge base for one domain. Pointing agents at a small set of approved content - rather than every file the company has - is what keeps answers grounded and trustworthy.

    Create a file called `publish.py`. It marks a file as approved, moves it into the domain's `approved` folder, and ensures a domain Hub exists and contains that folder:

    ```python theme={null}
    import os
    from dotenv import load_dotenv
    from box_sdk_gen import (
        BoxClient,
        UpdateFileMetadataByIdScope,
        UpdateFileMetadataByIdRequestBody,
        UpdateFileMetadataByIdRequestBodyOpField,
        UpdateFileByIdParent,
        FolderReferenceV2025R0,
        HubItemOperationV2025R0,
        HubItemOperationV2025R0ActionField,
    )

    from box_client import get_box_client

    load_dotenv()

    def approve_file(client: BoxClient, file_id: str, owner: str):
        template_key = os.getenv("BOX_METADATA_TEMPLATE_KEY")
        client.file_metadata.update_file_metadata_by_id(
            file_id=file_id,
            scope=UpdateFileMetadataByIdScope.ENTERPRISE,
            template_key=template_key,
            request_body=[
                UpdateFileMetadataByIdRequestBody(
                    op=UpdateFileMetadataByIdRequestBodyOpField.REPLACE,
                    path="/status",
                    value="approved",
                ),
                UpdateFileMetadataByIdRequestBody(
                    op=UpdateFileMetadataByIdRequestBodyOpField.REPLACE,
                    path="/owner",
                    value=owner,
                ),
            ],
        )
        print(f"Approved file {file_id}, owner set to {owner}")

    def move_to_approved(client: BoxClient, file_id: str, approved_folder_id: str):
        client.files.update_file_by_id(
            file_id, parent=UpdateFileByIdParent(id=approved_folder_id)
        )
        print(f"Moved file {file_id} to approved folder {approved_folder_id}")

    def create_domain_hub(client: BoxClient, domain: str) -> str:
        hub = client.hubs.create_hub_v2025_r0(
            f"{domain.title()} Knowledge Base",
            description=(
                f"Curated, approved {domain} knowledge for the company brain. "
                "Answers are grounded in reviewed, source-backed content."
            ),
        )
        print(f"Created hub: {hub.title} (ID: {hub.id})")
        return hub.id

    def add_folder_to_hub(client: BoxClient, hub_id: str, folder_id: str):
        client.hub_items.manage_hub_items_v2025_r0(
            hub_id,
            operations=[
                HubItemOperationV2025R0(
                    action=HubItemOperationV2025R0ActionField.ADD,
                    item=FolderReferenceV2025R0(id=folder_id),
                )
            ],
        )
        print(f"Added approved folder {folder_id} to hub {hub_id}")

    def main():
        client = get_box_client()

        approved_folder_id = os.getenv("APPROVED_FOLDER_ID")

        # Replace with a file ID from your staging folder and the accountable owner.
        file_id = "YOUR_FILE_ID"
        owner = "security-team@example.com"
        domain = "security"

        approve_file(client, file_id, owner)
        move_to_approved(client, file_id, approved_folder_id)

        hub_id = create_domain_hub(client, domain)
        add_folder_to_hub(client, hub_id, approved_folder_id)

        print(f"\n{domain.title()} knowledge base ready. Hub ID: {hub_id}")

    if __name__ == "__main__":
        main()
    ```

    Run it once for a file you want to approve:

    ```bash theme={null}
    python3 publish.py
    ```

    Save the hub ID from the output - you need it to query the brain.

    <Note>
      You create a domain's Hub once, then reuse it. In production, look up the existing Hub for a domain and only create one if it does not exist, rather than creating a new Hub on every approval. Adding a folder to a Hub automatically indexes the files within it, and new files added to that folder are picked up automatically.
    </Note>

    <Tip>
      Control who can query the knowledge base by adding Hub collaborations - `viewer` for people who consume knowledge, `editor` for the domain owners who curate it. See <Link href="/guides/hubs-api/hubs-collaborations/hub-collaborations">Manage hub collaborations</Link>.
    </Tip>
  </Step>

  <Step title="Query the company brain">
    With approved content published, both people and agents can ask natural-language questions and get answers grounded in reviewed, source-backed material - with citations back to the files the answer came from. Create a file called `ask.py`:

    ```python theme={null}
    from dotenv import load_dotenv
    from box_sdk_gen import BoxClient, CreateAiAskMode, AiItemAsk

    from box_client import get_box_client

    load_dotenv()

    def ask_company_brain(client: BoxClient, hub_id: str, question: str):
        response = client.ai.create_ai_ask(
            mode=CreateAiAskMode.SINGLE_ITEM_QA,
            prompt=question,
            items=[AiItemAsk(type="hubs", id=hub_id)],
            include_citations=True,
        )
        return response

    def main():
        client = get_box_client()

        hub_id = "YOUR_HUB_ID"  # From Step 7

        questions = [
            "What is our approved policy for handling customer data at rest?",
            "Which security controls must a new integration pass before launch?",
        ]

        for question in questions:
            print(f"\nQ: {question}")
            response = ask_company_brain(client, hub_id, question)
            print(f"A: {response.answer}")

            if response.citations:
                print("Sources:")
                for citation in response.citations:
                    print(f"  - {citation.name}")

    if __name__ == "__main__":
        main()
    ```

    Replace `YOUR_HUB_ID` with the hub ID from the previous step, then run:

    ```bash theme={null}
    python3 ask.py
    ```

    Because the Hub inherits Box permissions, the answer only ever draws on documents the querying identity can access, and each answer lists the approved files it came from. The result is a knowledge base agents can depend on, instead of a guess assembled from a random mix of search results.

    At this point, your project directory should contain the following files:

    ```
    company-brain/
    ├── .env
    ├── .venv/
    ├── ask.py
    ├── box_client.py
    ├── intake.py
    ├── publish.py
    └── review.py
    ```

    <Warning>
      After publishing content to a Hub, allow a few minutes for Box to index the files before querying. Queries against newly added content may return empty or incomplete results until indexing completes.
    </Warning>
  </Step>

  <Step title="Connect your agents to the company brain">
    <Info>
      This step is guidance rather than code you run now. It shows how the same governed brain you just built becomes the memory layer for your agents, wherever they run.
    </Info>

    The whole point of a company brain is that it stays put while the agents change. Different teammates use different tools - a coding agent, a cloud assistant, a workflow bot - and the brain should not have to change as the agent changes; it should keep compounding. Box exposes the same source of truth through two access paths:

    * **Local agents** (for example, a coding agent working against synced files) reach the brain through Box Drive or the <Link href="/guides/cli/index">Box CLI</Link>, treating it like a local repository. Authenticate the CLI as the *service account* from Step 4 so the agent inherits exactly the access you granted the brain. More information is available on how to <Link href="/tutorials/connect-an-agent-to-box#set-up-the-connection">authenticate into Box CLI with a CCG app</Link>.
    * **Cloud agents** (for example, an assistant in a chat app) reach the same content through the <Link href="/guides/box-mcp/index">Box MCP server</Link>, with permission-aware access and admin controls. See more information on how to <Link href="/tutorials/connect-an-agent-to-box#box-mcp-hosted-server">set up the connection with Box MCP</Link>.

    Both are working from the same underlying files, permissions, and versions. Store the rules that govern agent behavior in the `operating-rules` folder so every agent reads the current version from the brain itself. For example:

    ```text theme={null}
    - Always authenticate as the Box service account, never a personal user.
    - Only operate inside the approved folders and Hubs you are given.
    - Answer only from approved content, and cite the source files.
    - Ask before deleting or overwriting content; preserve version history.
    - Save generated outputs back to the outputs folder for reuse.
    ```

    <Tip>
      Version history, review workflows, and audit trails are not extra work you bolt on - they come from Box. When an agent writes an output back to the brain, it inherits the same governance every human contribution does.
    </Tip>
  </Step>

  <Step title="Keep the brain healthy">
    <Info>
      This step provides reference patterns for keeping the knowledge base trustworthy over time. You do not need to run it now.
    </Info>

    The default failure mode of any knowledge base is drift: policies go stale, best practices stay trapped in one team, and approved content slowly ages out of date. A healthy company brain closes the loop - the organization's own work continuously improves it.

    Use the governance metadata you are already writing to surface content that needs attention. A <Link href="/guides/metadata/queries">metadata query</Link> can find every approved file below a health threshold, or every file still `staged` after too long:

    ```python theme={null}
    import os
    from box_sdk_gen import BoxClient

    def find_low_health_content(client: BoxClient, template_key: str, threshold: int = 60):
        enterprise_id = os.getenv("BOX_ENTERPRISE_ID")
        results = client.search.search_by_metadata_query(
            from_=f"enterprise_{enterprise_id}.{template_key}",
            ancestor_folder_id="0",  # search the whole enterprise; scope to the brain folder in production
            query="status = :status AND contentHealthScore < :score",
            query_params={"status": "approved", "score": threshold},
        )
        for entry in results.entries:
            print(f"Needs review: {entry.name} ({entry.id})")
    ```

    Feed those signals back to the domain owner. Every workflow also generates signals worth capturing - missing context, repeated review comments, gaps an agent could not answer. Route them into the next review cycle so the brain gets more valuable the more it is used.

    <Note>
      The `from` value must include your enterprise ID. Use `enterprise_{BOX_ENTERPRISE_ID}.{template_key}` - for example, `enterprise_123456789.knowledgeGovernance`.
    </Note>

    <Tip>
      Pair this with a scheduled review per domain. The owner re-reviews low-health and aging content, updates or archives it, and the Hub reindexes automatically. Ownership is what keeps quality from degrading - assign every domain an accountable owner.
    </Tip>
  </Step>
</Steps>

## Troubleshooting

<AccordionGroup>
  <Accordion title="ModuleNotFoundError: No module named '...'">
    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.
  </Accordion>

  <Accordion title="invalid_client: The client credentials are invalid">
    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 (Admin Console > Account & Billing, or Developer Console > icon in the top-right > Copy Enterprise ID).
    * Ensure your app is authorized in the Developer Console and uses Client Credentials Grant.
  </Accordion>

  <Accordion title="404 Not Found">
    The service account does not have access to the folder or file. Invite the service account email (Developer Console > General Settings) as a collaborator with the **Editor** role on the top-level `Company Brain` folder. Because subfolders inherit collaboration, this grants access to every domain, staging, and approved folder inside it.
  </Accordion>

  <Accordion title="metadata_template must have both scope and template_key">
    The `BOX_METADATA_TEMPLATE_KEY` value in your `.env` file is missing or empty. Add the template key you copied when creating the `Knowledge Governance` template in Step 2.
  </Accordion>

  <Accordion title="Metadata write fails with 400 bad_request: Request contains invalid parameters">
    This means the request reached your template (a missing template returns `404`, not `400`), but one of the keys in the payload is not a field in the template, or a value breaks a field's type. The most common cause is a field name mismatch - for example, if the first field was created as `Domain` in the Admin Console, Box generates the key `domain`, but if it was named anything else the key differs and the `domain` key the code sends is rejected.

    Every key in `build_governance` (`domain`, `owner`, `status`, `contentHealthScore`, `summary`, `tags`, `reviewNotes`) must exactly match a field key in your template. Print the actual keys and types to compare:

    ```python theme={null}
    from box_sdk_gen import GetMetadataTemplateScope
    from box_client import get_box_client

    client = get_box_client()
    template = client.metadata_templates.get_metadata_template(
        GetMetadataTemplateScope.ENTERPRISE, "knowledgeGovernance"
    )
    for field in template.fields:
        print(field.type, field.key, "-", field.display_name)
    ```

    Then either rename or add the field in the Admin Console so its key matches (field keys are immutable once created, so add a correctly named field rather than trying to rename the key), or update the dict keys in `build_governance` to match what the template actually uses. A `Number` field also rejects non-numeric values, so make sure the health score is sent as a number.
  </Accordion>

  <Accordion title="Box AI returns empty or irrelevant answers">
    This usually means the Hub content has not finished indexing, or the approved folder is empty:

    * Wait a few minutes after publishing before querying. Indexing is not instantaneous.
    * Verify the approved folder contains files (not just subfolders) and that they contain text (scanned image PDFs without OCR are not searchable).
    * Confirm you are querying the correct hub ID from Step 7.
  </Accordion>
</AccordionGroup>

## Scaling to production

<AccordionGroup>
  <Accordion title="Automate the intake and approval loop">
    Instead of running `intake.py` by hand, trigger the review when a file lands in a `_staging` folder and route the result to the right owner as an approval task. See <Link href="/guides/box-automate/index">Box Automate</Link> for event-driven workflows, or register a <Link href="/guides/webhooks/v2/signatures-v2">webhook</Link> on each staging folder.
  </Accordion>

  <Accordion title="One hub per domain, combined per task">
    Provision a dedicated Hub for each domain and let agents combine the hubs a task needs - a product-planning agent might draw on product, legal, and security, while a code-review agent draws on architecture, security, and accessibility. Loop over your domains to create their folder pairs and Hubs, and give each domain an accountable owner.
  </Accordion>

  <Accordion title="Support conversational follow-ups">
    The `POST /2.0/ai/ask` endpoint accepts a `dialogue_history` parameter carrying previous question-and-answer pairs. Maintain a list of exchanges and pass it with each request so users can ask follow-ups without losing context. See <Link href="/tutorials/sales-rfp-answer-bank">Build a sales RFP answer bank</Link> for a conversational implementation.
  </Accordion>

  <Accordion title="Govern content lifecycle">
    Combine the brain with <Link href="/guides/retention-policies">retention policies</Link> to archive or delete outdated material automatically, and use enterprise events to see which content agents rely on most. Treat the knowledge layer as enterprise infrastructure, not an AI feature - the organizations that build it as a governed, reusable, permission-aware layer gain an advantage every time a new agent enters the business.
  </Accordion>
</AccordionGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="Sales RFP answer bank" href={localizeLink("/tutorials/sales-rfp-answer-bank")} icon="magnifying-glass" arrow="true">
    Apply the same Hubs and Box AI pattern to a sales knowledge base, and embed it in your CRM.
  </Card>

  <Card title="Box Hubs API reference" href={localizeLink("/guides/hubs-api/index")} icon="server" arrow="true">
    See the full Hubs API guide for advanced hub management.
  </Card>
</CardGroup>

<RelatedLinks
  title="RELATED GUIDES"
  items={[
{ label: translate("Box Hubs overview"), href: "/guides/hubs-api/index", badge: "GUIDE" },
{ label: translate("Ask questions to Box AI"), href: "/guides/box-ai/ai-tutorials/ask-questions", badge: "GUIDE" },
{ label: translate("Extract metadata from file (structured)"), href: "/guides/box-ai/ai-tutorials/extract-metadata-structured", badge: "GUIDE" },
{ label: translate("Box MCP server"), href: "/guides/box-mcp/index", badge: "GUIDE" },
{ label: translate("Working with metadata"), href: "/guides/metadata/index", badge: "GUIDE" }
]}
/>
