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

# Usar un archivo ya subido también en otro destino

> Copia un archivo que el cliente **ya subió y confirmó** a otro namespace, sin volver a
subirlo: los bytes se duplican dentro del almacén y no pasan por la API.

El caso que lo motiva: alguien adjunta un tarifario mientras conversa con el asistente
que le crea su agente —eso vive en `agent-briefs`— y ese mismo tarifario tiene que acabar
siendo una base de conocimiento, que consume `knowledge-sources`.

**No se comparte la fila, se duplica**, y no es por comodidad: una subida tiene un solo
reclamante y los dos destinos tienen retenciones distintas. Con una sola fila, borrar el
agente soltaría el documento de una base que quizá consultan otros agentes.

**Manda la política del destino, no la del origen.** `agent-briefs` admite `.md`, `.csv`
y hasta 10 MiB; `knowledge-sources` sólo `.pdf`, `.docx`, `.txt` y hasta 2 MiB —ese tope
es del proveedor que indexa, no nuestro—. Un archivo que no cabe se rechaza nombrándolo,
para que quien llama pueda explicarle al cliente por qué su documento no sirvió para eso.

**No es idempotente**, igual que el presign: cada llamada deja una fila. Una copia que
nadie reclame se la lleva el barrido a las 72 h, así que un reintento cuesta un objeto
temporal y no un huérfano permanente.

Exige los mismos permisos que subir a ese destino directamente: el scope del namespace
**y** `uploads:write`. Por eso el destino va en la ruta y no en el cuerpo.




## OpenAPI

````yaml /openapi/public-api.yaml post /v1/uploads/{namespace}/from/{uploadId}
openapi: 3.1.0
info:
  title: Agents Studio API
  version: 1.0.0
  description: >
    API del backend de Agents Studio. Expone servicios multi-tenant para
    gestionar agentes

    (texto y voz), catálogo de herramientas, habilitación de workspaces y
    operaciones de

    telefonía. Todos los endpoints requieren autenticación Bearer y respetan los
    contratos

    documentados en `docs/modules`.
  license:
    name: Proprietary
    url: https://getsupervisor.ai/legal/terms
servers:
  - url: https://api-prod.studio.getsupervisor.ai
    description: Producción
  - url: https://sandbox.agents.studio.getsupervisor.ai
    description: Sandbox
security:
  - BearerAuth: []
