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

# Knowledge base

> Manage documents, attach them to genies, and sync content across agents

The knowledge base stores documents that genies reference during conversations. Documents can be created from PDFs, websites, YouTube videos, or plain text. Each document is stored both in the HelpGenie database and in the voice agent system.

<Note>
  Standard users can only access their own documents. Admin users can access all documents and filter by owner when passing `adminMode: true`.
</Note>

***

## List all documents

Retrieves all documents with pagination, filtering, and role-based access control. Returns documents along with their dependent agents.

<ParamField body="resource" type="string" required>
  Must be `"knowledge-base"`
</ParamField>

<ParamField body="action" type="string" required>
  Must be `"all"`
</ParamField>

<ParamField body="data" type="object">
  <Expandable title="properties">
    <ParamField body="limit" type="number" default="50">
      Results per page. Maximum 500.
    </ParamField>

    <ParamField body="offset" type="number" default="0">
      Number of results to skip.
    </ParamField>

    <ParamField body="filters" type="object">
      <Expandable title="properties">
        <ParamField body="searchTerm" type="string">
          Filter by document name (case-insensitive).
        </ParamField>

        <ParamField body="selectedPrivacy" type="string">
          Filter by visibility: `"public"` or `"private"`.
        </ParamField>

        <ParamField body="selectedSourceType" type="string">
          Filter by document source type (for example `"pdf_upload"`, `"website"`, `"youtube"`).
        </ParamField>

        <ParamField body="selectedActivityStatus" type="string">
          Filter by active status: `"active"` or `"inactive"`.
        </ParamField>

        <ParamField body="selectedProcessingStatus" type="string">
          Filter by processing status (for example `"completed"`, `"pending"`, `"failed"`).
        </ParamField>

        <ParamField body="folderId" type="string">
          Filter by folder ID. Pass `null` for uncategorized documents.
        </ParamField>

        <ParamField body="agentId" type="string">
          Filter to documents attached to a specific genie. Pass `"all"` or omit to include all documents.
        </ParamField>

        <ParamField body="readyOnly" type="boolean" default="false">
          When `true`, only returns documents that have been synced to the knowledge base system (have an active external document ID).
        </ParamField>

        <ParamField body="selectedUsername" type="string">
          Admin only. Filter by owner name (partial match).
        </ParamField>
      </Expandable>
    </ParamField>
  </Expandable>
</ParamField>

<ResponseField name="success" type="boolean">
  Whether the request succeeded.
</ResponseField>

<ResponseField name="data" type="object">
  <Expandable title="properties">
    <ResponseField name="documents" type="array">
      <Expandable title="item properties">
        <ResponseField name="id" type="string">Document UUID.</ResponseField>
        <ResponseField name="name" type="string">Document name.</ResponseField>
        <ResponseField name="url" type="string">Source URL or storage URL.</ResponseField>
        <ResponseField name="is_active" type="boolean">Whether the document is active.</ResponseField>
        <ResponseField name="is_public" type="boolean">Whether the document is publicly visible.</ResponseField>
        <ResponseField name="el_doc_id" type="string">External document identifier used by the voice platform.</ResponseField>
        <ResponseField name="user_id" type="string">Owner user ID.</ResponseField>
        <ResponseField name="folder_id" type="string | null">Folder ID, or `null` if uncategorized.</ResponseField>

        <ResponseField name="metadata" type="object">
          <Expandable title="properties">
            <ResponseField name="pdf_size" type="number">File size in bytes.</ResponseField>
            <ResponseField name="type" type="string">Document type (for example `"pdf_upload"`).</ResponseField>
            <ResponseField name="total_pages" type="number">Number of pages (PDFs only).</ResponseField>
            <ResponseField name="source" type="string">Source type.</ResponseField>
          </Expandable>
        </ResponseField>

        <ResponseField name="created_at" type="string">ISO 8601 creation timestamp.</ResponseField>
        <ResponseField name="updated_at" type="string">ISO 8601 last-updated timestamp.</ResponseField>
        <ResponseField name="folder" type="object | null">Folder details, if assigned.</ResponseField>
        <ResponseField name="owner" type="object | null">Owner profile (admin mode only).</ResponseField>

        <ResponseField name="dependent_agents" type="array">
          Genies that use this document.

          <Expandable title="item properties">
            <ResponseField name="id" type="string">Genie ID.</ResponseField>
            <ResponseField name="name" type="string">Genie name.</ResponseField>
            <ResponseField name="category" type="string">Genie category.</ResponseField>
            <ResponseField name="is_active" type="boolean">Whether the genie is active.</ResponseField>
          </Expandable>
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="count" type="number">Total matching documents.</ResponseField>
    <ResponseField name="inKBCount" type="number">Number of matching documents that have been synced to the knowledge base system (i.e. have an active external document ID). Filters applied to the main query are also applied here.</ResponseField>
    <ResponseField name="limit" type="number">Applied limit.</ResponseField>
    <ResponseField name="offset" type="number">Applied offset.</ResponseField>
  </Expandable>
</ResponseField>

