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

# Upload a document to a project (REST multipart)

> Uploads a document via REST `multipart/form-data`, no MCP connection required. Streams the file bytes in the request body; the route validates type, size, and magic bytes, stores the file in Supabase Storage, and registers a `documents` row, returning a durable `documentId`.

**Does NOT trigger analysis.** Call `POST /api/agent/projects/{id}/audit-analysis/async` (or the MCP `audity_enqueue_document_analysis` tool) once documents are uploaded.

**Cost:** 0 credits, this is a free write.

**Feature gates:** both the `agent-api` and `agent-document-upload` flags must be enabled for the caller, otherwise 403 `FEATURE_NOT_AVAILABLE`.

**Requires:** the `write` scope on PATs.

**Size limits:** 10MB per file, 25MB for `.pptx` presentations.



## OpenAPI

````yaml /api-reference/openapi.json post /api/agent/projects/{id}/documents
openapi: 3.1.0
info:
  title: Audity Agent API
  version: 1.0.0
  summary: >-
    The agent-facing surface of Audity AI: projects, leads, Nucleus, and
    account.
  description: >-
    Audity is a B2B SaaS for consultants who run automated business audits. This
    API is the agent-facing slice: a deliberately small set of task-shaped
    endpoints that lets external AI agents (Claude, ChatGPT, Cursor, n8n) act on
    a consultant's Audity workspace on their behalf.


    **Authentication:** Bearer token (`aky_...`) issued from
    `https://app.auditynow.com/dashboard/settings/api-tokens`. The middleware
    resolves the token to a Clerk user identity; tier and Row-Level Security
    gates apply automatically.


    **Rate limits:** reads 100/min, writes 20/min, async job polling 120/min;
    some expensive or high-volume endpoints have stricter caps. 429 responses
    include `Retry-After`.


    **Tier gating:** public plan names can change faster than API enum names.
    Use `GET /api/user/tier` and any `requiredTier` field in 403 responses as
    the source of truth for a specific account. Read-only or
    lead-generation-focused plans can read available data; audit-capable paid
    plans unlock writes, subject to token scope and credits.


    **Document upload.** `POST /api/agent/projects/{id}/documents` accepts a
    `multipart/form-data` upload directly, no MCP connection required, gated by
    the `agent-api` and `agent-document-upload` feature flags.


    **MCP-only capabilities:** this REST reference does not cover the full agent
    surface. Interview-session creation and PDF-generation status polling exist
    only as MCP tools (`audity_create_interview_session`,
    `audity_get_pdf_status`), reachable via `npx @auditynow/connect` (see the
    Claude/Cursor/ChatGPT connect guides), not as REST endpoints. A REST-only
    client (no MCP capability) cannot perform these actions today.
  contact:
    name: Audity Support
    email: support@auditynow.com
    url: https://app.auditynow.com
  license:
    name: Proprietary
    identifier: LicenseRef-Audity-Proprietary
  termsOfService: https://auditynow.com/terms
servers:
  - url: https://app.auditynow.com
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Projects
    description: >-
      Audit project lifecycle, create, list, fetch, trigger analysis, get
      deliverables.
  - name: Leads
    description: ReadyLink-sourced leads, list, fetch, convert into projects.
  - name: Nucleus
    description: >-
      Nucleus is Audity's persistent memory + insight layer. Search memories,
      capture notes, read proactive insights, manage contacts.
  - name: Account
    description: Identity, tier, credits, usually called once per session for context.
  - name: ReadyLinks
    description: >-
      Manage ReadyLinks, the survey distribution links that generate leads. CRUD
      operations for links, customization, lead tracking.
  - name: Assessment Configs
    description: >-
      Manage assessment configurations that define survey questions, order,
      scoring, and customization.
  - name: Documents
    description: >-
      Upload source documents to a project. REST-only client friendly, no MCP
      connection required.
  - name: Jobs
    description: >-
      Monitor async job status for long-running operations like audit analysis
      synthesis.