tags:
  - name: Agents
    description: >
      Gestión general de agentes y sus metadatos.

      Scopes requeridos: `agents:read` para consultas y `agents:write` para
      creación, actualización y operaciones sobre teléfonos.
  - name: Agent Versions
    description: Versionado y mantenimiento de versiones publicadas de los agentes.
  - name: Agent Schedules
    description: >-
      Configuración de horarios regulares y excepciones puntuales para controlar
      la disponibilidad de los agentes.
  - name: Campaigns
    description: >
      Gestión de campañas masivas basadas en CSV para ejecutar agentes de forma
      batch.

      Scopes requeridos: `campaigns:read` para consultas y `campaigns:write`
      para creación.
  - name: Calls
    description: |
      Consultas de llamadas (Speech Analytics) disponibles para el workspace.
      Scopes requeridos: `calls:read`.
  - name: Agent Instructions
    description: >
      Operaciones sobre instrucciones (prompt) gestionadas por los usuarios
      finales para

      guiar y corregir el comportamiento del agente. Las instrucciones se
      aplican a la

      versión activa del agente, se mantienen como texto libre sin formato (1 a
      500

      caracteres) y su prioridad se controla mediante el campo `order`.

      Scopes requeridos: `agent-instructions:read` para consultas y
      `agent-instructions:write` para creación o edición.
  - name: Catalogs
    description: >
      Gestión centralizada de catálogos (idiomas, estilos de mensaje, tonos,
      etiquetas, voces y eventos de

      webhook) con alcance global o específico por workspace. Las voces del
      workspace se consultan aquí, con

      `GET /v1/catalogs/items?type=voice`. Scopes requeridos: `catalogs:read`
      para consultas y

      `catalogs:write` para creación y modificaciones.
  - name: Catalog Templates
    description: >
      Catálogo de plantillas de agente publicadas. Sirve para descubrir qué
      plantillas hay disponibles y con

      qué `agentVersionId` clonarlas mediante `POST /v1/agents/from-template`.

      Scopes requeridos: `catalogs:read`.
  - name: Agent Phones
    description: Conexión y desconexión de teléfonos asignados a agentes.
  - name: API Keys
    description: Gestión de credenciales de acceso programático y sus permisos.
  - name: Agent Blueprints
    description: >
      Gestión del blueprint (personalidad) asociado a cada versión de agente.

      Scopes requeridos: `agent-blueprints:read` para lectura y
      `agent-blueprints:write` para cambios en blueprint.
  - name: Blueprint Stages
    description: >
      Autoría de stages de un blueprint del agente, abarcando orden, prompts y
      validaciones previas a la

      publicación. Todas las rutas cuelgan del blueprint
      (`/v1/agents/{agentId}/blueprints/{blueprintId}/stages`),

      así que hay que resolver primero el `blueprintId` con `GET
      /v1/agents/{agentId}/blueprints`.

      Scopes requeridos: `blueprint-stages:read` para consultas y
      `blueprint-stages:write` para creación y edición.

      La validación del grafo se ejecuta automáticamente en cada mutación y la
      sincronización con el proveedor

      sucede mediante jobs internos tras los cambios.
  - name: Stage Triggers
    description: >
      Gestión detallada de triggers que conectan stages dentro del blueprint y
      definen las transiciones

      conversacionales. Scopes requeridos: `stage-triggers:read` y
      `stage-triggers:write`.
  - name: Workspaces
    description: Gestión multi-tenant y habilitación de credenciales por workspace.
  - name: Tools
    description: >
      Catálogo y ejecución de tools disponibles para los agentes.

      Scopes requeridos: `tools:read` para listar y `tools:execute` para
      ejecutar tools.
  - name: Webhooks
    description: >
      Gestión de webhooks por workspace para suscribirse a eventos del dominio y
      recibir notificaciones HTTP.

      Scopes requeridos: `webhooks:read` para consultas y `webhooks:write` para
      crear, actualizar o eliminar webhooks y suscripciones.
  - name: SIP Trunks
    description: >
      Provisionamiento y gestión de SIP trunks para conectar agentes de voz con
      carriers PSTN

      (Twilio, ccc2.uno, etc.) via Kamailio. Genera credenciales digest
      automaticamente.

      Scopes requeridos: `sip:read` para consultas y `sip:write` para creación,
      actualización y eliminación.
  - name: Documents
    description: >
      Gestión de documentos asociados al workspace. Permite obtener URLs
      presignadas

      para subir archivos que luego se referencian al crear o personalizar
      agentes.

      Scopes requeridos: `documents:write`.
  - name: Message Templates
    description: >
      Gestión de message templates de WhatsApp (Kapso). Permite crear, listar y
      eliminar templates,

      así como consultar el estado de la conexión con el número de teléfono
      configurado.

      Scopes requeridos: `agents:read` para consultas y `agents:write` para
      creación y eliminación.
  - name: Billing
    description: |
      Consulta del balance de consumo del workspace sobre el plan contratado.
      Scopes requeridos: `billing:read`.
  - name: Usage
    description: |
      Consultas de consumo de recursos agregado por agente.
      Scopes requeridos: `usage:read`.
  - name: Uploads
    description: >
      Subida de archivos por URL prefirmada: la API reserva el sitio y devuelve
      la URL, y

      el cliente sube contra el almacén sin que los bytes pasen por aquí.

      Toda escritura exige `uploads:write`, y la firma exige **además** el scope
      que decide el

      namespace de la ruta: `agent-briefs` y `avatars` exigen `agents:write`, y

      `knowledge-sources` exige `knowledge-bases:write`.


      Cada namespace declara además qué acepta. `avatars` sólo admite PNG, JPEG
      y WEBP —y

      **no** SVG: se sirven en línea, y un SVG es texto ejecutable—, con un
      archivo por

      lote y 1 MiB como máximo.
  - name: Health
    description: >
      Sonda de disponibilidad del servicio. No requiere autenticación ni
      `X-Workspace-Id`.
  - name: Knowledge Bases
    description: >
      Bases de conocimiento del workspace: el material que el agente consulta
      durante la

      conversación.


      El prefijo nombra el recurso y no la tool que lo implementa, así que
      renombrar la

      tool `knowledge` no mueve estas rutas.


      Las sirve un plugin instalable, y eso tiene una consecuencia visible: si
      la tool no

      está abierta para el workspace, **todas** responden `404` —no `403`—. Una
      feature

      que no está disponible para un cliente no existe para él.


      Scopes requeridos: `knowledge-bases:read` para consultas y
      `knowledge-bases:write`

      para el alta, la baja y el mantenimiento de sus documentos.