<CodeGroup>
  ```typescript ApiService.invoke() theme={null}
  const response = await ApiService.invoke("knowledge-base", "all", undefined, {
    limit: 50,
    offset: 0,
    filters: {
      searchTerm: "product",
      selectedPrivacy: "public",
      selectedSourceType: "pdf_upload",
    },
  });
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.helpgenie.ai/v1/knowledge-base \
    -H "Authorization: Bearer hg_live_YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "action": "all",
      "data": {
        "limit": 50,
        "offset": 0,
        "filters": {
          "searchTerm": "product",
          "selectedPrivacy": "public",
          "selectedSourceType": "pdf_upload"
        }
      }
    }'
  ```

  ```json Request body theme={null}
  {
    "resource": "knowledge-base",
    "action": "all",
    "data": {
      "limit": 50,
      "offset": 0,
      "filters": {
        "searchTerm": "product",
        "selectedPrivacy": "public",
        "selectedSourceType": "pdf_upload"
      }
    }
  }
  ```

  ```json Response theme={null}
  {
    "success": true,
    "data": {
      "documents": [
        {
          "id": "770e8400-e29b-41d4-a716-446655440002",
          "name": "Product Documentation",
          "url": "https://storage.example.com/documents/product-docs.pdf",
          "is_active": true,
          "is_public": true,
          "el_doc_id": "el-doc-123",
          "user_id": "660e8400-e29b-41d4-a716-446655440001",
          "folder_id": null,
          "metadata": {
            "pdf_size": 2048576,
            "type": "pdf_upload",
            "total_pages": 45,
            "source": "pdf_upload"
          },
          "created_at": "2024-01-15T10:30:00.000Z",
          "updated_at": "2024-01-15T10:30:00.000Z",
          "folder": null,
          "owner": null,
          "dependent_agents": [
            {
              "id": "880e8400-e29b-41d4-a716-446655440003",
              "name": "Customer Support",
              "category": "support",
              "is_active": true
            }
          ]
        }
      ],
      "count": 125,
      "inKBCount": 98,
      "limit": 50,
      "offset": 0
    }
  }
  ```
</CodeGroup>

***

## List documents

Retrieves a compact, paginated list of documents. Returns only essential fields — optimized for dropdowns, selects, and search results. For full document details including metadata and folder info, use the `all` action instead.

<ParamField body="resource" type="string" required>
  Must be `"knowledge-base"`
</ParamField>

<ParamField body="action" type="string" required>
  Must be `"list"`
</ParamField>

<ParamField body="data" type="object">
  <Expandable title="properties">
    <ParamField body="limit" type="number" default="50">
      Results per page. Maximum 500.
    </ParamField>

    <ParamField body="offset" type="number" default="0">
      Number of results to skip.
    </ParamField>

    <ParamField body="filters" type="object">
      <Expandable title="properties">
        <ParamField body="searchTerm" type="string">
          Filter by document name (case-insensitive).
        </ParamField>

        <ParamField body="selectedPrivacy" type="string">
          Filter by visibility: `"public"` or `"private"`.
        </ParamField>

        <ParamField body="selectedType" type="string">
          Filter by document type (for example `"pdf_upload"`, `"website"`).
        </ParamField>

        <ParamField body="folderId" type="string">
          Filter by folder ID. Pass `null` for uncategorized documents.
        </ParamField>

        <ParamField body="agentId" type="string">
          Filter to documents attached to a specific genie. Pass `"all"` or omit to include all documents.
        </ParamField>

        <ParamField body="selectedUsername" type="string">
          Admin only. Filter by owner name (partial match).
        </ParamField>
      </Expandable>
    </ParamField>

    <ParamField body="adminMode" type="boolean" default="false">
      Admin only. When `true`, includes owner profile in each item and allows `userId` filtering.
    </ParamField>

    <ParamField body="userId" type="string">
      Admin only. Return documents owned by this user ID.
    </ParamField>
  </Expandable>
</ParamField>

<ResponseField name="success" type="boolean">
  Whether the request succeeded.
</ResponseField>

<ResponseField name="data" type="object">
  <Expandable title="properties">
    <ResponseField name="documents" type="array">
      <Expandable title="item properties">
        <ResponseField name="id" type="string">Document UUID.</ResponseField>
        <ResponseField name="name" type="string">Document name.</ResponseField>
        <ResponseField name="el_doc_id" type="string | null">External document identifier used by the voice platform, or `null` if not yet synced.</ResponseField>
        <ResponseField name="is_public" type="boolean">Whether the document is publicly visible.</ResponseField>
        <ResponseField name="created_at" type="string">ISO 8601 creation timestamp.</ResponseField>
        <ResponseField name="owner" type="object | null">Owner profile — present only when `adminMode` is `true`.</ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="count" type="number">Total matching documents.</ResponseField>
    <ResponseField name="limit" type="number">Applied limit.</ResponseField>
    <ResponseField name="offset" type="number">Applied offset.</ResponseField>
  </Expandable>
</ResponseField>

<CodeGroup>
  ```typescript ApiService.invoke() theme={null}
  const response = await ApiService.invoke("knowledge-base", "list", undefined, {
    limit: 50,
    offset: 0,
    filters: {
      searchTerm: "FAQ",
      agentId: "550e8400-e29b-41d4-a716-446655440000",
    },
  });
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.helpgenie.ai/v1/knowledge-base \
    -H "Authorization: Bearer hg_live_YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "resource": "knowledge-base",
      "action": "list",
      "data": {
        "limit": 50,
        "offset": 0,
        "filters": {
          "searchTerm": "FAQ"
        }
      }
    }'
  ```

  ```json Response theme={null}
  {
    "success": true,
    "data": {
      "documents": [
        {
          "id": "770e8400-e29b-41d4-a716-446655440002",
          "name": "Product FAQ",
          "el_doc_id": "el-doc-123",
          "is_public": true,
          "created_at": "2024-01-15T10:30:00.000Z"
        }
      ],
      "count": 1,
      "limit": 50,
      "offset": 0
    }
  }
  ```
</CodeGroup>

***

## Get document

Retrieves a single document by its UUID or 20-character external document ID. Returns full details including metadata, folder info, and dependent agents.

<ParamField body="resource" type="string" required>
  Must be `"knowledge-base"`
</ParamField>

<ParamField body="action" type="string" required>
  Must be `"get"`
</ParamField>

<ParamField body="id" type="string" required>
  The document UUID or external document ID (`el_doc_id`).
</ParamField>

<ParamField body="data" type="object">
  <Expandable title="properties">
    <ParamField body="adminMode" type="boolean" default="false">
      Admin only. Include owner profile information.
    </ParamField>
  </Expandable>
</ParamField>

