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

# Upload a document (multipart)

> Uploads a single document as multipart/form-data, intended for browser drag-and-drop
and accepting larger files than the inline JSON path (up to 64 MiB). The file bytes
go in a part named file (or body), with optional text fields scope (private,
channel, or team; defaults to private), channel_id, category, tags
(comma-separated), and caption. Behavior otherwise matches the inline upload,
including content-addressed deduplication and metadata enrichment of an existing
identical document. Returns the document id, download URL, SHA-256, and size.
Requires the documents:write scope.

## Multipart file upload

Send the file as **`multipart/form-data`** (a real binary file part), not JSON. Use this variant for direct uploads from a browser file-picker or a form. The interactive playground's file control posts the part for you; from code, build a multipart body.


## OpenAPI

````yaml POST /v1/api/documents/upload
openapi: 3.1.0
info:
  title: HQ API
  description: >-
    Public HTTP API for HQ. Authenticate with a Personal Access Token
    (`Authorization: Bearer hq_pat_...`) for server-side integrations, or an
    OAuth 2.1 authorization-code + PKCE flow for browser apps acting on a user's
    behalf. Both grant from the same resource:action scope vocabulary; an
    endpoint's required scope is listed under its `security`.
  license:
    name: Apache-2.0
    identifier: Apache-2.0
  version: 1.0.0
servers:
  - url: https://api.hq.zone
    description: HQ API (production)
security: []
tags:
  - name: me
    description: The signed-in user's own account
  - name: conversations
    description: Conversations and their messages
  - name: documents
    description: The content-addressed documents library
  - name: schedules
    description: Scheduled prompts and recurring tasks
  - name: agents
    description: Agents, their skills and integrations
  - name: memory
    description: What the assistant remembers (L5 governance)
  - name: tokens
    description: Personal Access Token management
  - name: billing
    description: Usage and billing
  - name: notifications
    description: In-app notification center
  - name: admin
    description: Workspace administration
  - name: integrations
    description: Workspace integrations (Slack, MCP, skills)
  - name: onboarding
    description: New-workspace onboarding wizard
  - name: auth
    description: Sign-in, sessions, and OAuth
paths:
  /v1/api/documents/upload:
    post:
      tags:
        - documents
      summary: Upload a document (multipart)
      description: >-
        Uploads a single document as multipart/form-data, intended for browser
        drag-and-drop

        and accepting larger files than the inline JSON path (up to 64 MiB). The
        file bytes

        go in a part named file (or body), with optional text fields scope
        (private,

        channel, or team; defaults to private), channel_id, category, tags

        (comma-separated), and caption. Behavior otherwise matches the inline
        upload,

        including content-addressed deduplication and metadata enrichment of an
        existing

        identical document. Returns the document id, download URL, SHA-256, and
        size.

        Requires the documents:write scope.
      operationId: upload_multipart
      requestBody:
        content:
          multipart/form-data:
            schema:
              type: object
              description: >-
                Documentation shape for the `POST /v1/api/documents/upload`
                multipart

                form. The handler reads the parts directly; this only drives the
                spec.
              required:
                - file
              properties:
                caption:
                  type:
                    - string
                    - 'null'
                category:
                  type:
                    - string
                    - 'null'
                channel_id:
                  type:
                    - string
                    - 'null'
                  description: Required iff `scope=channel`.
                file:
                  type: string
                  format: binary
                  description: >-
                    The file bytes (a file part carrying filename +
                    content-type).
                scope:
                  type:
                    - string
                    - 'null'
                  description: '`private` | `channel` | `team`. Defaults to `private`.'
                tags:
                  type:
                    - string
                    - 'null'
                  description: Comma-separated tag list.
        required: true
      responses:
        '200':
          description: Document saved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentUploadResp'
        '400':
          description: Missing file part / filename, invalid scope, or empty filename
      security:
        - bearer_pat:
            - documents:write
        - oauth2:
            - documents:write
components:
  schemas:
    DocumentUploadResp:
      type: object
      required:
        - document_id
        - download_url
        - sha256
        - size_bytes
        - deduplicated
      properties:
        deduplicated:
          type: boolean
          description: |-
            True iff this hashed identical to an existing document in this
            tenant - we still record the new metadata row but the Trove
            object is shared. The UI surfaces this as a "saved (dedup'd
            against existing copy)" hint.
        document_id:
          type: string
          format: uuid
        download_url:
          type: string
        enriched:
          type: boolean
          description: |-
            True iff this was a same-owner/same-scope dedup AND the caller
            supplied new metadata (tags/caption/category) that we actually
            merged onto the existing row. Lets the UI say "already in your
            library - details updated" honestly vs a bare "already there".
        sha256:
          type: string
        size_bytes:
          type: integer
          format: int64
          minimum: 0
  securitySchemes:
    bearer_pat:
      type: http
      scheme: bearer
      bearerFormat: hq_pat
      description: 'Personal Access Token. Send as `Authorization: Bearer hq_pat_...`.'
    oauth2:
      type: oauth2
      flows:
        authorizationCode:
          authorizationUrl: https://app.hq.zone/v1/oauth/authorize
          tokenUrl: https://api.hq.zone/v1/oauth/token
          refreshUrl: https://api.hq.zone/v1/oauth/token
          scopes:
            admin: Administer the workspace (users, settings, integrations)
            agents:read: View the agents in your workspace
            agents:write: Create and configure agents
            billing:read: View usage and billing information
            conversations:read: Read your conversations and their messages
            conversations:write: Start conversations and send messages on your behalf
            documents:read: Read your documents library
            documents:write: Upload and manage documents in your library
            memory:read: Read what the assistant remembers about you
            memory:write: Correct or delete what the assistant remembers
            schedules:read: View your scheduled tasks
            schedules:write: Create and manage scheduled tasks
            tables:read: Read your tables and their rows
            tables:write: Create tables and add, edit, or delete rows

````