openapi: 3.0.3
info:
  title: SOAT Documents API
  version: 1.0.0
  description: API for managing AI-indexed text documents with semantic search
  contact:
    name: SOAT Team
    url: https://github.com/ttoss/soat
servers:
  - url: '{baseUrl}'
    description: Base URL of your SOAT deployment (e.g. https://your-soat.com or http://localhost:5047)
    variables:
      baseUrl:
        description: The base URL of your SOAT deployment
        default: http://localhost:5047
tags:
  - name: Documents
    description: Manage documents
security:
  - bearerAuth: []
paths:
  /api/v1/documents:
    get:
      tags:
        - Documents
      summary: List documents
      description: Returns all documents the caller has access to. If projectId is provided, returns only documents in that project. project keys are scoped to a single project automatically. JWT users without projectId receive documents across all their accessible projects.
      operationId: listDocuments
      parameters:
        - name: project_id
          in: query
          required: false
          description: Project ID (optional)
          schema:
            type: string
            example: 'proj_V1StGXR8Z5jdHi6B'
      responses:
        '200':
          description: List of documents
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/DocumentRecord'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    post:
      tags:
        - Documents
      summary: Create a document
      description: Creates a new text document and generates an embedding vector for semantic search. project keys automatically infer the project from the key's scope; JWT callers must supply projectId.
      operationId: createDocument
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - content
              properties:
                project_id:
                  type: string
                  description: Project ID. Required for JWT auth; omit when using an project key.
                  example: 'proj_V1StGXR8Z5jdHi6B'
                content:
                  type: string
                  example: 'The quick brown fox jumps over the lazy dog.'
                path:
                  type: string
                  description: Logical path within the project (e.g. /reports/q1.txt). Defaults to /filename if omitted.
                  example: '/reports/q1.txt'
                filename:
                  type: string
                  example: 'my-doc.txt'
      responses:
        '201':
          description: Document created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentRecord'
        '400':
          description: Invalid request body
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v1/documents/{document_id}:
    get:
      tags:
        - Documents
      summary: Get a document by ID
      description: Returns a document with its text content
      operationId: getDocument
      parameters:
        - name: document_id
          in: path
          required: true
          description: Document ID
          schema:
            type: string
            example: 'doc_V1StGXR8Z5jdHi6B'
      responses:
        '200':
          description: Document found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentRecord'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Document not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    delete:
      tags:
        - Documents
      summary: Delete a document
      description: Deletes a document and its underlying file
      operationId: deleteDocument
      parameters:
        - name: document_id
          in: path
          required: true
          description: Document ID
          schema:
            type: string
            example: 'doc_V1StGXR8Z5jdHi6B'
      responses:
        '204':
          description: Document deleted
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Document not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    patch:
      tags:
        - Documents
      summary: Update a document
      description: Updates document content, title, path, metadata, or tags. Supplying `path` moves the document to a new logical path within the project.
      operationId: updateDocument
      parameters:
        - name: document_id
          in: path
          required: true
          description: Document ID
          schema:
            type: string
            example: 'doc_V1StGXR8Z5jdHi6B'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                content:
                  type: string
                  description: New text content
                title:
                  type: string
                  description: New title
                path:
                  type: string
                  nullable: true
                  description: Logical path within the project (e.g. /reports/q1.txt). Pass null to clear.
                  example: '/reports/q1.txt'
                metadata:
                  type: object
                  description: Arbitrary metadata object
                tags:
                  type: object
                  additionalProperties:
                    type: string
                  description: Key-value tags
      responses:
        '200':
          description: Document updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentRecord'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Document not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v1/documents/{document_id}/tags:
    get:
      tags:
        - Documents
      summary: Get document tags
      description: Returns all tags attached to the document
      operationId: getDocumentTags
      parameters:
        - name: document_id
          in: path
          required: true
          description: Document ID
          schema:
            type: string
            example: 'doc_V1StGXR8Z5jdHi6B'
      responses:
        '200':
          description: Document tags
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: string
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Document not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    put:
      tags:
        - Documents
      summary: Replace document tags
      description: Replaces all tags on the document with the provided tags (not merged)
      operationId: replaceDocumentTags
      parameters:
        - name: document_id
          in: path
          required: true
          description: Document ID
          schema:
            type: string
            example: 'doc_V1StGXR8Z5jdHi6B'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties:
                type: string
              example:
                category: research
                status: draft
      responses:
        '200':
          description: Tags replaced
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: string
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Document not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    patch:
      tags:
        - Documents
      summary: Merge document tags
      description: Merges provided tags with existing tags (existing tags are preserved unless overridden)
      operationId: mergeDocumentTags
      parameters:
        - name: document_id
          in: path
          required: true
          description: Document ID
          schema:
            type: string
            example: 'doc_V1StGXR8Z5jdHi6B'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties:
                type: string
              example:
                priority: high
      responses:
        '200':
          description: Tags merged
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: string
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Document not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: JWT token or sk_ api key
  schemas:
    DocumentRecord:
      type: object
      properties:
        id:
          type: string
          description: Document ID
          example: 'doc_V1StGXR8Z5jdHi6B'
        file_id:
          type: string
          description: Underlying file ID
          example: 'file_V1StGXR8Z5jdHi6B'
        project_id:
          type: string
          description: Project ID
          example: 'proj_V1StGXR8Z5jdHi6B'
        path:
          type: string
          nullable: true
          description: Logical path of the document within the project (e.g. /reports/q1.txt)
          example: '/reports/q1.txt'
        filename:
          type: string
          description: Original filename
          example: 'my-doc.txt'
        size:
          type: integer
          description: File size in bytes
          example: 42
        content:
          type: string
          nullable: true
          description: Text content (only present on getDocument)
          example: 'The quick brown fox jumps over the lazy dog.'
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    ErrorResponse:
      type: object
      properties:
        error:
          type: string
          example: 'Document not found'