<ResponseField name="success" type="boolean">
  Whether the request succeeded.
</ResponseField>

<ResponseField name="data" type="object">
  <Expandable title="properties">
    <ResponseField name="document" type="object">
      Full document object including `id`, `name`, `url`, `is_active`, `is_public`, `el_doc_id`, `user_id`, `folder_id`, `metadata`, `folder`, `owner`, `dependent_agents`, `created_at`, and `updated_at`.
    </ResponseField>
  </Expandable>
</ResponseField>

<CodeGroup>
  ```typescript ApiService.invoke() theme={null}
  const response = await ApiService.invoke(
    "documents",
    "get",
    "770e8400-e29b-41d4-a716-446655440002"
  );
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.helpgenie.ai/v1/knowledge-base \
    -H "Authorization: Bearer hg_live_YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "action": "get",
      "id": "770e8400-e29b-41d4-a716-446655440002"
    }'
  ```

  ```json Request body theme={null}
  {
    "resource": "knowledge-base",
    "action": "get",
    "id": "770e8400-e29b-41d4-a716-446655440002"
  }
  ```

  ```json Response theme={null}
  {
    "success": true,
    "data": {
      "document": {
        "id": "770e8400-e29b-41d4-a716-446655440002",
        "name": "Product Documentation",
        "url": "https://storage.example.com/documents/product-docs.pdf",
        "is_active": true,
        "is_public": true,
        "el_doc_id": "el-doc-123",
        "user_id": "660e8400-e29b-41d4-a716-446655440001",
        "folder_id": "550e8400-e29b-41d4-a716-446655440000",
        "metadata": {
          "pdf_size": 2048576,
          "type": "pdf_upload",
          "total_pages": 45,
          "source": "pdf_upload"
        },
        "created_at": "2024-01-15T10:30:00.000Z",
        "updated_at": "2024-01-15T10:30:00.000Z",
        "folder": {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "name": "Documentation",
          "color": "#FF5733",
          "icon": "folder",
          "position": 0
        },
        "owner": null,
        "dependent_agents": [
          {
            "id": "880e8400-e29b-41d4-a716-446655440003",
            "name": "Customer Support",
            "category": "support",
            "is_active": true
          }
        ]
      }
    }
  }
  ```
</CodeGroup>

<Note>
  Non-admin users can only access their own documents or documents marked as public. Admin users with `adminMode: true` can access all documents and see owner information.
</Note>

***

## Get document content

Retrieves the plain-text content of a document. Uses a priority order: manual edits → optimised content → original parsed content.

<ParamField body="resource" type="string" required>
  Must be `"knowledge-base"`
</ParamField>

<ParamField body="action" type="string" required>
  Must be `"get-content"`
</ParamField>

<ParamField body="id" type="string" required>
  The document UUID.
</ParamField>

<ParamField body="data" type="object">
  <Expandable title="properties">
    <ParamField body="preferOptimized" type="boolean" default="true">
      When `true` (default), optimised content is tried before falling back to the original parsed content. Set to `false` to skip the optimised version and fetch the raw parsed content directly.
    </ParamField>
  </Expandable>
</ParamField>

<ResponseField name="success" type="boolean">
  Whether the request succeeded.
</ResponseField>

<ResponseField name="data" type="object">
  <Expandable title="properties">
    <ResponseField name="documentId" type="string">Document UUID.</ResponseField>
    <ResponseField name="name" type="string">Document name.</ResponseField>
    <ResponseField name="content" type="string">The document's text content.</ResponseField>

    <ResponseField name="source" type="string">
      Which content version was returned: `"edited"` (manual override), `"optimized"` (processed version), or `"parsed"` (original extraction).
    </ResponseField>
  </Expandable>
</ResponseField>

<Note>
  Non-admin users can only retrieve content for their own documents or documents marked as public. A `404` is returned when no readable content is available yet — the document may still be processing.
</Note>

<CodeGroup>
  ```typescript ApiService.invoke() theme={null}
  const response = await ApiService.invoke({
    resource: "knowledge-base",
    action: "get-content",
    id: "770e8400-e29b-41d4-a716-446655440002",
  });
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.helpgenie.ai/v1/knowledge-base \
    -H "Authorization: Bearer hg_live_YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "resource": "knowledge-base",
      "action": "get-content",
      "id": "770e8400-e29b-41d4-a716-446655440002"
    }'
  ```

  ```json Response theme={null}
  {
    "success": true,
    "data": {
      "documentId": "770e8400-e29b-41d4-a716-446655440002",
      "name": "Product Documentation",
      "content": "Welcome to Acme Products...\n\nThis guide covers installation, configuration, and troubleshooting.",
      "source": "optimized"
    }
  }
  ```
</CodeGroup>

***

## Create document (text)

The simplest way to add a knowledge base document. Pass `title` and `content` directly — no file upload needed. If you omit the nested `action` field, `create-notes` is used by default.

<ParamField body="resource" type="string" required>
  Must be `"knowledge-base"`
</ParamField>

<ParamField body="action" type="string" required>
  Must be `"create"`
</ParamField>

<ParamField body="data" type="object" required>
  <Expandable title="properties">
    <ParamField body="title" type="string">
      Document name. Defaults to `"Notes {timestamp}"`.
    </ParamField>

    <ParamField body="content" type="string" required>
      Plain text content for the document.
    </ParamField>

    <ParamField body="agentId" type="string">
      Genie UUID. If provided, the document is automatically attached to this genie.
    </ParamField>
  </Expandable>
</ParamField>

