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

# Upload files

> Upload up to 25 files as `multipart/form-data` parts named `files`. Accepted types are PDF, Word, PowerPoint, Excel, and text formats (`.txt`, `.md`, `.html`, `.csv`, `.rtf`), each up to 25 MB.

Indexing starts on its own after the upload: an `active` source indexes and bills immediately, a `draft` source runs a credit estimate instead (see [Retrieve estimate](/api-reference/brain/get-estimate)). Poll [List files](/api-reference/brain/list-files) until each file's `status` is `indexed`.

Files the upload policy refuses (unsupported type, too large, empty) are returned in `rejected` with a `201`; the request is a `400 no_files_accepted` only when every file was refused.




## OpenAPI

````yaml post /brain/sources/{source_id}/files
openapi: 3.0.0
info:
  title: Public API
  version: 1.0.0
servers:
  - url: https://api.gumloop.com/api/v1
security: []
paths:
  /brain/sources/{source_id}/files:
    post:
      tags:
        - Brain
      summary: Upload files
      description: >
        Upload up to 25 files as `multipart/form-data` parts named `files`.
        Accepted types are PDF, Word, PowerPoint, Excel, and text formats
        (`.txt`, `.md`, `.html`, `.csv`, `.rtf`), each up to 25 MB.


        Indexing starts on its own after the upload: an `active` source indexes
        and bills immediately, a `draft` source runs a credit estimate instead
        (see [Retrieve estimate](/api-reference/brain/get-estimate)). Poll [List
        files](/api-reference/brain/list-files) until each file's `status` is
        `indexed`.


        Files the upload policy refuses (unsupported type, too large, empty) are
        returned in `rejected` with a `201`; the request is a `400
        no_files_accepted` only when every file was refused.
      operationId: uploadBrainFiles
      parameters:
        - $ref: '#/components/parameters/BrainSourceId'
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - files
              properties:
                files:
                  type: array
                  maxItems: 25
                  description: >-
                    One part per file. The part's filename becomes the file's
                    name in the source.
                  items:
                    type: string
                    format: binary
      responses:
        '201':
          description: At least one file was stored.
          content:
            application/json:
              schema:
                type: object
                required:
                  - files
                  - rejected
                properties:
                  files:
                    type: array
                    description: Files that were stored, in `uploaded` status.
                    items:
                      $ref: '#/components/schemas/BrainFile'
                  rejected:
                    type: array
                    description: Files the upload policy refused.
                    items:
                      type: object
                      properties:
                        file_name:
                          type: string
                        error:
                          type: string
                  sync_run_id:
                    type: string
                    nullable: true
                    description: >-
                      The indexing (or estimate) run this upload started, when
                      one could be queued.
              example:
                files:
                  - id: f2bf0108-1650-47cd-a20d-15268a3cc5cc
                    file_name: handbook.pdf
                    mime_type: application/pdf
                    size_bytes: 48213
                    sha256: >-
                      9bae5b3a3eb1af86840cd141aa644f73d55ec445e056b74fb2ecffc17f15ac9d
                    status: uploaded
                    error: null
                    document_id: null
                    created_at: '2026-09-25T23:52:13.997124+00:00'
                    indexed_at: null
                rejected:
                  - file_name: notes.exe
                    error: unsupported file type ".exe"
                sync_run_id: 4umrkunyhiBgUhLLBT2toA
        '400':
          description: >-
            No `files` part, more than 25 parts, or `no_files_accepted` (every
            file was refused; the rejections are in `error.details.rejected`).
        '401':
          description: Unauthorized — missing or invalid Gumloop API credentials.
        '403':
          description: Forbidden — the caller cannot manage this source.
        '404':
          description: Source not found.
        '409':
          description: The source is paused (`source_paused`).
        '413':
          description: The request body exceeds the batch limit.
      security:
        - bearerAuth: []
      x-codeSamples:
        - lang: bash
          label: cURL
          source: >
            curl
            'https://api.gumloop.com/api/v1/brain/sources/PFqdAMir8PA2Xc6qcszSN9/files'
            \
              -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
              -F 'files=@handbook.pdf' \
              -F 'files=@release-process.md'
        - lang: python
          label: Python
          source: |
            from gumloop import Gumloop

            client = Gumloop(access_token="YOUR_ACCESS_TOKEN")

            upload = client.brain.upload_files(
                "PFqdAMir8PA2Xc6qcszSN9",
                {"handbook.pdf": open("handbook.pdf", "rb").read()},
            )
            print([f.status for f in upload.files], upload.rejected)
components:
  parameters:
    BrainSourceId:
      name: source_id
      in: path
      required: true
      schema:
        type: string
      description: The source id.
  schemas:
    BrainFile:
      type: object
      required:
        - id
        - file_name
        - status
      properties:
        id:
          type: string
        file_name:
          type: string
        mime_type:
          type: string
        size_bytes:
          type: integer
        sha256:
          type: string
          nullable: true
          description: Hex digest of the uploaded bytes.
        status:
          type: string
          enum:
            - uploaded
            - indexing
            - indexed
            - failed
        error:
          type: string
          nullable: true
          description: Why indexing failed, when `status` is `failed`.
        document_id:
          type: string
          nullable: true
          description: >-
            Set once the file is searchable; matches `document_id` on search
            results.
        created_at:
          type: string
          format: date-time
          nullable: true
        indexed_at:
          type: string
          format: date-time
          nullable: true
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >-
        A personal API key or an [OAuth 2.0](/api-reference/oauth) access token.
        Personal API keys also require the `x-auth-key` header with your user
        ID.

````