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

# Upload attachments to an existing operation

> Adds one or more files to an existing operation (the same storage path and OperationAttachment model as the dashboard). Typical flow: create a Pagos a proveedores operation with POST /api/operations, then attach the supplier invoice PDF here.

**Limits (aligned with the dashboard / FS-04 upload budget)**
- Content-Type: `multipart/form-data`
- Field name: `file` or `files` (repeat the part for several files, max 10)
- Types (same allowlist as the dashboard, detected from magic bytes where they exist — not the filename alone): PDF; JPEG, PNG, GIF, WEBP, BMP, TIFF, SVG; XLSX/XLSM (ZIP + `xl/`), XLS (OLE); DOCX (ZIP + `word/`), DOC (OLE); PPTX (ZIP + `ppt/`), PPT (OLE); CSV, TSV, TXT, JSON, XML (text, no NUL / low control-char ratio); ZIP; other non-executable files (`application/octet-stream`). Executables (MZ/ELF/Mach-O) are rejected.
- Max request / file size: 25 MiB
- Optional `display_name` applies when a single file is sent

Completed and adjusted operations accept attachments, matching the dashboard. Soft-deleted operations return 404.



## OpenAPI

````yaml /docs/openapi.json post /api/operations/{id}/attachments
openapi: 3.0.0
info:
  title: FinSync API Documentation
  version: 1.0.0
  description: API documentation for FinSync application
  contact:
    name: FinSync Support
    email: support@finsync.com
servers:
  - url: https://api.finsync.ar
    description: Direct Fly API (preferred for API keys and webhooks)
  - url: https://finsync.ar
    description: Same-origin via finsync.ar (Vercel rewrite)
security:
  - apiKey: []
tags:
  - name: Clients
    description: Operations related to clients and their accounts
  - name: Tags
    description: >-
      Company-scoped client labels (Configuración > Etiquetas). Distinct from
      the special Centro de resultados / Centro de costos tag used for budget
      clients.
  - name: Accounts
    description: Operations related to account balances
  - name: Cost Centers
    description: Company centros de resultados (cost_center_id) and their budgets
  - name: Audit
    description: >-
      Read-only company audit trail. Requires audit.view for employee / personal
      API keys. Company x-api-key sees the full company. Tenant scope is
      audit_logs.company_id (FS-03: never attributed by actor).
  - name: Operations
    description: Operations for creating and managing financial operations
  - name: Operation Types
    description: Company operation types management
  - name: Transaction Types
    description: Company transaction types management
  - name: WhatsApp Tools
    description: >-
      Company-scoped WhatsApp bot operation tools (Configuración > WhatsApp >
      Tools). Same WhatsAppOperationTool rows as the dashboard. Authenticate
      with x-api-key.
  - name: External client
    description: >-
      Exports for external (portal-linked) clients. Authenticate with
      `x-api-key` (company-owned client ID) or `Authorization: Bearer` (Auth.js
      session for an ExternalAccessGrant).
  - name: Mesas
    description: >-
      Company FX trading desks (mesas). Same records and paths as the dashboard
      Mesas module. Integrators authenticate with x-api-key; the dashboard uses
      an employee session. Superadmin must set mesas_enabled first; there is no
      public PATCH for that flag. created_by / opened_by / closed_by are null
      for API-key callers.
  - name: Home metrics
    description: >-
      Named dashboard home metrics (list + values). API keys receive the
      Métricas-group cards (day P&L, commissions, cost centers) without the
      metrics-password cookie. Employee sessions need the matching view
      permission; they still skip the UI password gate on this surface.