<CodeGroup>
  ```bash cURL theme={null}
  curl -s -X POST https://api.helpgenie.ai/v1/knowledge-base \
    -H "Authorization: Bearer hg_live_YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "action": "create",
      "title": "Company FAQ",
      "content": "Q: What are your hours?\nA: Monday-Friday 9am-5pm.",
      "agentId": "genie-uuid"
    }'
  ```

  ```json Request body theme={null}
  {
    "resource": "knowledge-base",
    "action": "create",
    "data": {
      "title": "Company FAQ",
      "content": "Q: What are your hours?\nA: Monday-Friday 9am-5pm.",
      "agentId": "genie-uuid"
    }
  }
  ```

  ```json Response theme={null}
  {
    "success": true,
    "data": {
      "success": true,
      "document": {
        "id": "doc-uuid",
        "name": "Company FAQ",
        "metadata": {
          "source": "notes",
          "status": "pending",
          "stage": "pending_extraction"
        }
      }
    }
  }
  ```
</CodeGroup>

<Note>
  Documents are processed asynchronously. After creation, the document transitions through stages: `pending_extraction` → `pending_optimization` → `completed`.
</Note>

***

## Create document (advanced)

For content sources other than text, use the nested sub-action pattern. The `create` action delegates to the internal document upload pipeline.

<ParamField body="resource" type="string" required>
  Must be `"knowledge-base"`
</ParamField>

<ParamField body="action" type="string" required>
  Must be `"create"`
</ParamField>

<ParamField body="data" type="object" required>
  <Expandable title="properties">
    <ParamField body="action" type="string" required>
      The upload sub-action. One of:

      * `"upload-website"` -- Extract content from a URL
      * `"scrape-website"` -- Scrape and parse website content
      * `"extract-youtube"` -- Extract transcript from a YouTube video
      * `"upload-pdf"` -- Upload and process a PDF file
      * `"extract-document"` -- Extract content from various document formats
    </ParamField>

    <ParamField body="data" type="object" required>
      Sub-action-specific parameters. For `upload-website`, pass `{ "url": "https://..." }`.
    </ParamField>
  </Expandable>
</ParamField>

<ResponseField name="success" type="boolean">
  Whether the request succeeded.
</ResponseField>

<ResponseField name="data" type="object">
  <Expandable title="properties">
    <ResponseField name="document" type="object">
      <Expandable title="properties">
        <ResponseField name="id" type="string">New document ID.</ResponseField>
        <ResponseField name="el_doc_id" type="string">External document ID used by the voice platform.</ResponseField>
        <ResponseField name="name" type="string">Extracted document name.</ResponseField>
        <ResponseField name="metadata" type="object">Source metadata.</ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>
</ResponseField>

<Info>
  The `create` action delegates to the internal document upload pipeline (`doc-upload` edge function). The nested `data.action` field determines which content source is used. Supported sub-actions:

  * `upload-website` -- Extract content from a single URL
  * `scrape-website` -- Crawl and parse website content
  * `extract-youtube` -- Extract the transcript from a YouTube video URL
  * `upload-pdf` -- Upload and process a PDF file
  * `extract-document` -- Extract content from various document formats (DOCX, TXT, etc.)
</Info>

<CodeGroup>
  ```typescript ApiService.invoke() theme={null}
  const response = await ApiService.invoke("knowledge-base", "create", undefined, {
    action: "upload-website",
    data: {
      url: "https://example.com/documentation",
    },
  });
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.helpgenie.ai/v1/knowledge-base \
    -H "Authorization: Bearer hg_live_YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "action": "create",
      "data": {
        "action": "upload-website",
        "data": {
          "url": "https://example.com/documentation"
        }
      }
    }'
  ```

  ```json Request body theme={null}
  {
    "resource": "knowledge-base",
    "action": "create",
    "data": {
      "action": "upload-website",
      "data": {
        "url": "https://example.com/documentation"
      }
    }
  }
  ```

  ```json Response theme={null}
  {
    "success": true,
    "data": {
      "document": {
        "id": "doc-new-id",
        "el_doc_id": "el-doc-123",
        "name": "Extracted Document",
        "metadata": {
          "source": "website",
          "url": "https://example.com/documentation"
        }
      }
    }
  }
  ```
</CodeGroup>

***

## Update document (data mode)

Updates document metadata fields without changing the content. Only the fields you provide are updated. Metadata is intelligently merged with existing values. If the document name changes and the document has been synced with the voice platform, the name is also updated there.

<ParamField body="resource" type="string" required>
  Must be `"knowledge-base"`
</ParamField>

<ParamField body="action" type="string" required>
  Must be `"update"`
</ParamField>

<ParamField body="id" type="string" required>
  The document UUID to update.
</ParamField>

<ParamField body="data" type="object" required>
  <Expandable title="properties">
    <ParamField body="mode" type="string">
      Use `"data"` to update metadata fields. Can be omitted — when no `content` field is present, `data` mode is inferred automatically.
    </ParamField>

    <ParamField body="name" type="string">
      New document name. If the document has been synced with the voice platform, it will be renamed there as well.
    </ParamField>

    <ParamField body="is_active" type="boolean">
      Active status.
    </ParamField>

    <ParamField body="is_public" type="boolean">
      Public visibility.
    </ParamField>

    <ParamField body="folder_id" type="string | null">
      Folder ID for organization. Pass `null` to remove from any folder.
    </ParamField>

    <ParamField body="is_personal" type="boolean">
      Personal document flag.
    </ParamField>

    <ParamField body="user_id" type="string">
      Admin only. Reassign document ownership to this user ID.
    </ParamField>

    <ParamField body="metadata" type="object">
      Custom metadata key-value pairs. Merged with existing metadata — not overwritten.
    </ParamField>
  </Expandable>
</ParamField>

<ResponseField name="success" type="boolean">
  Whether the request succeeded.
</ResponseField>

<ResponseField name="data" type="object">
  <Expandable title="properties">
    <ResponseField name="document" type="object">
      The updated document object with all current field values.
    </ResponseField>
  </Expandable>
</ResponseField>