paths:
  /v1/uploads/{namespace}/from/{uploadId}:
    post:
      tags:
        - Uploads
      summary: Usar un archivo ya subido también en otro destino
      description: >
        Copia un archivo que el cliente **ya subió y confirmó** a otro
        namespace, sin volver a

        subirlo: los bytes se duplican dentro del almacén y no pasan por la API.


        El caso que lo motiva: alguien adjunta un tarifario mientras conversa
        con el asistente

        que le crea su agente —eso vive en `agent-briefs`— y ese mismo tarifario
        tiene que acabar

        siendo una base de conocimiento, que consume `knowledge-sources`.


        **No se comparte la fila, se duplica**, y no es por comodidad: una
        subida tiene un solo

        reclamante y los dos destinos tienen retenciones distintas. Con una sola
        fila, borrar el

        agente soltaría el documento de una base que quizá consultan otros
        agentes.


        **Manda la política del destino, no la del origen.** `agent-briefs`
        admite `.md`, `.csv`

        y hasta 10 MiB; `knowledge-sources` sólo `.pdf`, `.docx`, `.txt` y hasta
        2 MiB —ese tope

        es del proveedor que indexa, no nuestro—. Un archivo que no cabe se
        rechaza nombrándolo,

        para que quien llama pueda explicarle al cliente por qué su documento no
        sirvió para eso.


        **No es idempotente**, igual que el presign: cada llamada deja una fila.
        Una copia que

        nadie reclame se la lleva el barrido a las 72 h, así que un reintento
        cuesta un objeto

        temporal y no un huérfano permanente.


        Exige los mismos permisos que subir a ese destino directamente: el scope
        del namespace

        **y** `uploads:write`. Por eso el destino va en la ruta y no en el
        cuerpo.
      operationId: reuseUploadInNamespace
      parameters:
        - $ref: '#/components/parameters/XWorkspaceId'
        - $ref: '#/components/parameters/UploadNamespace'
        - $ref: '#/components/parameters/UploadId'
      responses:
        '201':
          description: >
            La copia existe y está verificada. Es una subida nueva, con su
            propio `uploadId` y su

            propio ciclo de vida: hay que reclamarla como cualquier otra o el
            barrido se la lleva.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Upload'
        '400':
          description: El namespace destino no existe.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Faltan el scope del destino o `uploads:write`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: >
            No hay una subida con ese id en este workspace. Un id de otro
            cliente responde igual

            que uno inventado, a propósito.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: >
            El archivo no está en un estado desde el que se pueda reusar: sigue
            a medio subir, o

            se rechazó, caducó o se descartó. Un descarte además ya borró los
            bytes.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '413':
          description: >
            El archivo pesa más de lo que el destino admite.


            Los dos rechazos del destino comparten forma: `code` vale

            `UPLOAD_REJECTED_BY_NAMESPACE` y `details` trae `fileName` y
            `namespace`. **El nombre

            del archivo viaja sólo en `details`, nunca en `message`**: el
            mensaje de un error

            acaba en el log operativo, y el nombre de un fichero es dato del
            cliente. Quien

            quiera enseñárselo lo compone desde `details`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UploadNamespaceRejection'
        '422':
          description: >
            Su extensión no está entre las que el destino acepta.


            Los dos rechazos del destino comparten forma: `code` vale

            `UPLOAD_REJECTED_BY_NAMESPACE` y `details` trae `fileName` y
            `namespace`. **El nombre

            del archivo viaja sólo en `details`, nunca en `message`**: el
            mensaje de un error

            acaba en el log operativo, y el nombre de un fichero es dato del
            cliente. Quien

            quiera enseñárselo lo compone desde `details`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UploadNamespaceRejection'