paths:
  /api/agent/projects/{id}/documents:
    parameters:
      - $ref: '#/components/parameters/ProjectId'
    post:
      tags:
        - Documents
      summary: Upload a document to a project (REST multipart)
      description: >-
        Uploads a document via REST `multipart/form-data`, no MCP connection
        required. Streams the file bytes in the request body; the route
        validates type, size, and magic bytes, stores the file in Supabase
        Storage, and registers a `documents` row, returning a durable
        `documentId`.


        **Does NOT trigger analysis.** Call `POST
        /api/agent/projects/{id}/audit-analysis/async` (or the MCP
        `audity_enqueue_document_analysis` tool) once documents are uploaded.


        **Cost:** 0 credits, this is a free write.


        **Feature gates:** both the `agent-api` and `agent-document-upload`
        flags must be enabled for the caller, otherwise 403
        `FEATURE_NOT_AVAILABLE`.


        **Requires:** the `write` scope on PATs.


        **Size limits:** 10MB per file, 25MB for `.pptx` presentations.
      operationId: uploadDocument
      parameters:
        - name: Content-Length
          in: header
          required: true
          description: >-
            Byte length of the multipart body. Validated before the body is
            parsed: missing or non-positive returns 411; a value over the
            absolute body cap (25MB + multipart overhead) returns 413.
          schema:
            type: integer
            minimum: 1
        - name: Idempotency-Key
          in: header
          required: false
          description: >-
            Optional client-supplied key for safe retries. A random UUID is
            generated server-side if omitted.
          schema:
            type: string
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/UploadDocumentInput'
      responses:
        '201':
          description: Document uploaded and registered.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UploadDocumentResponse'
        '400':
          description: >-
            Malformed multipart body, missing `file` part, invalid `category`,
            or an unsafe/too-long filename.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorWithCode'
              example:
                error: No file provided (multipart field "file")
                code: INVALID_ARGUMENTS
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: >-
            Either the PAT is missing the `write` scope, or the
            `agent-api`/`agent-document-upload` feature flags are disabled for
            this account.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorWithCode'
              examples:
                featureNotAvailable:
                  summary: agent-api or agent-document-upload flag disabled
                  value:
                    error: Document upload agent API not available
                    code: FEATURE_NOT_AVAILABLE
                scopeInsufficient:
                  summary: PAT missing write scope
                  value:
                    error: 'Token missing required scope(s): write'
                    code: PAT_SCOPE_INSUFFICIENT
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: >-
            A file already exists at the server-computed storage path (a
            UUID-suffixed collision, effectively unreachable in practice).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorWithCode'
              example:
                error: A file already exists at the computed path
                code: CONFLICT
        '411':
          description: Missing or invalid `Content-Length` header.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorWithCode'
              example:
                error: A valid Content-Length header is required
                code: LENGTH_REQUIRED
        '413':
          description: >-
            Request body exceeds the absolute cap (25MB + multipart overhead),
            or the file exceeds its type's size limit (10MB, 25MB for `.pptx`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorWithCode'
              examples:
                bodyTooLarge:
                  summary: Content-Length exceeds the absolute body cap
                  value:
                    error: Request body too large
                    code: FILE_TOO_LARGE
                fileTooLarge:
                  summary: File exceeds its type's size limit
                  value:
                    error: File too large. Maximum is 10MB
                    code: FILE_TOO_LARGE
        '415':
          description: >-
            File type not allowlisted, or its content doesn't match its
            declared/extension type (magic-byte check).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorWithCode'
              example:
                error: >-
                  File content does not match its declared type: not a valid PDF
                  (missing %PDF header)
                code: UNSUPPORTED_MEDIA_TYPE
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
components:
  parameters:
    ProjectId:
      name: id
      in: path
      required: true
      description: The project's UUID.
      schema:
        type: string
        format: uuid
  schemas:
    UploadDocumentInput:
      type: object
      description: >-
        `multipart/form-data` parts for `POST
        /api/agent/projects/{id}/documents`.
      properties:
        file:
          type: string
          format: binary
          description: >-
            The file to upload. Allowed types: PDF, Word (.doc/.docx), plain
            text, Markdown, audio (.mp3/.wav/.m4a), images
            (.png/.jpg/.jpeg/.webp), spreadsheets (.csv/.xlsx/.xls),
            presentations (.pptx), XML, and email (.eml). Max 10MB per file,
            25MB for .pptx.
        category:
          type: string
          enum:
            - audit
            - supporting
          default: audit
          description: Document category. Defaults to `audit` if omitted.
        department:
          type: string
          description: Optional free-text department label.
      required:
        - file
    UploadDocumentResponse:
      type: object
      properties:
        data:
          $ref: '#/components/schemas/AgentUploadedDocument'
        nextStep:
          type: string
          description: Guidance for what to call next.
          example: >-
            Call audity_enqueue_document_analysis (or POST
            .../audit-analysis/async) to analyze uploaded documents.
      required:
        - data
        - nextStep
    ErrorWithCode:
      type: object
      description: >-
        Error response with an optional stable `code` field. When present,
        branch on `code` (the human-readable `error` may change). Some 403/400
        responses omit `code` (e.g. generic feature-disabled gates).
      properties:
        error:
          type: string
          description: Human-readable error message.
        code:
          type: string
          description: >-
            Stable error code, when applicable. Examples: `EMPTY_PATCH`,
            `INVALID_DESCRIPTION_TYPE`, `DESCRIPTION_TOO_LONG`, `PAT_MALFORMED`,
            `PAT_ROUTE_NOT_ALLOWED`, `PAT_NOT_SUPPORTED_FOR_ENDPOINT`,
            `PAT_SCOPE_INSUFFICIENT`, `PAT_DISABLED`.
      required:
        - error
    AgentUploadedDocument:
      type: object
      description: The registered document record returned by the agent upload endpoint.
      properties:
        id:
          type: string
          format: uuid
        documentId:
          type: string
          format: uuid
          description: Same value as `id`, provided for clients that key off `documentId`.
        projectId:
          type: string
          format: uuid
        clientName:
          type:
            - string
            - 'null'
        fileName:
          type: string
        fileType:
          type: string
          description: >-
            Canonical MIME type derived from the validated file extension, not
            the caller-declared MIME type.
        fileSize:
          type: integer
          description: Size in bytes.
        sha256:
          type: string
          description: Hex-encoded SHA-256 of the uploaded bytes.
        storagePath:
          type: string
          description: Server-computed Supabase Storage path.
        category:
          type: string
          enum:
            - audit
            - supporting
        department:
          type:
            - string
            - 'null'
        uploadStatus:
          type: string
        createdAt:
          type: string
          format: date-time
      required:
        - id
        - documentId
        - projectId
        - clientName
        - fileName
        - fileType
        - fileSize
        - sha256
        - storagePath
        - category
        - department
        - uploadStatus
        - createdAt
    Error:
      type: object
      properties:
        error:
          type: string
          description: Human-readable error message.
        details:
          type: string
          description: Additional context (development only).
      required:
        - error
  responses:
    Unauthorized:
      description: Missing or invalid token.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: Unauthorized
    NotFound:
      description: Resource not found, or you don't have access (RLS).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    RateLimited:
      description: Rate limit exceeded.
      headers:
        Retry-After:
          schema:
            type: integer
          description: Seconds to wait before retrying.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    ServerError:
      description: Internal server error.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: Audity Personal Access Token (aky_...)
      description: >-
        A Personal Access Token issued from
        https://app.auditynow.com/dashboard/settings/api-tokens. Format:
        `aky_<32 random chars>`.

````