<CodeGroup>
  ```typescript ApiService.invoke() theme={null}
  const response = await ApiService.invoke(
    "documents",
    "update",
    "770e8400-e29b-41d4-a716-446655440002",
    {
      mode: "data",
      name: "Updated Product Documentation",
      is_active: true,
      is_public: false,
      folder_id: "550e8400-e29b-41d4-a716-446655440000",
    }
  );
  ```

  ```json Request body theme={null}
  {
    "resource": "knowledge-base",
    "action": "update",
    "id": "770e8400-e29b-41d4-a716-446655440002",
    "data": {
      "mode": "data",
      "name": "Updated Product Documentation",
      "is_active": true,
      "is_public": false,
      "folder_id": "550e8400-e29b-41d4-a716-446655440000"
    }
  }
  ```

  ```json Response theme={null}
  {
    "success": true,
    "data": {
      "document": {
        "id": "770e8400-e29b-41d4-a716-446655440002",
        "name": "Updated Product Documentation",
        "is_active": true,
        "is_public": false,
        "folder_id": "550e8400-e29b-41d4-a716-446655440000",
        "metadata": {
          "pdf_size": 2048576,
          "type": "pdf_upload",
          "total_pages": 45,
          "source": "pdf_upload"
        },
        "updated_at": "2024-01-15T11:00:00.000Z"
      }
    }
  }
  ```
</CodeGroup>

***

## Update document (content mode)

Replaces the document content. A new voice platform document is created with the updated content, and all dependent genies are automatically updated with the new external document ID (fire-and-forget sync). The new content is also saved to storage and a signed URL is returned.

<ParamField body="resource" type="string" required>
  Must be `"knowledge-base"`
</ParamField>

<ParamField body="action" type="string" required>
  Must be `"update"`
</ParamField>

<ParamField body="id" type="string" required>
  The document UUID to update.
</ParamField>

<ParamField body="data" type="object" required>
  <Expandable title="properties">
    <ParamField body="mode" type="string">
      Use `"content"` to replace document content. Can be omitted — when a `content` string is present, content mode is inferred automatically.
    </ParamField>

    <ParamField body="content" type="string" required>
      The new document content (non-empty string).
    </ParamField>

    <ParamField body="isOptimized" type="boolean" default="true">
      When `true`, content is stored with an `_optimized` suffix and the document metadata is flagged as optimized. When `false`, content is stored with an `_edited` suffix.
    </ParamField>
  </Expandable>
</ParamField>

<ResponseField name="success" type="boolean">
  Whether the request succeeded.
</ResponseField>

<ResponseField name="data" type="object">
  <Expandable title="properties">
    <ResponseField name="document" type="object">
      Updated document with the new `el_doc_id` and storage metadata.
    </ResponseField>

    <ResponseField name="contentType" type="string">
      Either `"edited"` or `"optimized"`.
    </ResponseField>

    <ResponseField name="storagePath" type="string">
      Path to the stored content file.
    </ResponseField>

    <ResponseField name="signedUrl" type="string">
      Signed URL for accessing the content (valid for 7 days).
    </ResponseField>
  </Expandable>
</ResponseField>

<CodeGroup>
  ```typescript ApiService.invoke() theme={null}
  const response = await ApiService.invoke(
    "documents",
    "update",
    "770e8400-e29b-41d4-a716-446655440002",
    {
      mode: "content",
      content: "Updated document content with revised product information...",
      isOptimized: false,
    }
  );
  ```

  ```json Request body theme={null}
  {
    "resource": "knowledge-base",
    "action": "update",
    "id": "770e8400-e29b-41d4-a716-446655440002",
    "data": {
      "mode": "content",
      "content": "Updated document content with revised product information...",
      "isOptimized": false
    }
  }
  ```

  ```json Response theme={null}
  {
    "success": true,
    "data": {
      "document": {
        "id": "770e8400-e29b-41d4-a716-446655440002",
        "el_doc_id": "el-doc-new-id",
        "name": "Product Documentation",
        "metadata": {
          "pdf_size": 2048576,
          "type": "pdf_upload",
          "source": "pdf_upload",
          "edited_url": "https://signed.url/documents/edited/product_documentation_edited.txt?expires=..."
        },
        "updated_at": "2024-01-15T11:30:00.000Z"
      },
      "contentType": "edited",
      "storagePath": "documents/edited/product_documentation_edited.txt",
      "signedUrl": "https://signed.url/documents/edited/product_documentation_edited.txt?expires=..."
    }
  }
  ```
</CodeGroup>

<Note>
  Content mode deletes the old external document and creates a new one. All genies that depend on this document are automatically updated with the new external document ID.
</Note>

***

## Bulk update documents

Updates multiple documents at once. Only metadata fields can be bulk-updated — document content cannot be changed via this action.

<ParamField body="resource" type="string" required>
  Must be `"knowledge-base"`
</ParamField>

<ParamField body="action" type="string" required>
  Must be `"bulk-update"`
</ParamField>

<ParamField body="data" type="object" required>
  <Expandable title="properties">
    <ParamField body="documentIds" type="string[]" required>
      Array of document UUIDs to update. Must contain at least one ID.
    </ParamField>

    <ParamField body="updates" type="object" required>
      Fields to apply across all listed documents. At least one field is required. Only the fields you include are written — unspecified fields are left unchanged.

      <Expandable title="properties">
        <ParamField body="is_active" type="boolean">Active status.</ParamField>
        <ParamField body="is_public" type="boolean">Public visibility.</ParamField>
        <ParamField body="is_private" type="boolean">Private visibility flag.</ParamField>
        <ParamField body="folder_id" type="string | null">Folder assignment. Pass `null` to remove from any folder.</ParamField>
        <ParamField body="consumer_genie_id" type="string | null">Associates the documents with a specific consumer genie. Pass `null` to clear the association.</ParamField>
        <ParamField body="status" type="string">Document processing status.</ParamField>
        <ParamField body="source_type" type="string">Source type override.</ParamField>
        <ParamField body="user_id" type="string">Admin only. Reassigns document ownership to this user ID.</ParamField>
      </Expandable>
    </ParamField>
  </Expandable>