paths:
  /api/operations/{id}/attachments:
    post:
      tags:
        - Operations
      summary: Upload attachments to an existing operation
      description: >-
        Adds one or more files to an existing operation (the same storage path
        and OperationAttachment model as the dashboard). Typical flow: create a
        Pagos a proveedores operation with POST /api/operations, then attach the
        supplier invoice PDF here.


        **Limits (aligned with the dashboard / FS-04 upload budget)**

        - Content-Type: `multipart/form-data`

        - Field name: `file` or `files` (repeat the part for several files, max
        10)

        - Types (same allowlist as the dashboard, detected from magic bytes
        where they exist — not the filename alone): PDF; JPEG, PNG, GIF, WEBP,
        BMP, TIFF, SVG; XLSX/XLSM (ZIP + `xl/`), XLS (OLE); DOCX (ZIP +
        `word/`), DOC (OLE); PPTX (ZIP + `ppt/`), PPT (OLE); CSV, TSV, TXT,
        JSON, XML (text, no NUL / low control-char ratio); ZIP; other
        non-executable files (`application/octet-stream`). Executables
        (MZ/ELF/Mach-O) are rejected.

        - Max request / file size: 25 MiB

        - Optional `display_name` applies when a single file is sent


        Completed and adjusted operations accept attachments, matching the
        dashboard. Soft-deleted operations return 404.
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: integer
          description: Existing operation ID
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - file
              properties:
                file:
                  type: string
                  format: binary
                  description: One file. Repeat the part or use `files` to send several.
                files:
                  type: string
                  format: binary
                  description: Additional files (same allowlist and 25 MiB budget)
                display_name:
                  type: string
                  description: >-
                    Visible name for a single-file upload. The original
                    extension is kept if omitted.
      responses:
        '200':
          description: Uploaded attachments (same shape as GET)
          content:
            application/json:
              schema:
                type: object
                properties:
                  attachments:
                    type: array
                    items:
                      $ref: '#/components/schemas/OperationAttachment'
              example:
                attachments:
                  - id: 501
                    file_name: factura-proveedor.pdf
                    mime_type: application/pdf
                    size_bytes: 184320
                    created_at: '2026-04-09T18:30:00.000Z'
                    uploaded_by: null
                    signed_url: https://example.r2.cloudflarestorage.com/...
        '400':
          description: Missing file, empty file, too many files, or body is not multipart
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
              examples:
                missing:
                  value:
                    error: file is required
                notMultipart:
                  value:
                    error: multipart/form-data with one or more files is required
        '401':
          description: Unauthorized - Missing or invalid API key
        '404':
          description: Operation not found or belongs to another company
        '413':
          description: File or multipart body exceeds 25 MiB (FS-04)
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
              example:
                error: File too large
        '415':
          description: >-
            Unsupported or spoofed media type (executables, or a declared
            allowlisted type that does not match magic bytes / text
            classification)
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
              example:
                error: >-
                  Unsupported media type. Allowed types: PDF, JPEG, PNG, GIF,
                  WEBP, BMP, TIFF, SVG, XLSX / XLSM, XLS, DOCX, DOC, PPTX, PPT,
                  CSV, TSV, TXT, JSON, XML, ZIP, other non-executable files
                  (application/octet-stream)
        '500':
          description: Server error
      security:
        - apiKey: []
components:
  schemas:
    OperationAttachment:
      type: object
      description: >-
        Metadata for a file stored on an existing operation. signed_url is a
        temporary R2 download link (1 hour).
      properties:
        id:
          type: integer
        file_name:
          type: string
        mime_type:
          type: string
          nullable: true
          enum:
            - application/pdf
            - image/jpeg
            - image/png
            - image/gif
            - image/webp
            - image/bmp
            - image/tiff
            - image/svg+xml
            - application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
            - application/vnd.ms-excel.sheet.macroEnabled.12
            - application/vnd.ms-excel
            - >-
              application/vnd.openxmlformats-officedocument.wordprocessingml.document
            - application/msword
            - >-
              application/vnd.openxmlformats-officedocument.presentationml.presentation
            - application/vnd.ms-powerpoint
            - text/csv
            - text/tab-separated-values
            - text/plain
            - application/json
            - application/xml
            - text/xml
            - application/zip
            - application/octet-stream
        size_bytes:
          type: integer
          nullable: true
        created_at:
          type: string
          format: date-time
        uploaded_by:
          $ref: '#/components/schemas/PublicAuditActor'
        signed_url:
          type: string
          format: uri
          nullable: true
          description: Temporary signed download URL (expires in 3600 seconds)
    PublicAuditActor:
      type: object
      description: >-
        Resolved actor for an audit event or operation. Email is included
        because audit_logs.changed_by stores only the employee email; without it
        integrators cannot tell two people with the same display name apart. It
        is Employee.email, never an AuthUser secret, password hash, or session
        token. id is null when the email does not match a current employee of
        this company (deleted employee, missing GUC, company API key write).
      properties:
        id:
          type: integer
          nullable: true
        name:
          type: string
          nullable: true
        email:
          type: string
          nullable: true
  securitySchemes:
    apiKey:
      type: apiKey
      in: header
      name: x-api-key
      description: API Key for authentication

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.