components:
  parameters:
    XWorkspaceId:
      name: x-workspace-id
      in: header
      required: true
      description: Identificador del workspace multi-tenant.
      schema:
        type: string
        format: uuid
    UploadNamespace:
      name: namespace
      in: path
      required: true
      schema:
        $ref: '#/components/schemas/UploadNamespace'
    UploadId:
      name: uploadId
      in: path
      required: true
      description: >
        La fila que dejó el presign. El namespace **no** viaja en estas rutas:
        sale de la fila,

        porque consultarlo antes de autorizar exigiría leer la base para decidir
        el permiso, y

        eso filtraría si un id existe a quien no debería saberlo.
      schema:
        type: string
        format: uuid
  schemas:
    Upload:
      type: object
      description: Lo que se sabe de un archivo subido, mirado desde fuera.
      properties:
        uploadId:
          type: string
          format: uuid
        namespace:
          $ref: '#/components/schemas/UploadNamespace'
        status:
          $ref: '#/components/schemas/UploadStatus'
        originalFileName:
          type: string
          description: El nombre con el que el cliente lo mandó, sin normalizar.
        contentType:
          type: string
        declaredSizeBytes:
          type: integer
          description: El tamaño que el cliente **dijo** al pedir el presign.
        sizeBytes:
          type:
            - integer
            - 'null'
          description: >
            El tamaño real del objeto, medido al confirmar. Es `null` mientras
            nadie lo haya

            mirado — está aparte de `declaredSizeBytes` precisamente porque
            pueden no coincidir.
        rejectedReason:
          type:
            - string
            - 'null'
          description: >
            Por qué se rechazó, cuando `status` es `rejected`. Si no, `null`.


            Es un **código**, no una frase: hoy `size-mismatch` (lo medido no
            cuadra con lo

            declarado) o `content-mismatch` (los bytes no son del formato que
            dice la

            extensión). El detalle legible va en el mensaje del `422`, no aquí.
          example: content-mismatch
        createdBy:
          type: string
        createdAt:
          type: string
          format: date-time
        confirmedAt:
          type:
            - string
            - 'null'
          format: date-time
      required:
        - uploadId
        - namespace
        - status
        - originalFileName
        - contentType
        - declaredSizeBytes
        - sizeBytes
        - rejectedReason
        - createdBy
        - createdAt
        - confirmedAt
    ErrorResponse:
      type: object
      properties:
        code:
          type: string
          description: Código principal del error (alto nivel).
        message:
          type: string
          description: Mensaje legible para humanos.
        details:
          type: object
          description: >
            Información adicional específica del error.

            Cuando aplique, `details.subcode` provee un identificador estable
            para que el frontend decida qué UI mostrar.
          properties:
            subcode:
              type: string
              description: >-
                Identificador específico del error (p.ej.
                `WORKSPACE_NOT_PROVISIONED`).
            workspaceId:
              type: string
              format: uuid
              description: Identificador de workspace relacionado cuando aplica.
          additionalProperties: true
      required:
        - code
        - message
    UploadNamespaceRejection:
      type: object
      description: >
        El destino rechazó el archivo por su política: la extensión no está
        entre las que acepta,

        o pesa más de lo que admite.


        Tiene esquema propio y no el `ErrorResponse` genérico porque **el nombre
        del archivo llega

        aquí y en ningún otro sitio**. En `message` no viaja a propósito: el
        mensaje de una

        excepción acaba en el log operativo y el nombre de un fichero es dato
        del cliente. Quien

        quiera enseñárselo lo compone desde `details`, y para poder hacerlo
        necesita que el

        contrato garantice el campo — con `ErrorResponse` a secas, `details` es
        un saco abierto y

        los tipos generados lo dejan opcional.
      required:
        - code
        - message
        - details
      properties:
        code:
          type: string
          enum:
            - UPLOAD_REJECTED_BY_NAMESPACE
          description: Identificador estable del rechazo por política del destino.
        message:
          type: string
          description: >
            Por qué el destino no lo admite, **sin datos del cliente dentro**:
            es lo que acaba en

            el log.
        details:
          type: object
          required:
            - fileName
            - namespace
          properties:
            fileName:
              type: string
              description: >
                El nombre con el que el cliente subió el archivo. Viaja sólo
                aquí, nunca en

                `message`.
            namespace:
              type: string
              description: El destino que lo rechazó.
          additionalProperties: true
    UploadNamespace:
      type: string
      enum:
        - agent-briefs
        - knowledge-sources
      description: >
        Destino del archivo. Es un conjunto cerrado y versionado en el código:
        cada namespace

        trae su propia política de formatos, tamaños y permisos.
    UploadStatus:
      type: string
      enum:
        - pending
        - ready
        - rejected
        - expired
        - discarded
      description: >
        En qué punto del ciclo está el archivo.


        `pending` es la fila que dejó el presign: existe antes de que se suba un
        solo byte, y

        caduca a las 24 h si nadie la confirma. `ready` es el archivo verificado
        —sigue siendo

        operable: se puede reclamar y se puede descartar—. `rejected`, `expired`
        y `discarded`

        son estados de los que ya no se sale.
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

````