</ParamField>

<ResponseField name="success" type="boolean">Whether all documents were updated successfully.</ResponseField>
<ResponseField name="message" type="string">Human-readable summary of the operation.</ResponseField>
<ResponseField name="updatedCount" type="number">Number of documents actually updated.</ResponseField>
<ResponseField name="documentIds" type="string[]">The document IDs that were targeted by the update.</ResponseField>
<ResponseField name="updates" type="object">The update payload that was applied, containing only the fields that were provided.</ResponseField>

<CodeGroup>
  ```typescript ApiService.invoke() theme={null}
  const response = await ApiService.invoke("knowledge-base", "bulk-update", undefined, {
    documentIds: ["doc-id-1", "doc-id-2", "doc-id-3"],
    updates: {
      is_active: true,
      folder_id: "folder-uuid",
    },
  });
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.helpgenie.ai/v1/knowledge-base \
    -H "Authorization: Bearer hg_live_YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "action": "bulk-update",
      "data": {
        "documentIds": ["doc-id-1", "doc-id-2", "doc-id-3"],
        "updates": {
          "is_active": true,
          "folder_id": "folder-uuid"
        }
      }
    }'
  ```

  ```json Request body theme={null}
  {
    "resource": "knowledge-base",
    "action": "bulk-update",
    "data": {
      "documentIds": ["doc-id-1", "doc-id-2", "doc-id-3"],
      "updates": {
        "is_active": true,
        "folder_id": "folder-uuid"
      }
    }
  }
  ```

  ```json Response theme={null}
  {
    "success": true,
    "message": "Successfully updated 3 documents",
    "updatedCount": 3,
    "documentIds": ["doc-id-1", "doc-id-2", "doc-id-3"],
    "updates": {
      "is_active": true,
      "folder_id": "folder-uuid"
    }
  }
  ```
</CodeGroup>

***

## Delete document

Permanently deletes a document, removes it from storage and the voice agent system, and syncs the removal with all dependent agents.

<ParamField body="resource" type="string" required>
  Must be `"knowledge-base"`
</ParamField>

<ParamField body="action" type="string" required>
  Must be `"delete"`
</ParamField>

<ParamField body="id" type="string" required>
  The document UUID to delete.
</ParamField>

<ParamField body="data" type="object">
  <Expandable title="properties">
    <ParamField body="skipAgentSync" type="boolean" default="false">
      When `true`, skips the step that removes the document from dependent genies after deletion. Use this when you are managing agent knowledge base membership separately or when the document was never synced with any genie.
    </ParamField>
  </Expandable>
</ParamField>

<ResponseField name="success" type="boolean">
  Whether the request succeeded.
</ResponseField>

<ResponseField name="data" type="object">
  <Expandable title="properties">
    <ResponseField name="message" type="string">Confirmation message.</ResponseField>
    <ResponseField name="documentId" type="string">The ID of the deleted document.</ResponseField>

    <ResponseField name="errors" type="array">
      Present only when partial errors occurred during cleanup (for example, storage deletion failed but the database record was removed). The deletion is still considered complete if the database record was removed.

      <Expandable title="item properties">
        <ResponseField name="step" type="string">Which cleanup step failed (e.g. `"storage_deletion"`, `"agent_sync_initiation"`).</ResponseField>
        <ResponseField name="error" type="string">Error detail.</ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>
</ResponseField>

<Warning>
  This action is irreversible. The document, its storage file, and its external record will be permanently removed. All dependent genies will have the document removed from their knowledge base unless `skipAgentSync` is set to `true`.
</Warning>

<CodeGroup>
  ```typescript ApiService.invoke() theme={null}
  const response = await ApiService.invoke({
    resource: "knowledge-base",
    action: "delete",
    id: "770e8400-e29b-41d4-a716-446655440002",
    data: {
      skipAgentSync: false,
    },
  });
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.helpgenie.ai/v1/knowledge-base \
    -H "Authorization: Bearer hg_live_YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "resource": "knowledge-base",
      "action": "delete",
      "id": "770e8400-e29b-41d4-a716-446655440002"
    }'
  ```

  ```json Response theme={null}
  {
    "success": true,
    "data": {
      "success": true,
      "message": "Document deleted successfully",
      "documentId": "770e8400-e29b-41d4-a716-446655440002"
    }
  }
  ```
</CodeGroup>

***

## Attach documents to agent

Adds one or more documents to a genie's knowledge base. Duplicate documents are automatically skipped.

<ParamField body="resource" type="string" required>
  Must be `"knowledge-base"`
</ParamField>

<ParamField body="action" type="string" required>
  Must be `"attach"`
</ParamField>

<ParamField body="data" type="object" required>
  <Expandable title="properties">
    <ParamField body="agentId" type="string" required>
      The genie ID to attach documents to.
    </ParamField>

    <ParamField body="documents" type="array" required>
      Array of documents to attach.

      <Expandable title="item properties">
        <ParamField body="id" type="string" required>Document ID.</ParamField>
        <ParamField body="name" type="string" required>Document name.</ParamField>
        <ParamField body="type" type="string" required>Document type (for example `"file"`, `"url"`).</ParamField>
      </Expandable>
    </ParamField>
  </Expandable>
</ParamField>

<ResponseField name="success" type="boolean">
  Whether the request succeeded.
</ResponseField>

<ResponseField name="message" type="string">
  Summary of attached documents.
</ResponseField>

<ResponseField name="attachedCount" type="number">
  Number of documents successfully attached.
</ResponseField>

<CodeGroup>
  ```typescript ApiService.invoke() theme={null}
  const response = await ApiService.invoke("knowledge-base", "attach", undefined, {
    agentId: "550e8400-e29b-41d4-a716-446655440000",
    documents: [
      { id: "doc-id-1", name: "Product FAQ", type: "file" },
      { id: "doc-id-2", name: "Pricing Guide", type: "url" },
    ],
  });
  ```

  ```json Request body theme={null}
  {
    "resource": "knowledge-base",
    "action": "attach",
    "data": {
      "agentId": "550e8400-e29b-41d4-a716-446655440000",
      "documents": [
        { "id": "doc-id-1", "name": "Product FAQ", "type": "file" },
        { "id": "doc-id-2", "name": "Pricing Guide", "type": "url" }
      ]
    }
  }
  ```

  ```json Response theme={null}
  {
    "success": true,
    "message": "Attached 2 documents",
    "attachedCount": 2
  }
  ```
</CodeGroup>

***

## Detach documents from agent

Removes specific documents from a genie's knowledge base.

<ParamField body="resource" type="string" required>
  Must be `"knowledge-base"`
</ParamField>

<ParamField body="action" type="string" required>
  Must be `"detach"`
</ParamField>

<ParamField body="data" type="object" required>
  <Expandable title="properties">
    <ParamField body="agentId" type="string" required>
      The genie ID to detach documents from.
    </ParamField>

    <ParamField body="documentIds" type="string[]" required>
      Array of document IDs to remove.
    </ParamField>
  </Expandable>
</ParamField>

<ResponseField name="success" type="boolean">
  Whether the request succeeded.
</ResponseField>

<ResponseField name="message" type="string">
  Summary of detached documents.
</ResponseField>

<ResponseField name="detachedCount" type="number">
  Number of documents successfully detached.
</ResponseField>

<CodeGroup>
  ```typescript ApiService.invoke() theme={null}
  const response = await ApiService.invoke("knowledge-base", "detach", undefined, {
    agentId: "550e8400-e29b-41d4-a716-446655440000",
    documentIds: ["doc-id-1", "doc-id-2"],
  });
  ```

  ```json Request body theme={null}
  {
    "resource": "knowledge-base",
    "action": "detach",
    "data": {
      "agentId": "550e8400-e29b-41d4-a716-446655440000",
      "documentIds": ["doc-id-1", "doc-id-2"]
    }
  }
  ```

  ```json Response theme={null}
  {
    "success": true,
    "message": "Detached 2 documents",
    "detachedCount": 2
  }
  ```
</CodeGroup>

***

## Replace agent knowledge base

Atomically replaces a genie's entire knowledge base with a new document set. Each document is validated before the update is applied. The genie's voice platform configuration is updated with the new document list, `agent_document` relations are synced (old documents removed, new documents upserted), and connected clients are notified via a realtime broadcast.

<ParamField body="resource" type="string" required>
  Must be `"knowledge-base"`
</ParamField>

<ParamField body="action" type="string" required>
  Must be `"replace"`
</ParamField>

<ParamField body="data" type="object" required>
  <Expandable title="properties">
    <ParamField body="agentId" type="string" required>
      The genie ID whose knowledge base will be replaced.
    </ParamField>

    <ParamField body="documents" type="array" required>
      The new complete set of documents. Pass an empty array to clear the knowledge base.

      <Expandable title="item properties">
        <ParamField body="id" type="string" required>Document external ID (voice platform ID).</ParamField>
        <ParamField body="name" type="string" required>Document name.</ParamField>
        <ParamField body="type" type="string" required>Document type (for example `"file"`, `"url"`).</ParamField>
      </Expandable>
    </ParamField>
  </Expandable>
</ParamField>

<ResponseField name="success" type="boolean">
  Whether the request succeeded.
</ResponseField>

<ResponseField name="message" type="string">
  Summary of the replacement, including document count.
</ResponseField>

<ResponseField name="replacedCount" type="number">
  Number of documents in the new knowledge base.
</ResponseField>

<CodeGroup>
  ```typescript ApiService.invoke() theme={null}
  const response = await ApiService.invoke({
    resource: "knowledge-base",
    action: "replace",
    data: {
      agentId: "550e8400-e29b-41d4-a716-446655440000",
      documents: [
        { id: "el-doc-id-1", name: "Product FAQ", type: "file" },
        { id: "el-doc-id-3", name: "Return Policy", type: "file" },
      ],
    },
  });
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.helpgenie.ai/v1/knowledge-base \
    -H "Authorization: Bearer hg_live_YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "action": "replace",
      "data": {
        "agentId": "550e8400-e29b-41d4-a716-446655440000",
        "documents": [
          { "id": "el-doc-id-1", "name": "Product FAQ", "type": "file" },
          { "id": "el-doc-id-3", "name": "Return Policy", "type": "file" }
        ]
      }
    }'
  ```

  ```json Response theme={null}
  {
    "success": true,
    "message": "Replaced knowledge base with 2 documents",
    "replacedCount": 2
  }
  ```
</CodeGroup>

<Note>
  Document IDs passed in the `documents` array are voice platform external IDs, not HelpGenie document UUIDs. All documents are validated against the platform before the replacement is applied — if no valid documents are found in a non-empty array, the request is rejected with a `VALIDATION_ERROR`.
</Note>

***

## Sync documents with agents

Synchronizes document changes across all dependent agents. Removes old external documents and adds updated ones to each agent's knowledge base. Typically used internally after document content is updated or deleted.

<ParamField body="resource" type="string" required>
  Must be `"knowledge-base"`
</ParamField>

<ParamField body="action" type="string" required>
  Must be `"sync-with-agents"`
</ParamField>

<ParamField body="data" type="object">
  <Expandable title="properties">
    <ParamField body="oldDocId" type="string">
      External document ID to remove from agents.
    </ParamField>

    <ParamField body="newDocId" type="string">
      External document ID to add to agents.
    </ParamField>

    <ParamField body="documentIds" type="string[]">
      Specific document IDs to sync. If omitted, syncs all dependent documents.
    </ParamField>

    <ParamField body="preventDeletion" type="boolean" default="false">
      When `true`, skip deletion of the old document from agents (non-destructive update).
    </ParamField>
  </Expandable>
</ParamField>

<ResponseField name="success" type="boolean">
  Whether the request succeeded.
</ResponseField>

<ResponseField name="data" type="object">
  <Expandable title="properties">
    <ResponseField name="synced_agents" type="array">
      Agents that were successfully updated.

      <Expandable title="item properties">
        <ResponseField name="agent_id" type="string">Genie ID.</ResponseField>
        <ResponseField name="updated" type="boolean">Whether the agent was updated.</ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="failed_agents" type="array">
      Agents that failed to update.
    </ResponseField>

    <ResponseField name="total_agents" type="number">
      Total number of dependent agents processed.
    </ResponseField>
  </Expandable>
</ResponseField>

<CodeGroup>
  ```typescript ApiService.invoke() theme={null}
  const response = await ApiService.invoke(
    "knowledge-base",
    "sync-with-agents",
    undefined,
    {
      oldDocId: "el-doc-abc123def456",
      newDocId: "el-doc-xyz789uvw012",
      preventDeletion: false,
    }
  );
  ```

  ```json Request body theme={null}
  {
    "resource": "knowledge-base",
    "action": "sync-with-agents",
    "data": {
      "oldDocId": "el-doc-abc123def456",
      "newDocId": "el-doc-xyz789uvw012",
      "preventDeletion": false
    }
  }
  ```

  ```json Response theme={null}
  {
    "success": true,
    "data": {
      "synced_agents": [
        {
          "agent_id": "880e8400-e29b-41d4-a716-446655440003",
          "updated": true
        }
      ],
      "failed_agents": [],
      "total_agents": 1
    }
  }
  ```
</CodeGroup>

***

## Clone document

Creates a copy of an existing document.

<ParamField body="action" type="string" required>
  `clone`
</ParamField>

<ParamField body="id" type="string" required>
  UUID of the document to clone.
</ParamField>

### Response

Returns the newly created document object.

***

## Consolidate documents

Merges multiple documents into a single new document. Optionally prunes the originals after consolidation. This delegates to the `doc-upload` edge function.

<ParamField body="action" type="string" required>
  `consolidate`
</ParamField>

<ParamField body="data" type="object" required>
  <Expandable title="properties">
    <ParamField body="documentIds" type="string[]" required>
      Array of document UUIDs to consolidate.
    </ParamField>

    <ParamField body="docName" type="string">
      Name for the consolidated document.
    </ParamField>

    <ParamField body="prune" type="boolean">
      When `true`, deletes the source documents after consolidation.
    </ParamField>
  </Expandable>
</ParamField>

***

## Documents outside workspace

Returns documents that are attached to one of the workspace's genies but are owned by a different user. This surfaces documents that were added to a genie by an admin on behalf of the workspace — they would otherwise be invisible because documents are normally scoped to the owner's account.

Pagination is supported. Results are sorted newest-first. Only the current user's workspace is searched — no additional parameters are required.

<ParamField body="resource" type="string" required>
  Must be `"knowledge-base"`
</ParamField>

<ParamField body="action" type="string" required>
  Must be `"outside-workspace"`
</ParamField>

<ParamField body="data" type="object">
  <Expandable title="properties">
    <ParamField body="limit" type="number" default="50">
      Results per page. Maximum 500.
    </ParamField>

    <ParamField body="offset" type="number" default="0">
      Number of results to skip.
    </ParamField>
  </Expandable>
</ParamField>

<ResponseField name="success" type="boolean">
  Whether the request succeeded.
</ResponseField>

<ResponseField name="data" type="object">
  <Expandable title="properties">
    <ResponseField name="documents" type="array">
      Full document objects. Same shape as the `all` action response items, including `folder`, `owner`, and `dependent_agents`.
    </ResponseField>

    <ResponseField name="count" type="number">Total matching documents.</ResponseField>
    <ResponseField name="limit" type="number">Applied limit.</ResponseField>
    <ResponseField name="offset" type="number">Applied offset.</ResponseField>
  </Expandable>
</ResponseField>

<CodeGroup>
  ```typescript ApiService.invoke() theme={null}
  const response = await ApiService.invoke("knowledge-base", "outside-workspace", undefined, {
    limit: 50,
    offset: 0,
  });
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.helpgenie.ai/v1/knowledge-base \
    -H "Authorization: Bearer hg_live_YOUR_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "resource": "knowledge-base",
      "action": "outside-workspace",
      "data": {
        "limit": 50,
        "offset": 0
      }
    }'
  ```

  ```json Response theme={null}
  {
    "success": true,
    "data": {
      "documents": [
        {
          "id": "770e8400-e29b-41d4-a716-446655440099",
          "name": "Admin Uploaded Guide",
          "is_active": true,
          "is_public": false,
          "user_id": "admin-user-id",
          "folder": null,
          "owner": {
            "id": "admin-user-id",
            "full_name": "Admin User",
            "email": "admin@example.com"
          },
          "dependent_agents": [
            {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "name": "Support Genie",
              "is_active": true
            }
          ],
          "created_at": "2024-01-10T08:00:00.000Z",
          "updated_at": "2024-01-10T08:00:00.000Z"
        }
      ],
      "count": 1,
      "limit": 50,
      "offset": 0
    }
  }
  ```
</CodeGroup>

***

## Error codes

| Code               | Status | Description                               |
| ------------------ | ------ | ----------------------------------------- |
| `UNAUTHORIZED`     | 401    | Missing or invalid authentication token   |
| `INVALID_TOKEN`    | 401    | Token validation failed                   |
| `FORBIDDEN`        | 403    | User lacks required permissions           |
| `NOT_FOUND`        | 404    | Document not found                        |
| `VALIDATION_ERROR` | 400    | Invalid request parameters                |
| `INVALID_ACTION`   | 400    | Unknown action for the documents resource |
| `INTERNAL_ERROR`   | 500    | Server-side error                         |
