{
  "openapi": "3.1.1",
  "paths": {
    "/v1/external/conversations/{id}/resolve": {
      "post": {
        "operationId": "ExternalConversationsController_resolve",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResolveConversationDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "La conversacion, con alreadyResolved en true si ya estaba resuelta. Reintentar es seguro.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EscalamientoResueltoDto"
                }
              }
            }
          },
          "400": {
            "description": "Falta la resolucion o viene vacia. El cuerpo trae errors con el campo que fallo."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso conversations:resolve."
          },
          "404": {
            "description": "La conversacion no existe o no es de esta organizacion."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Avisar que un escalamiento se resolvio",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/conversations/{id}/send": {
      "post": {
        "operationId": "ExternalConversationsController_enviar",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EnviarMensajeDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "El mensaje quedo guardado y encolado en el canal de la conversacion. Encolado, no entregado. alreadySent en true si esa externalRef ya habia enviado: reintentar es seguro.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MensajeEnviadoDto"
                }
              }
            }
          },
          "400": {
            "description": "Falta message o externalRef, vienen vacios o no son texto, o el cuerpo trae un campo de autor (senderName, authorName, author, senderId, userId): el autor lo fija la credencial, no se manda."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso conversations:send."
          },
          "404": {
            "description": "La conversacion no existe o no es de esta organizacion."
          },
          "409": {
            "description": "code agent_in_control: el agente esta atendiendo un caso abierto. Toma el control desde la bandeja o cierra el caso y reintenta."
          },
          "422": {
            "description": "code channel_window_closed: pasaron mas de 24 horas desde el ultimo mensaje de la persona y WhatsApp solo admite plantillas; el mensaje no se mando. code no_outbound_channel: la conversacion no tiene por donde escribirle a la persona."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Mandar un mensaje a la persona de una conversacion, sin que corra el agente",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/conversations/{id}": {
      "get": {
        "operationId": "ExternalConversationsController_find",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "El estado de la conversacion.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EstadoDeConversacionDto"
                }
              }
            }
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso conversations:read."
          },
          "404": {
            "description": "La conversacion no existe o no es de esta organizacion."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Ver el estado de una conversacion",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/conversations/{id}/historial": {
      "get": {
        "operationId": "ExternalConversationsController_historial",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Las conversaciones anteriores, la mas nueva primero.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HistorialDelClienteDto"
                }
              }
            }
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso conversations:read."
          },
          "404": {
            "description": "La conversacion no existe o no es de esta organizacion."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Ver las conversaciones anteriores de la misma persona",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/conversations/{id}/campos": {
      "put": {
        "operationId": "ExternalConversationsController_escribirCampos",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EscribirCamposDelCasoDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Que claves entraron y cuales no. Que una clave se rechace no es un error: las demas se escribieron.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CamposDelCasoEscritosDto"
                }
              }
            }
          },
          "400": {
            "description": "Falta datos o no es un objeto."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso conversations:write."
          },
          "404": {
            "description": "La conversacion no existe o no es de esta organizacion."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Escribir en el caso los datos que ya tiene tu sistema",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/conversations/{id}/derivar": {
      "post": {
        "operationId": "ExternalConversationsController_derivar",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DerivarCasoDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Donde quedo el caso. movido en false si ya estaba ahi: reintentar es seguro.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CasoDerivadoDto"
                }
              }
            }
          },
          "400": {
            "description": "La etapa no es de ese embudo, o el embudo no tiene etapa de entrada y no nombraste una."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso conversations:route."
          },
          "404": {
            "description": "La conversacion o el embudo no existen, o no son de esta organizacion."
          },
          "422": {
            "description": "La etapa destino exige campos del caso que faltan. El cuerpo trae code CAMPOS_OBLIGATORIOS y la lista faltantes, con key y nombre de cada campo."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Mandar el caso a otro embudo",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/conversations/{id}/notas": {
      "post": {
        "operationId": "ExternalNotasController_crear",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CrearNotaInternaDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "La nota quedo en el chat, visible solo para el equipo. Nunca se manda a la persona por ningun canal ni la lee el agente. alreadyCreated en true si esa externalRef ya habia dejado una nota: reintentar es seguro.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotaInternaCreadaDto"
                }
              }
            }
          },
          "400": {
            "description": "Falta texto, viene vacio o no es texto, o el cuerpo trae un campo de autor (senderName, authorName, author, senderId, userId): el autor lo fija la credencial, no se manda."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso conversations:annotate."
          },
          "404": {
            "description": "La conversacion no existe o no es de esta organizacion."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Dejar una nota interna en el chat de una conversacion",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/agents/{slug}/conversations": {
      "post": {
        "operationId": "ExternalAgentConversationsController_open",
        "parameters": [
          {
            "name": "slug",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OpenAgentConversationDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "La conversacion, nueva o la que ya existia para ese externalId. Es idempotente por externalId.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConversacionAbiertaDto"
                }
              }
            }
          },
          "400": {
            "description": "Falta un campo del cuerpo o el externalId esta vacio. El cuerpo trae errors con el campo que fallo."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso agents:converse."
          },
          "404": {
            "description": "No hay un agente con ese slug en esta organizacion, es interno, o no atiende por API."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Abrir una conversacion con un agente",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/conversations/{id}/messages": {
      "post": {
        "operationId": "ExternalAgentConversationsController_send",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendAgentMessageDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "La respuesta del agente.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RespuestaDelAgenteDto"
                }
              }
            }
          },
          "202": {
            "description": "Se vencio el techo de espera. La respuesta se escribe despues y se lee con GET /conversations/:id/messages.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RespuestaDelAgenteDto"
                }
              }
            }
          },
          "400": {
            "description": "El mensaje esta vacio o falta."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso agents:converse."
          },
          "404": {
            "description": "La conversacion no existe o no es de esta organizacion."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Mandar un mensaje y recibir la respuesta",
        "tags": [
          "external-api"
        ]
      },
      "get": {
        "operationId": "ExternalAgentConversationsController_messages",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "incluirNotas",
            "required": false,
            "in": "query",
            "description": "true para recibir tambien las notas internas del chat, con role internal_note y su autor. Sin esto el hilo es solo la conversacion con la persona.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "El hilo de la conversacion.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HiloDeConversacionDto"
                }
              }
            }
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso conversations:read."
          },
          "404": {
            "description": "La conversacion no existe o no es de esta organizacion."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Leer los mensajes de una conversacion",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/knowledge/documents": {
      "put": {
        "description": "Idempotente por (coleccion, filename): el mismo filename reemplaza el documento y sus chunks.",
        "operationId": "ExternalKnowledgeDocumentsController_upsert",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PushKnowledgeDocumentDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Documento creado o reemplazado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentoEmpujadoDto"
                }
              }
            }
          },
          "400": {
            "description": "Falta un campo o el contenido esta vacio. El cuerpo trae errors con el campo que fallo y la forma de lo que llego, nunca su valor. Un campo de mas se descarta en silencio, no da error."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso knowledge:write."
          },
          "404": {
            "description": "La coleccion no existe o no es de esta organizacion."
          },
          "413": {
            "description": "El documento pasa los 15 MB."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Crear o reemplazar un documento de conocimiento",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/knowledge/collections": {
      "post": {
        "description": "Idempotente por slug: la segunda llamada con el mismo slug devuelve la misma coleccion, con 200 en vez de 201.",
        "operationId": "ExternalKnowledgeController_crearColeccion",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CrearColeccionDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ya existia una con ese slug: es la misma, no una copia.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ColeccionCreadaDto"
                }
              }
            }
          },
          "201": {
            "description": "La coleccion, recien creada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ColeccionCreadaDto"
                }
              }
            }
          },
          "400": {
            "description": "Falta el slug o el nombre, o el slug no tiene la forma esperada. El cuerpo trae errors con el campo que fallo."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso knowledge:write."
          },
          "409": {
            "description": "No se pudo crear ni encontrar. Reintenta."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Crear una coleccion de conocimiento, o recuperar la que ya existe",
        "tags": [
          "external-api"
        ]
      },
      "get": {
        "operationId": "ExternalKnowledgeController_listarColecciones",
        "parameters": [
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Cuántas filas devolver. Por omisión 50.",
            "schema": {
              "minimum": 1,
              "maximum": 500,
              "default": 50,
              "type": "number"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Cuántas saltear. Por omisión 0.",
            "schema": {
              "minimum": 0,
              "default": 0,
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Una pagina de colecciones.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginaDeColeccionesDto"
                }
              }
            }
          },
          "400": {
            "description": "El limit pasa de 500, o limit/offset no son enteros."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso knowledge:read."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Listar las colecciones de conocimiento",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/knowledge/collections/{collectionId}/documents": {
      "get": {
        "operationId": "ExternalKnowledgeController_listarDocumentos",
        "parameters": [
          {
            "name": "collectionId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Cuántas filas devolver. Por omisión 50.",
            "schema": {
              "minimum": 1,
              "maximum": 500,
              "default": 50,
              "type": "number"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Cuántas saltear. Por omisión 0.",
            "schema": {
              "minimum": 0,
              "default": 0,
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Una pagina de documentos.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginaDeDocumentosDto"
                }
              }
            }
          },
          "400": {
            "description": "El limit pasa de 500, o limit/offset no son enteros."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso knowledge:read."
          },
          "404": {
            "description": "La coleccion no existe o no es de esta organizacion."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Listar los documentos de una coleccion",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/knowledge/documents/{documentId}": {
      "delete": {
        "description": "Borra sus trozos y saca el documento de circulacion. El agente deja de poder citarlo.",
        "operationId": "ExternalKnowledgeController_borrarDocumento",
        "parameters": [
          {
            "name": "documentId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Borrado."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso knowledge:write."
          },
          "404": {
            "description": "El documento no existe o no es de esta organizacion."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Borrar un documento y sacarlo del indice",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/agents/{agentId}/knowledge/{collectionId}": {
      "put": {
        "description": "Idempotente: asignar la misma dos veces no la duplica ni falla. Sin esto, el documento esta indexado y no lo alcanza nadie.",
        "operationId": "ExternalKnowledgeController_asignarColeccion",
        "parameters": [
          {
            "name": "agentId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "collectionId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Asignada."
          },
          "400": {
            "description": "Esa coleccion no existe en tu organizacion."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso knowledge:write."
          },
          "404": {
            "description": "El agente no existe, no es de esta organizacion, o es interno."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Asignarle una coleccion a un agente",
        "tags": [
          "external-api"
        ]
      },
      "delete": {
        "description": "La coleccion no se borra: deja de estar asignada a ese agente. Quitar algo que no estaba no es un error.",
        "operationId": "ExternalKnowledgeController_quitarColeccion",
        "parameters": [
          {
            "name": "agentId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "collectionId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Quitada."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso knowledge:write."
          },
          "404": {
            "description": "El agente no existe, no es de esta organizacion, o es interno."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Quitarle una coleccion a un agente",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/agent-templates": {
      "get": {
        "description": "Las privadas de tu organizacion mas las del catalogo comun. De aca sale el templateId para instanciar.",
        "operationId": "ExternalAgentTemplatesController_listarPlantillas",
        "parameters": [
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Cuántas filas devolver. Por omisión 50.",
            "schema": {
              "minimum": 1,
              "maximum": 500,
              "default": 50,
              "type": "number"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Cuántas saltear. Por omisión 0.",
            "schema": {
              "minimum": 0,
              "default": 0,
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Una pagina de plantillas.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginaDePlantillasDto"
                }
              }
            }
          },
          "400": {
            "description": "El limit pasa de 500, o limit/offset no son enteros."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso agents:write."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Listar las plantillas de agente disponibles",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/agent-templates/{templateId}/instantiate": {
      "post": {
        "description": "Idempotente por slug: reintentar con el mismo slug devuelve el mismo agente, con 200 en vez de 201. El agente queda en draft; se activa aparte.",
        "operationId": "ExternalAgentTemplatesController_instanciar",
        "parameters": [
          {
            "name": "templateId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InstanciarPlantillaDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ese slug ya nombraba un agente: es el mismo, no una copia.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgenteInstanciadoDto"
                }
              }
            }
          },
          "201": {
            "description": "El agente, recien creado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgenteInstanciadoDto"
                }
              }
            }
          },
          "400": {
            "description": "Falta el slug o no tiene la forma esperada, o el cuerpo nombra tools. El cuerpo trae errors con el campo que fallo."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso agents:write."
          },
          "404": {
            "description": "Esa plantilla no existe para ti."
          },
          "409": {
            "description": "No se pudo crear ni encontrar. Reintenta."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Crear un agente a partir de una plantilla",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/agents/{agentId}/template": {
      "post": {
        "description": "Fotografia su configuracion —prompt, modelo, acciones, canales y las colecciones de conocimiento que tiene asignadas— para poder clonarlo.",
        "operationId": "ExternalAgentTemplatesController_sacarPlantilla",
        "parameters": [
          {
            "name": "agentId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SacarPlantillaDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "La plantilla.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlantillaDto"
                }
              }
            }
          },
          "400": {
            "description": "Falta el nombre o la categoria. El cuerpo trae errors."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso agents:write."
          },
          "404": {
            "description": "Ese agente no existe, no es de esta organizacion, o es interno."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Sacarle una plantilla a un agente que ya anda",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/agents/{agentId}/activate": {
      "post": {
        "description": "Devuelve readiness con lo que todavia impide que alguien lo alcance: sin flujo, en un flujo pero no en su etapa de entrada, o sin el canal api. Que readiness venga vacio significa que ya lo alcanza cualquiera.",
        "operationId": "ExternalAgentTemplatesController_activar",
        "parameters": [
          {
            "name": "agentId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Activo, con su readiness.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgenteActivadoDto"
                }
              }
            }
          },
          "400": {
            "description": "Le falta el prompt o el modelo: activarlo reventaria con el primer mensaje."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso agents:write."
          },
          "404": {
            "description": "Ese agente no existe, no es de esta organizacion, o es interno."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Activar un agente",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/agents/{agentId}/pause": {
      "post": {
        "description": "Deja de atender. Las conversaciones abiertas quedan; no contesta hasta que se lo active de nuevo.",
        "operationId": "ExternalAgentTemplatesController_pausar",
        "parameters": [
          {
            "name": "agentId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Pausado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgenteAprovisionadoDto"
                }
              }
            }
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso agents:write."
          },
          "404": {
            "description": "Ese agente no existe, no es de esta organizacion, o es interno."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Pausar un agente",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/agents": {
      "get": {
        "operationId": "ExternalAgentsLecturaController_listar",
        "parameters": [
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Cuántas filas devolver. Por omisión 50.",
            "schema": {
              "minimum": 1,
              "maximum": 500,
              "default": 50,
              "type": "number"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Cuántas saltear. Por omisión 0.",
            "schema": {
              "minimum": 0,
              "default": 0,
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Una pagina de agentes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginaDeAgentesDto"
                }
              }
            }
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso agents:read."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Listar los agentes de la organizacion",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/agents/{agentId}": {
      "get": {
        "operationId": "ExternalAgentsLecturaController_leerUno",
        "parameters": [
          {
            "name": "agentId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "El agente. knowledge viene solo si la credencial ademas tiene knowledge:read.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgenteDto"
                }
              }
            }
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso agents:read."
          },
          "404": {
            "description": "No existe en tu organizacion."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Leer un agente entero",
        "tags": [
          "external-api"
        ]
      },
      "patch": {
        "description": "Cambia solo lo que mandes. Si mandas expectedVersion y alguien lo cambio en el medio, la respuesta es 409 y no se escribe nada.",
        "operationId": "ExternalAgentsEscrituraController_actualizar",
        "parameters": [
          {
            "name": "agentId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ActualizarAgenteDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "El agente, con su version nueva.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgenteGestionadoDto"
                }
              }
            }
          },
          "400": {
            "description": "El cuerpo nombra tools o audience, o el modelo no existe. Trae errors con el campo que fallo."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso agents:write."
          },
          "404": {
            "description": "Ese agente no existe, no es de esta organizacion, o es interno."
          },
          "409": {
            "description": "Cambio desde que lo leiste. Vuelve a leerlo y reintenta."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Cambiar la configuracion de un agente",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/agents/{agentId}/prompt-versions": {
      "get": {
        "description": "De la mas nueva a la mas vieja, SIN el texto: un prompt puede pesar cientos de KB y esto es para elegir a donde volver.",
        "operationId": "ExternalAgentsEscrituraController_historial",
        "parameters": [
          {
            "name": "agentId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Las versiones.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HistorialDePromptsDto"
                }
              }
            }
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso agents:read."
          },
          "404": {
            "description": "No existe en tu organizacion."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "El historial de instrucciones del agente",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/agents/{agentId}/prompt-versions/{versionId}/restore": {
      "post": {
        "description": "Escribe el texto viejo como una version NUEVA. No rebobina: nunca se pierde a lo que estabas volviendo.",
        "operationId": "ExternalAgentsEscrituraController_restaurar",
        "parameters": [
          {
            "name": "agentId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "versionId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "El agente, con el prompt viejo.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgenteRestauradoDto"
                }
              }
            }
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso agents:write."
          },
          "404": {
            "description": "Ese agente o esa version no existen en tu organizacion."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Volver a una version anterior del prompt",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/actions/authorization": {
      "get": {
        "description": "Lo que autorizas al registrar una accion HTTP. La version que devuelve es la que va en autorizacionVersion; si el destino no esta en Chile, ademas hay que confirmar la salida del pais.",
        "operationId": "ExternalAccionesController_textoDeAutorizacion",
        "parameters": [],
        "responses": {
          "200": {
            "description": "El texto vigente.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AutorizacionVigenteDto"
                }
              }
            }
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso ninguno: alcanza con una credencial viva."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "El texto de autorizacion que hay que aceptar, y su version",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/actions": {
      "post": {
        "description": "Queda para cualquiera de tus agentes hasta que se la concedes a uno (PUT agents/:agentId/actions/:slug); paraClientes la restringe. La autorizacion de destino (purpose, destinationCountry, autorizacionVersion) es opcional como bloque: si mandas uno, van los tres. Reintentar con el mismo slug reescribe la misma accion con lo que mandes (lo que no mandes se conserva), no crea otra.",
        "operationId": "ExternalAccionesController_crearAccion",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CrearAccionHttpDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "La accion registrada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccionDto"
                }
              }
            }
          },
          "400": {
            "description": "La autorizacion de destino vino a medias o con una version vieja, o la URL no se puede usar (privada, no resuelve, no es http/https), o una variable no sirve (no existe, nombre invalido, repetida o igual a un parametro). El mensaje dice cual."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "Falta actions:write, o la URL apunta a un sitio que la credencial no tiene permitido (tambien al reescribir por slug una accion que ya apunta a uno). Los sitios los agrega una persona con rol de administrador en Integraciones, API."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Registrar una accion HTTP",
        "tags": [
          "external-api"
        ]
      },
      "get": {
        "description": "Las que registraste tú. No incluye las nuestras ni las de un servidor MCP: para ver que alcanza un agente concreto, GET agents/:agentId/actions.",
        "operationId": "ExternalAccionesController_listarAcciones",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Las acciones.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListaDeAccionesDto"
                }
              }
            }
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso actions:read."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Las acciones HTTP de tu organizacion",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/actions/{slug}": {
      "delete": {
        "description": "Deja de estar disponible y se retira de todos los agentes. El slug queda ocupado: volver a registrarlo reescribe esta misma accion.",
        "operationId": "ExternalAccionesController_borrarAccion",
        "parameters": [
          {
            "name": "slug",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Dada de baja."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso actions:write."
          },
          "404": {
            "description": "No tienes una accion con ese slug."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Dar de baja una accion HTTP",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/agents/{agentId}/actions": {
      "get": {
        "description": "El catalogo completo que ese agente puede tener, con enabled diciendo cuales estan concedidas hoy. No incluye la direccion a la que llama cada accion; cada entrada trae destinationOrigin: solo el origen.",
        "operationId": "ExternalAccionesController_listar",
        "parameters": [
          {
            "name": "agentId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Las acciones.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogoDeAccionesDelAgenteDto"
                }
              }
            }
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso actions:read."
          },
          "404": {
            "description": "Ese agente no existe, no es de esta organizacion, o es interno."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Que acciones alcanza un agente y cuales tiene prendidas",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/agents/{agentId}/actions/{slug}": {
      "put": {
        "description": "Idempotente: concederla dos veces no la duplica ni falla. Se concede de a una, nunca mandando la lista entera.",
        "operationId": "ExternalAccionesController_conceder",
        "parameters": [
          {
            "name": "agentId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "slug",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Concedida."
          },
          "400": {
            "description": "Esa accion no existe para tu organizacion, o no alcanza a este agente."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso actions:write."
          },
          "404": {
            "description": "Ese agente no existe, no es de esta organizacion, o es interno."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Concederle una accion a un agente",
        "tags": [
          "external-api"
        ]
      },
      "delete": {
        "description": "Quitar algo que no estaba no es un error. La accion no se borra: deja de estar concedida a ese agente.",
        "operationId": "ExternalAccionesController_quitar",
        "parameters": [
          {
            "name": "agentId",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "slug",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Quitada."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso actions:write."
          },
          "404": {
            "description": "Ese agente no existe, no es de esta organizacion, o es interno."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Quitarle una accion a un agente",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/events": {
      "post": {
        "operationId": "ExternalEventosController_reportar",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReportarHechoDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Todos los triggers que escuchaban ya le habian llegado a esa persona. No se escribio nada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HechoReportadoDto"
                }
              }
            }
          },
          "201": {
            "description": "Disparo al menos un trigger: el agente le escribio a esa persona.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HechoReportadoDto"
                }
              }
            }
          },
          "202": {
            "description": "No se escribio nada y tampoco se habia escrito antes. El cuerpo trae motivo: sin trigger, control humano, o error si ninguno pudo despachar.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HechoReportadoDto"
                }
              }
            }
          },
          "400": {
            "description": "Falta un campo del cuerpo o esta vacio. El cuerpo trae errors con el campo que fallo."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso events:write."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Reportar un hecho de tu sistema",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/triggers": {
      "post": {
        "operationId": "ExternalTriggersController_crear",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CrearTriggerDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "El trigger creado, activo.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TriggerDto"
                }
              }
            }
          },
          "400": {
            "description": "Falta un campo del cuerpo o esta vacio. El cuerpo trae errors con el campo que fallo."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso triggers:write."
          },
          "404": {
            "description": "No hay un agente con ese id en esta organizacion, o no es de los que la API administra."
          },
          "422": {
            "description": "El agente existe y es tuyo, pero no esta activo: no podria escribir."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Crear un trigger",
        "tags": [
          "external-api"
        ]
      },
      "get": {
        "operationId": "ExternalTriggersController_listar",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Los triggers, el mas nuevo primero.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TriggersDto"
                }
              }
            }
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso triggers:read."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Listar los triggers de la organizacion",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/triggers/{id}": {
      "patch": {
        "operationId": "ExternalTriggersController_editar",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EditarTriggerDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "El trigger como quedo.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TriggerDto"
                }
              }
            }
          },
          "400": {
            "description": "Un campo del cuerpo no es valido."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso triggers:write."
          },
          "404": {
            "description": "No hay un trigger con ese id en esta organizacion."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Editar o pausar un trigger",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/agents/{slug}/runs": {
      "post": {
        "description": "El agente trabaja solo, fuera de este pedido, hasta cerrar el encargo o topar con un limite. La respuesta llega enseguida con la corrida en curso; se sigue con GET /runs/{id}.\n\nCon este permiso el agente corre desatendido cualquier accion que tenga concedida, incluidas las que escriben.\n\npedidoPor dice a nombre de quien corre, y tiene que venir firmado por tu sistema: firma = HMAC-SHA256 en hex minuscula, con el secreto de servicio que tu sistema comparte con Lelemon como llave, sobre el texto \"lelemon-run:v1:<id>:<vence>\". vence son segundos Unix (UTC), en el futuro y a no mas de 300 segundos. Sin firma valida y vigente responde 401 y no crea nada.\n\nEl header Idempotency-Key es opcional: si tu sistema reintenta el mismo POST (mismo agente, misma clave), te devolvemos la corrida que ya se creo la primera vez, con 200, en vez de crear una segunda. Sin el header, cada POST crea una corrida nueva, como siempre.",
        "operationId": "ExternalRunsController_crear",
        "parameters": [
          {
            "name": "slug",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "idempotency-key",
            "required": true,
            "in": "header",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "Clave del reintento. Repetirla para el mismo agente devuelve la corrida ya creada, sin duplicarla.",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CrearCorridaDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ya existia una corrida con esta Idempotency-Key para este agente: se devuelve esa, sin crear otra.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CorridaDto"
                }
              }
            }
          },
          "201": {
            "description": "La corrida, en curso y en la cola.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CorridaDto"
                }
              }
            }
          },
          "400": {
            "description": "Falta la meta o pedidoPor, o un campo no tiene la forma que se pide (el cuerpo trae errors con el campo); o Idempotency-Key no tiene entre 1 y 200 caracteres imprimibles sin espacios."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida; o pedidoPor no trae una firma valida y vigente."
          },
          "403": {
            "description": "A la credencial le falta el permiso runs:write, o el agente es interno y no se expone por API."
          },
          "404": {
            "description": "No hay un agente con ese slug en esta organizacion."
          },
          "422": {
            "description": "El agente no esta activo, o no atiende por el canal api."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          },
          "503": {
            "description": "No se pudo poner la corrida en la cola. No quedo nada corriendo: reintenta."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Encargarle un trabajo a un agente",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/runs/{id}": {
      "get": {
        "operationId": "ExternalRunsController_leer",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "La corrida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CorridaDto"
                }
              }
            }
          },
          "400": {
            "description": "El id no es un uuid."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso runs:read."
          },
          "404": {
            "description": "La corrida no existe o no es de esta organizacion."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Ver en que va una corrida",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/runs/{id}/cancel": {
      "post": {
        "operationId": "ExternalRunsController_cancelar",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "La corrida. Si nadie la estaba corriendo, queda cancelada. Si la esta corriendo un worker, queda con cancelacionPedida y se cancela al empezar su vuelta siguiente: lo que estaba haciendo termina.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CorridaDto"
                }
              }
            }
          },
          "400": {
            "description": "El id no es un uuid."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso runs:write."
          },
          "404": {
            "description": "La corrida no existe o no es de esta organizacion."
          },
          "409": {
            "description": "La corrida ya termino: no hay nada que cancelar."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Cancelar una corrida",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/runs/{id}/answer": {
      "post": {
        "description": "La corrida pausada retoma con la respuesta como contexto de la ronda siguiente. respondidoPor sigue el mismo formato firmado que pedidoPor al crear la corrida.",
        "operationId": "ExternalRunsController_responder",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResponderCorridaDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "La corrida, retomada con la respuesta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CorridaDto"
                }
              }
            }
          },
          "400": {
            "description": "El id no es un uuid, o falta respuesta o respondidoPor."
          },
          "401": {
            "description": "Credencial invalida; o respondidoPor no trae una firma valida y vigente."
          },
          "403": {
            "description": "A la credencial le falta el permiso runs:write."
          },
          "404": {
            "description": "La corrida no existe o no es de esta organizacion."
          },
          "409": {
            "description": "La corrida no esta esperando una respuesta."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Responderle a una corrida que pregunto algo",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/scheduling/agendas": {
      "get": {
        "operationId": "ExternalAgendaController_agendas",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Las agendas activas, con el slug que las otras rutas piden. Una lista vacia significa que la organizacion no configuro ninguna todavia.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgendasDto"
                }
              }
            }
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso scheduling:read."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Ver que agendas hay",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/scheduling/availability": {
      "get": {
        "operationId": "ExternalAgendaController_disponibilidad",
        "parameters": [
          {
            "name": "agenda",
            "required": true,
            "in": "query",
            "description": "Qué agenda mirar, por su slug. Los slugs salen de GET /v1/external/scheduling/agendas.",
            "schema": {
              "type": "string",
              "examples": [
                "fundador"
              ]
            }
          },
          {
            "name": "daysAhead",
            "required": false,
            "in": "query",
            "description": "Cuántos días hacia adelante mirar. Por omisión 7. El techo real lo pone la propia agenda, y este número no puede pasarlo.",
            "schema": {
              "minimum": 1,
              "maximum": 60,
              "default": 7,
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Los bloques libres, ordenados de mas cercano a mas lejano. Una lista vacia significa que no hay ninguno en la ventana pedida.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DisponibilidadDto"
                }
              }
            }
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso scheduling:read."
          },
          "404": {
            "description": "Esa agenda no existe en tu organizacion, o esta apagada."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Ver los horarios libres de una agenda",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/scheduling/meetings": {
      "post": {
        "operationId": "ExternalAgendaController_reservar",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReservarBloqueDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "La reunion quedo creada. A quien agendo le llega la invitacion con el link de la videollamada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReunionAgendadaDto"
                }
              }
            }
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso scheduling:write."
          },
          "404": {
            "description": "Esa agenda no existe en tu organizacion, o esta apagada."
          },
          "409": {
            "description": "Ese horario ya no esta libre: alguien lo tomo entre que pediste la disponibilidad y reservaste. Vuelve a pedir los horarios y elegi otro. Reintentar el mismo no va a andar."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Reservar un horario",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/pipelines": {
      "get": {
        "operationId": "ExternalEmbudosController_listar",
        "parameters": [
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Cuántas filas devolver. Por omisión 50.",
            "schema": {
              "minimum": 1,
              "maximum": 500,
              "default": 50,
              "type": "number"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Cuántas saltear. Por omisión 0.",
            "schema": {
              "minimum": 0,
              "default": 0,
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Una pagina de embudos.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginaDeEmbudosDto"
                }
              }
            }
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso agents:read."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Listar los embudos de la organizacion",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/pipelines/{id}": {
      "get": {
        "operationId": "ExternalEmbudosController_leerUno",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "El embudo, con sus etapas y la definicion de sus campos. Un campo sensible viene marcado con sensitive: true, sin valor: esto describe el embudo, no un caso.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EmbudoDto"
                }
              }
            }
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso agents:read."
          },
          "404": {
            "description": "No existe en tu organizacion."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Leer un embudo entero, con sus etapas y sus campos",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/pipelines/{id}/fields/options": {
      "put": {
        "description": "Reemplaza por completo las opciones de los campos que el cuerpo nombra; los demas quedan como estaban. Repetir el mismo pedido no cambia nada y devuelve changed: false. Padre e hijo de una cascada van en el mismo pedido, porque se validan juntos. Los casos ya guardados conservan su valor aunque la opcion salga del catalogo.",
        "operationId": "ExternalEmbudosController_reemplazarOpciones",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReemplazarOpcionesDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Como quedo cada campo nombrado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OpcionesReemplazadasDto"
                }
              }
            }
          },
          "400": {
            "description": "Un campo que el embudo no declara, uno que no es de lista, el mismo campo dos veces, o una cascada que queda invalida: el mensaje nombra el campo, la opcion y el valor."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso pipelines:write."
          },
          "404": {
            "description": "No existe en tu organizacion."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Reemplazar las opciones de los campos de lista de un embudo",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/pipelines/{id}/respuestas-rapidas": {
      "get": {
        "description": "Los textos que el equipo inserta en el chat escribiendo / y el atajo. Salen tal como estan guardados, con {agente} y {cliente} sin reemplazar.",
        "operationId": "ExternalEmbudosController_leerRespuestasRapidas",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Las respuestas del embudo. Vacia si no tiene.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RespuestasRapidasDto"
                }
              }
            }
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso agents:read."
          },
          "404": {
            "description": "No existe en tu organizacion."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Leer las respuestas rapidas de un embudo",
        "tags": [
          "external-api"
        ]
      },
      "put": {
        "description": "Reemplaza la lista entera: lo que no viene se borra, y una lista vacia deja el embudo sin respuestas. No toca ninguna otra configuracion del embudo. Se validan igual que en el panel: hasta 50 respuestas, atajos unicos de 1 a 30 caracteres en minuscula, textos de hasta 4096. En el texto, {agente} se reemplaza por el nombre de quien la inserta y {cliente} por el nombre del contacto; si falta el dato queda vacio. Repetir el mismo pedido no cambia nada y devuelve changed: false.",
        "operationId": "ExternalEmbudosController_reemplazarRespuestasRapidas",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReemplazarRespuestasRapidasDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Como quedaron las respuestas.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RespuestasRapidasReemplazadasDto"
                }
              }
            }
          },
          "400": {
            "description": "El cuerpo no es una lista de atajo y texto, o alguna respuesta no pasa la validacion del panel: el mensaje nombra el atajo y el motivo."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso pipelines:write."
          },
          "404": {
            "description": "No existe en tu organizacion."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Reemplazar las respuestas rapidas de un embudo",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/agents/{agentSlug}/skills": {
      "post": {
        "description": "Una skill es un instructivo que el agente sigue para una tarea. En su prompt viaja solo el indice —el slug y la descripcion de cada una—, y el contenido lo carga cuando va a hacer esa tarea, con la herramienta cargar_skill. Por eso la descripcion dice CUANDO usarla y el contenido dice COMO.\n\nEl agente no ve ninguna skill mientras no tenga concedida la accion cargar_skill.\n\nIdempotente por slug: la segunda llamada con el mismo slug y el MISMO cuerpo devuelve la misma skill, con 200 en vez de 201, y se la asigna al agente si todavia no la tenia.\n\nSi el slug ya existe y el cuerpo difiere, contesta 409: crear no reescribe. Descartar lo que mandaste y contestar 200 dejaria a quien re-empuja sus skills en cada deploy creyendo que actualizo, con el agente siguiendo el procedimiento anterior. Para cambiarla, PATCH.\n\nUn agente admite hasta 30 skills. El indice viaja en el prompt en cada mensaje, asi que la que pasa el tope se rechaza con 409 en vez de recortarse en silencio.",
        "operationId": "ExternalSkillsController_crear",
        "parameters": [
          {
            "name": "agentSlug",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CrearSkillDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ya existia una con ese slug: es la misma, no una copia.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SkillDto"
                }
              }
            }
          },
          "201": {
            "description": "La skill, recien creada y asignada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SkillDto"
                }
              }
            }
          },
          "400": {
            "description": "Falta un campo, el slug no tiene la forma esperada, o el contenido pasa los 20.000 caracteres. El cuerpo trae errors con el campo que fallo."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso skills:write."
          },
          "404": {
            "description": "El agente no existe, no es de esta organizacion, o es interno."
          },
          "409": {
            "description": "El slug ya existe con otro cuerpo (editalo con PATCH), o el agente llego al tope de skills."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Crear una skill y asignarsela a un agente",
        "tags": [
          "external-api"
        ]
      },
      "get": {
        "description": "Devuelve el indice, no el contenido: lo que el agente ve en su prompt. Para el texto completo de una, GET /v1/external/agents/{agentSlug}/skills/{skillSlug}.",
        "operationId": "ExternalSkillsController_listar",
        "parameters": [
          {
            "name": "agentSlug",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Cuántas filas devolver. Por omisión 50.",
            "schema": {
              "minimum": 1,
              "maximum": 500,
              "default": 50,
              "type": "number"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Cuántas saltear. Por omisión 0.",
            "schema": {
              "minimum": 0,
              "default": 0,
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Una pagina de skills.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginaDeSkillsDto"
                }
              }
            }
          },
          "400": {
            "description": "El limit pasa de 500, o limit/offset no son enteros."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso skills:read."
          },
          "404": {
            "description": "El agente no existe, no es de esta organizacion, o es interno."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Listar las skills de un agente",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/agents/{agentSlug}/skills/{skillSlug}": {
      "get": {
        "description": "Devuelve el instructivo completo: lo que el agente lee cuando la carga. Es lo que hace auditable que sigue el agente en esa tarea, sin poder reescribirlo. La lista devuelve solo el indice.",
        "operationId": "ExternalSkillsController_leer",
        "parameters": [
          {
            "name": "agentSlug",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "skillSlug",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "La skill con su contenido.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SkillConContenidoDto"
                }
              }
            }
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso skills:read."
          },
          "404": {
            "description": "El agente o la skill no existen en esta organizacion."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Leer una skill, con su contenido",
        "tags": [
          "external-api"
        ]
      },
      "patch": {
        "description": "Cambia solo lo que mandes. El slug no se edita: es la identidad con la que el agente la nombra.\n\nUn cuerpo vacio es 400: no hay edicion que aplicar, y contestar 200 invalidaria el runtime cacheado del agente diciendo que se cambio algo.",
        "operationId": "ExternalSkillsController_actualizar",
        "parameters": [
          {
            "name": "agentSlug",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "skillSlug",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ActualizarSkillDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "La skill, con los cambios aplicados.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SkillDto"
                }
              }
            }
          },
          "400": {
            "description": "El cuerpo viene vacio, un campo no tiene la forma que se pide, o el contenido pasa los 20.000 caracteres."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso skills:write."
          },
          "404": {
            "description": "El agente o la skill no existen en esta organizacion."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Editar una skill",
        "tags": [
          "external-api"
        ]
      },
      "delete": {
        "description": "Borra la skill de tu organizacion y la saca de TODOS los agentes que la tuvieran, no solo del que nombra la ruta. Borrarla dos veces contesta 404 la segunda.",
        "operationId": "ExternalSkillsController_borrar",
        "parameters": [
          {
            "name": "agentSlug",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "skillSlug",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Borrada."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso skills:write."
          },
          "404": {
            "description": "El agente o la skill no existen en esta organizacion."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Borrar una skill",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/metrics/conversations": {
      "get": {
        "operationId": "ExternalMetricasController_conversaciones",
        "parameters": [
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Cuántas filas devolver. Por omisión 50.",
            "schema": {
              "minimum": 1,
              "maximum": 500,
              "default": 50,
              "type": "number"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Cuántas saltear. Por omisión 0.",
            "schema": {
              "minimum": 0,
              "default": 0,
              "type": "number"
            }
          },
          {
            "name": "from",
            "required": true,
            "in": "query",
            "description": "Desde cuándo, en ISO 8601. Trae las conversaciones cuyo último cambio fue en el rango.",
            "schema": {
              "type": "string",
              "examples": [
                "2026-10-19T13:00:00Z"
              ]
            }
          },
          {
            "name": "to",
            "required": false,
            "in": "query",
            "description": "Hasta cuándo, sin incluirlo. Por omisión, ahora. A lo más 31 días después de from.",
            "schema": {
              "type": "string",
              "examples": [
                "2026-10-19T14:00:00Z"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Una fila por conversación con sus tiempos: cuándo entró, se derivó, la tomó una persona y se cerró, quién la cerró, y los segundos con el agente y con una persona.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginaDeMetricasDto"
                }
              }
            }
          },
          "400": {
            "description": "Rango inválido."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso conversations:read."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Métricas por conversación, de las que cambiaron en un rango",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/conversations/{id}/adjuntos": {
      "get": {
        "description": "Junta lo que el cliente mando por el chat (imagenes, audios y videos) y todo lo adjuntado a la ficha del caso: los documentos que el cliente mando por el chat y los archivos que subio el equipo. subidoPor dice cual es cual. El mas nuevo primero. Cada uno trae una url firmada que vive 10 minutos. No salen los archivos que el equipo manda por el chat, ni la imagen que el filtro de seguridad bloqueo, ni lo borrado.",
        "operationId": "ExternalAdjuntosController_listar",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Cuántas filas devolver. Por omisión 50.",
            "schema": {
              "minimum": 1,
              "maximum": 500,
              "default": 50,
              "type": "number"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Cuántas saltear. Por omisión 0.",
            "schema": {
              "minimum": 0,
              "default": 0,
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Una pagina de adjuntos.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginaDeAdjuntosDto"
                }
              }
            }
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso conversations:read."
          },
          "404": {
            "description": "La conversacion no existe o no es de esta organizacion."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Listar los archivos de una conversacion, con su descarga",
        "tags": [
          "external-api"
        ]
      }
    },
    "/v1/external/miembros/disponibilidad": {
      "get": {
        "description": "Busca a la persona por su email entre los miembros activos de tu organizacion. estado es lo que quedo marcado; disponibleParaRepartir dice si el reparto le asignaria un caso ahora, considerando el horario de atencion de la organizacion.",
        "operationId": "ExternalMiembrosController_leer",
        "parameters": [
          {
            "name": "email",
            "required": true,
            "in": "query",
            "description": "El email de la persona del equipo. No distingue mayusculas. Tiene que ser un miembro activo de tu organizacion.",
            "schema": {
              "type": "string",
              "examples": [
                "ana@empresa.cl"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "El estado de la persona.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DisponibilidadDelMiembroDto"
                }
              }
            }
          },
          "400": {
            "description": "Falta el email o no es un email."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso miembros:read."
          },
          "404": {
            "description": "Ese email no es un miembro activo de tu organizacion. Es la misma respuesta si el email existe en otra organizacion."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Ver si una persona del equipo esta disponible",
        "tags": [
          "external-api"
        ]
      },
      "put": {
        "description": "Hace lo mismo que cuando la persona cambia su propio estado: el reparto de casos nuevos lo lee de ahi. Ausente la saca del reparto y le quita la extension de horario que haya pedido. Disponible la deja marcada, pero si la organizacion tiene horario de atencion y ya cerro, el auto-away por horario sigue mandando: el reparto la trata como ausente (disponibleParaRepartir en false) hasta que abra el horario, salvo que ella pida seguir un rato mas. Si ya estaba en ese estado no se escribe nada ni se toca su extension, asi que repetir el pedido es seguro.",
        "operationId": "ExternalMiembrosController_cambiar",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CambiarDisponibilidadDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Como quedo la persona.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DisponibilidadDelMiembroDto"
                }
              }
            }
          },
          "400": {
            "description": "Falta el email o el estado, o el estado no es disponible ni ausente."
          },
          "401": {
            "description": "Credencial ausente, revocada o invalida."
          },
          "403": {
            "description": "A la credencial le falta el permiso miembros:write."
          },
          "404": {
            "description": "Ese email no es un miembro activo de tu organizacion. Es la misma respuesta si el email existe en otra organizacion."
          },
          "429": {
            "description": "Se paso un techo por minuto: 3.000 pedidos con la misma credencial, o 200 desde la misma direccion IP. Los dos se cuentan en cada instancia del servidor. La respuesta trae Retry-After con los segundos que faltan."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Poner a una persona del equipo disponible o ausente",
        "tags": [
          "external-api"
        ]
      }
    }
  },
  "info": {
    "title": "Lelemon API",
    "description": "La superficie que Lelemon expone para que otro sistema la use, con una credencial de organizacion.",
    "version": "1.0",
    "contact": {}
  },
  "tags": [],
  "servers": [],
  "components": {
    "securitySchemes": {
      "api-key": {
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "type": "http",
        "description": "La credencial de organizacion, en la cabecera Authorization: Bearer lel_<prefijo>_<secreto>."
      }
    },
    "schemas": {
      "ResolveConversationDto": {
        "type": "object",
        "properties": {
          "resolution": {
            "type": "string",
            "maxLength": 2000,
            "description": "Que se resolvio, en palabras. Queda escrito en la conversacion."
          },
          "message": {
            "type": "string",
            "maxLength": 4000,
            "description": "Si viene, se le manda al contacto por su canal. Si se omite, la resolucion solo queda registrada."
          },
          "externalRef": {
            "type": "string",
            "maxLength": 120,
            "description": "El id del ticket de su lado. Con el, un reintento del mismo aviso no se procesa dos veces."
          },
          "returnToAgent": {
            "type": "boolean",
            "default": true,
            "description": "Por defecto el control vuelve al agente. false lo deja en manos de una persona."
          }
        }
      },
      "EscalamientoResueltoDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "El id de la conversacion."
          },
          "controlStatus": {
            "type": "string",
            "description": "Quien la tiene despues del aviso. Lo normal es agent: el control vuelve al agente. Queda como estaba (human o escalated) si una persona del equipo la tenia tomada, o si mandaste returnToAgent: false.",
            "enum": [
              "agent",
              "human",
              "escalated"
            ]
          },
          "messageQueued": {
            "type": "boolean",
            "description": "true si el mensaje al contacto quedo encolado. Encolado, no entregado: el transporte lo despacha despues."
          },
          "alreadyResolved": {
            "type": "boolean",
            "description": "true si este aviso ya se habia procesado antes. Reintentar es seguro."
          }
        },
        "required": [
          "id",
          "controlStatus",
          "messageQueued",
          "alreadyResolved"
        ]
      },
      "EnviarMensajeDto": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string",
            "maxLength": 4000,
            "description": "El texto que le llega a la persona. No puede ser solo espacios."
          },
          "externalRef": {
            "type": "string",
            "maxLength": 120,
            "description": "El id de este envio en su sistema. Un reintento con el mismo valor no manda el mensaje dos veces."
          }
        },
        "required": [
          "message",
          "externalRef"
        ]
      },
      "MensajeEnviadoDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "El id de la conversacion."
          },
          "messageId": {
            "type": "string",
            "description": "El id del mensaje que quedo guardado en la conversacion. En un reintento es el del primer envio."
          },
          "alreadySent": {
            "type": "boolean",
            "description": "true si esa externalRef ya habia enviado un mensaje en esta conversacion y esta llamada no mando nada. Reintentar es seguro."
          }
        },
        "required": [
          "id",
          "messageId",
          "alreadySent"
        ]
      },
      "CampoDelCasoDto": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "La clave del campo, tal como se declaro en el embudo."
          },
          "label": {
            "type": "string",
            "description": "Como se muestra en el panel."
          },
          "type": {
            "type": "string",
            "description": "El tipo del campo, tal como se configuro."
          },
          "value": {
            "type": [
              "string",
              "null"
            ],
            "description": "El valor guardado en la ficha del contacto. null si el campo no tiene dato todavia."
          }
        },
        "required": [
          "key",
          "label",
          "type",
          "value"
        ]
      },
      "EstadoDeConversacionDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "El id de la conversacion."
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Nombre del contacto."
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "description": "Correo del contacto."
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "description": "Telefono del contacto."
          },
          "status": {
            "type": "string",
            "description": "El estado del caso en el flujo.",
            "enum": [
              "pending",
              "active",
              "waiting",
              "completed",
              "failed",
              "abandoned",
              "expired"
            ]
          },
          "controlStatus": {
            "type": "string",
            "description": "Quien la tiene: agent el agente, human una persona del equipo, escalated esperando que alguien la tome.",
            "enum": [
              "agent",
              "human",
              "escalated"
            ]
          },
          "controlledBy": {
            "type": [
              "string",
              "null"
            ],
            "description": "El id de la persona que la tomo, cuando controlStatus es human."
          },
          "assignedTo": {
            "type": [
              "string",
              "null"
            ],
            "description": "El id de la persona duena del caso. null si nadie lo tiene asignado."
          },
          "escalatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "escalationReason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Por que se escalo, si se escalo."
          },
          "pipelineId": {
            "type": "string",
            "description": "El flujo en el que esta el caso."
          },
          "pipelineName": {
            "type": "string"
          },
          "currentStageId": {
            "type": [
              "string",
              "null"
            ]
          },
          "currentStageName": {
            "type": [
              "string",
              "null"
            ]
          },
          "lastMessageAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "inboundNumberLabel": {
            "type": [
              "string",
              "null"
            ],
            "description": "La etiqueta del numero de WhatsApp del negocio por el que entro. null si no entro por WhatsApp."
          },
          "inboundNumberDisplay": {
            "type": [
              "string",
              "null"
            ],
            "description": "Ese numero, como se muestra. null si no entro por WhatsApp."
          },
          "fields": {
            "description": "Los campos del embudo ACTUAL del caso, en el orden en que se declararon. Un campo sensible no aparece: ni la clave, ni el valor.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CampoDelCasoDto"
            }
          }
        },
        "required": [
          "id",
          "name",
          "email",
          "phone",
          "status",
          "controlStatus",
          "controlledBy",
          "assignedTo",
          "escalatedAt",
          "escalationReason",
          "pipelineId",
          "pipelineName",
          "currentStageId",
          "currentStageName",
          "lastMessageAt",
          "createdAt",
          "inboundNumberLabel",
          "inboundNumberDisplay",
          "fields"
        ]
      },
      "ConversacionAnteriorDto": {
        "type": "object",
        "properties": {
          "conversationId": {
            "type": "string",
            "description": "El id de esa conversacion anterior. Sus mensajes se leen con GET /conversations/{id}/messages."
          },
          "pipelineId": {
            "type": "string"
          },
          "pipelineName": {
            "type": "string",
            "examples": [
              "SAC Webhosting"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "active",
              "waiting",
              "completed",
              "failed",
              "abandoned",
              "expired"
            ]
          },
          "outcome": {
            "type": [
              "string",
              "null"
            ],
            "description": "Por que termino: la clave del sistema (inactividad_despedida, seguimiento_agotado...) o el motivo que declara la etapa de cierre. null si no tiene."
          },
          "summary": {
            "type": [
              "string",
              "null"
            ],
            "description": "Resumen breve de la conversacion, escrito al cerrarse. null si no hay."
          },
          "firstMessage": {
            "type": [
              "string",
              "null"
            ],
            "description": "Los primeros 160 caracteres de lo primero que escribio el cliente. null si no escribio."
          },
          "messageCount": {
            "type": "number"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "lastMessageAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "required": [
          "conversationId",
          "pipelineId",
          "pipelineName",
          "status",
          "outcome",
          "summary",
          "firstMessage",
          "messageCount",
          "createdAt",
          "lastMessageAt"
        ]
      },
      "HistorialDelClienteDto": {
        "type": "object",
        "properties": {
          "conversationId": {
            "type": "string",
            "description": "La conversacion por la que se pregunto."
          },
          "conversations": {
            "description": "Las otras conversaciones de la misma persona en esta organizacion, de la mas nueva a la mas vieja. Hasta 50.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ConversacionAnteriorDto"
            }
          }
        },
        "required": [
          "conversationId",
          "conversations"
        ]
      },
      "EscribirCamposDelCasoDto": {
        "type": "object",
        "properties": {
          "datos": {
            "type": "object",
            "description": "Las claves y valores a escribir. Las claves son las que declara el embudo, sin importar quien las llena. Los valores son texto; los vacios se rechazan porque borrarian el que ya estaba.",
            "additionalProperties": {
              "type": "string"
            },
            "examples": [
              {
                "rut": "76543210-K",
                "plan": "Hosting Pro",
                "sucursal": "Talca"
              }
            ]
          }
        },
        "required": [
          "datos"
        ]
      },
      "CamposDelCasoEscritosDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "El id del caso."
          },
          "guardados": {
            "description": "Las claves que quedaron escritas en la ficha del contacto. Una clave que ya valia lo mismo vuelve aca igual: la llamada es idempotente y reintentarla no la saca de esta lista.",
            "type": "array",
            "items": {
              "type": "string"
            },
            "examples": [
              [
                "rut",
                "plan"
              ]
            ]
          },
          "rechazados": {
            "description": "Las claves que no entraron, por su nombre. Una clave cae aca si el embudo no la declara, si el valor venia vacio o no era texto, si no cumple el tipo del campo (un email sin arroba, por ejemplo), o si depende de otro campo que se rechazo en esta misma llamada. El \"quien lo llena\" NO limita esta puerta: cualquier clave declarada se puede escribir, la llene el equipo, el canal o el agente. No es un error: las demas si se escribieron.",
            "type": "array",
            "items": {
              "type": "string"
            },
            "examples": [
              [
                "motivo"
              ]
            ]
          },
          "motivos": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Por que se rechazo cada clave de \"rechazados\" que no paso el tipo del campo. Solo trae las claves de esa causa: si una se rechazo por no estar declarada en el embudo, no aparece aca.",
            "examples": [
              {
                "correo": "no parece un correo: falta el @ o el dominio"
              }
            ]
          },
          "limpiados": {
            "description": "Las claves que esta escritura BORRO de la ficha. Pasa cuando se escribe un campo del que dependen las opciones de otro (dependsOn en GET /pipelines/{id}) y el valor que el otro tenia ya no esta entre las validas para el valor nuevo: ese campo se limpia y hay que volver a pedirlo. No es un error, y las claves de aca no vuelven en \"guardados\" ni en \"rechazados\".",
            "type": "array",
            "items": {
              "type": "string"
            },
            "examples": [
              [
                "comuna"
              ]
            ]
          }
        },
        "required": [
          "id",
          "guardados",
          "rechazados",
          "motivos",
          "limpiados"
        ]
      },
      "DerivarCasoDto": {
        "type": "object",
        "properties": {
          "pipeline": {
            "type": "string",
            "description": "El embudo destino, por slug o por id.",
            "examples": [
              "soporte-hosting"
            ]
          },
          "stage": {
            "type": "string",
            "description": "La etapa destino dentro de ese embudo, por slug o por id. Si se omite, el caso entra por la etapa de entrada del embudo. Una etapa que no sea de ese embudo es un 400: no se corrige sola.",
            "examples": [
              "ticket-nuevo"
            ]
          }
        },
        "required": [
          "pipeline"
        ]
      },
      "CasoDerivadoDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "El id del caso."
          },
          "pipelineId": {
            "type": "string",
            "description": "El id del embudo en el que quedo."
          },
          "pipelineSlug": {
            "type": "string",
            "description": "Su slug.",
            "examples": [
              "soporte-hosting"
            ]
          },
          "stageId": {
            "type": "string",
            "description": "El id de la etapa en la que quedo."
          },
          "stageSlug": {
            "type": "string",
            "description": "Su slug.",
            "examples": [
              "ticket-nuevo"
            ]
          },
          "movido": {
            "type": "boolean",
            "description": "false si el caso ya estaba en esa etapa, o sea que esta llamada no lo movio. Reintentar es seguro: no se reescribe ni se vuelve a avisar."
          }
        },
        "required": [
          "id",
          "pipelineId",
          "pipelineSlug",
          "stageId",
          "stageSlug",
          "movido"
        ]
      },
      "CrearNotaInternaDto": {
        "type": "object",
        "properties": {
          "texto": {
            "type": "string",
            "maxLength": 4000,
            "description": "El texto de la nota. La ve solo el equipo. No puede ser solo espacios."
          },
          "externalRef": {
            "type": "string",
            "maxLength": 120,
            "description": "El id de esta nota en su sistema. Un reintento con el mismo valor no deja la nota dos veces."
          }
        },
        "required": [
          "texto"
        ]
      },
      "NotaInternaCreadaDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "El id de la conversacion."
          },
          "messageId": {
            "type": "string",
            "description": "El id de la nota. Es el mismo con que aparece en GET /conversations/{id}/messages?incluirNotas=true. En un reintento es el de la primera."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "alreadyCreated": {
            "type": "boolean",
            "description": "true si esa externalRef ya habia dejado una nota en esta conversacion y esta llamada no dejo ninguna. Reintentar es seguro."
          }
        },
        "required": [
          "id",
          "messageId",
          "createdAt",
          "alreadyCreated"
        ]
      },
      "OpenAgentConversationDto": {
        "type": "object",
        "properties": {
          "externalId": {
            "type": "string",
            "maxLength": 120,
            "description": "El id de esta conversacion en su sistema, unico en la organizacion. Si ya existe, se devuelve esa conversacion."
          },
          "contactName": {
            "type": "string",
            "maxLength": 255
          },
          "contactEmail": {
            "type": "string",
            "maxLength": 255,
            "format": "email"
          },
          "contactPhone": {
            "type": "string",
            "maxLength": 50
          }
        },
        "required": [
          "externalId"
        ]
      },
      "ConversacionAbiertaDto": {
        "type": "object",
        "properties": {
          "conversationId": {
            "type": "string",
            "description": "El id de la conversacion. Es lo que va en la URL para mandar y leer mensajes."
          },
          "agentSlug": {
            "type": "string",
            "description": "El agente que la atiende.",
            "examples": [
              "ventas"
            ]
          },
          "created": {
            "type": "boolean",
            "description": "true si se abrio en esta llamada; false si ese externalId ya tenia una conversacion y se devolvio esa. Un reintento la ve en false."
          }
        },
        "required": [
          "conversationId",
          "agentSlug",
          "created"
        ]
      },
      "SendAgentMessageDto": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string",
            "maxLength": 4000
          },
          "externalRef": {
            "type": "string",
            "maxLength": 120,
            "description": "El id de este mensaje en su sistema. Un reintento con el mismo valor no guarda el mensaje dos veces ni hace contestar al agente otra vez: devuelve lo que paso con el primero. Mandenlo siempre."
          }
        },
        "required": [
          "message"
        ]
      },
      "RespuestaDelAgenteDto": {
        "type": "object",
        "properties": {
          "conversationId": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "description": "answered: el agente contesto y la respuesta viene en response (HTTP 200). pending: se vencio el techo de espera y la respuesta se escribe despues; se lee con GET /conversations/{id}/messages (HTTP 202). human: alguien del equipo tomo la conversacion, el mensaje le llego a esa persona y el agente no contesta (HTTP 200).",
            "enum": [
              "answered",
              "pending",
              "human"
            ]
          },
          "response": {
            "type": "string",
            "description": "Lo que contesto el agente. Solo viene cuando status es answered."
          },
          "alreadySent": {
            "type": "boolean",
            "description": "true si ese externalRef ya habia llegado en esta conversacion: el mensaje no se guardo otra vez y la respuesta es la del primero. Reintentar es seguro."
          }
        },
        "required": [
          "conversationId",
          "status"
        ]
      },
      "MensajeDto": {
        "type": "object",
        "properties": {
          "role": {
            "type": "string",
            "description": "user lo que mando el cliente; assistant lo que contesto el agente; human lo que escribio alguien del equipo; internal_note una nota interna, que la persona nunca ve (solo viene con incluirNotas=true).",
            "enum": [
              "user",
              "assistant",
              "human",
              "internal_note"
            ]
          },
          "content": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "author": {
            "type": "string",
            "description": "Quien dejo la nota: el nombre de la persona del equipo o de la credencial. Solo viene en internal_note."
          }
        },
        "required": [
          "role",
          "content",
          "createdAt"
        ]
      },
      "HiloDeConversacionDto": {
        "type": "object",
        "properties": {
          "conversationId": {
            "type": "string"
          },
          "messages": {
            "description": "Del mas viejo al mas nuevo.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MensajeDto"
            }
          }
        },
        "required": [
          "conversationId",
          "messages"
        ]
      },
      "PushKnowledgeDocumentDto": {
        "type": "object",
        "properties": {
          "collectionId": {
            "type": "string",
            "description": "La colección donde vive el documento. Tiene que ser tuya."
          },
          "filename": {
            "type": "string",
            "description": "Ruta relativa del archivo, p. ej. \"docs/pricing.md\". Es la identidad: el mismo valor reemplaza el documento anterior.",
            "maxLength": 500
          },
          "content": {
            "type": "string",
            "description": "El contenido del documento, en markdown."
          }
        },
        "required": [
          "collectionId",
          "filename",
          "content"
        ]
      },
      "DocumentoEmpujadoDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "El id del documento. Es lo que va en documentId para borrarlo."
          },
          "collectionId": {
            "type": "string"
          },
          "filename": {
            "type": "string",
            "description": "La ruta relativa que se mando. Junto con la coleccion, es la identidad del documento.",
            "examples": [
              "guias/precios-2026.md"
            ]
          },
          "status": {
            "type": "string",
            "description": "processing recien empujado: los trozos y sus vectores se hacen despues. ready cuando el agente ya lo puede citar. failed si la indexacion fallo; el motivo se lee en error al listar.",
            "enum": [
              "processing",
              "ready",
              "failed"
            ]
          }
        },
        "required": [
          "id",
          "collectionId",
          "filename",
          "status"
        ]
      },
      "CrearColeccionDto": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "description": "El identificador que eliges tú. Es la llave de idempotencia: dos llamadas con el mismo slug devuelven la misma coleccion.",
            "maxLength": 100,
            "examples": [
              "guias-precios"
            ]
          },
          "name": {
            "type": "string",
            "description": "Como se llama la coleccion en la pantalla.",
            "maxLength": 255,
            "examples": [
              "Guías de precios"
            ]
          },
          "description": {
            "type": "string",
            "description": "Para que sirve, en una linea. Se ve en la pantalla.",
            "maxLength": 1000
          }
        },
        "required": [
          "slug",
          "name"
        ]
      },
      "ColeccionCreadaDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "El id de la coleccion. Es lo que va en collectionId al empujar documentos y al asignarla a un agente."
          },
          "slug": {
            "type": "string",
            "examples": [
              "guias-precios"
            ]
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "slug",
          "name",
          "description",
          "createdAt"
        ]
      },
      "ColeccionDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "El id de la coleccion. Es lo que va en collectionId al empujar documentos y al asignarla a un agente."
          },
          "slug": {
            "type": "string",
            "examples": [
              "guias-precios"
            ]
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "documentCount": {
            "type": "number",
            "description": "Cuantos documentos vivos tiene adentro.",
            "examples": [
              12
            ]
          }
        },
        "required": [
          "id",
          "slug",
          "name",
          "description",
          "createdAt",
          "documentCount"
        ]
      },
      "PaginaDeColeccionesDto": {
        "type": "object",
        "properties": {
          "limit": {
            "type": "number",
            "description": "Cuantas filas se pidieron. Es el limit del pedido o 50 si no se mando.",
            "examples": [
              50
            ]
          },
          "offset": {
            "type": "number",
            "description": "Cuantas filas se saltearon. Es el offset del pedido o 0.",
            "examples": [
              0
            ]
          },
          "hasMore": {
            "type": "boolean",
            "description": "true si hay mas filas despues de esta pagina. Para seguir, repetir el pedido con offset + limit."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ColeccionDto"
            }
          }
        },
        "required": [
          "limit",
          "offset",
          "hasMore",
          "data"
        ]
      },
      "DocumentoDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "El id del documento. Es lo que va en documentId para borrarlo."
          },
          "collectionId": {
            "type": "string"
          },
          "filename": {
            "type": "string",
            "description": "La ruta relativa que se mando. Junto con la coleccion, es la identidad del documento.",
            "examples": [
              "guias/precios-2026.md"
            ]
          },
          "status": {
            "type": "string",
            "description": "processing recien empujado: los trozos y sus vectores se hacen despues. ready cuando el agente ya lo puede citar. failed si la indexacion fallo; el motivo se lee en error al listar.",
            "enum": [
              "processing",
              "ready",
              "failed"
            ]
          },
          "sizeBytes": {
            "type": "number",
            "description": "Peso del contenido en bytes.",
            "examples": [
              18432
            ]
          },
          "chunkCount": {
            "type": "number",
            "description": "En cuantos trozos quedo partido. 0 mientras esta en processing."
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "description": "Por que fallo la indexacion, cuando status es failed. null si no fallo."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "collectionId",
          "filename",
          "status",
          "sizeBytes",
          "chunkCount",
          "error",
          "createdAt",
          "updatedAt"
        ]
      },
      "PaginaDeDocumentosDto": {
        "type": "object",
        "properties": {
          "limit": {
            "type": "number",
            "description": "Cuantas filas se pidieron. Es el limit del pedido o 50 si no se mando.",
            "examples": [
              50
            ]
          },
          "offset": {
            "type": "number",
            "description": "Cuantas filas se saltearon. Es el offset del pedido o 0.",
            "examples": [
              0
            ]
          },
          "hasMore": {
            "type": "boolean",
            "description": "true si hay mas filas despues de esta pagina. Para seguir, repetir el pedido con offset + limit."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DocumentoDto"
            }
          }
        },
        "required": [
          "limit",
          "offset",
          "hasMore",
          "data"
        ]
      },
      "PlantillaDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "El id de la plantilla. Es lo que va en templateId."
          },
          "slug": {
            "type": "string",
            "examples": [
              "ventas-inmobiliaria"
            ]
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "category": {
            "type": "string",
            "description": "Para agrupar en la galeria.",
            "examples": [
              "ventas"
            ]
          },
          "knowledgeCollections": {
            "description": "Los slugs de las colecciones de conocimiento que el agente instanciado va a usar. Conviene tenerlas creadas antes de instanciar; las que falten salen en missing.",
            "type": "array",
            "items": {
              "type": "string"
            },
            "examples": [
              [
                "precios",
                "preguntas-frecuentes"
              ]
            ]
          }
        },
        "required": [
          "id",
          "slug",
          "name",
          "description",
          "category",
          "knowledgeCollections"
        ]
      },
      "PaginaDePlantillasDto": {
        "type": "object",
        "properties": {
          "limit": {
            "type": "number",
            "description": "Cuantas filas se pidieron. Es el limit del pedido o 50 si no se mando.",
            "examples": [
              50
            ]
          },
          "offset": {
            "type": "number",
            "description": "Cuantas filas se saltearon. Es el offset del pedido o 0.",
            "examples": [
              0
            ]
          },
          "hasMore": {
            "type": "boolean",
            "description": "true si hay mas filas despues de esta pagina. Para seguir, repetir el pedido con offset + limit."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PlantillaDto"
            }
          }
        },
        "required": [
          "limit",
          "offset",
          "hasMore",
          "data"
        ]
      },
      "InstanciarPlantillaDto": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "description": "Identificador del agente dentro de tu organizacion. Es la llave de idempotencia: reintentar con el mismo slug devuelve el mismo agente, no uno nuevo.",
            "examples": [
              "ventas"
            ]
          },
          "name": {
            "type": "string",
            "description": "Como se llama el agente. Por omision, el nombre de la plantilla."
          }
        },
        "required": [
          "slug"
        ]
      },
      "ConocimientoInstanciadoDto": {
        "type": "object",
        "properties": {
          "assigned": {
            "description": "Los slugs de las colecciones que quedaron asignadas al agente.",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "missing": {
            "description": "Los slugs que la plantilla pedia y la organizacion no tiene. Se crean con POST /knowledge/collections y se asignan con PUT /agents/{agentId}/knowledge/{collectionId}.",
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "assigned",
          "missing"
        ]
      },
      "AgenteInstanciadoDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "El id del agente. Es lo que va en agentId."
          },
          "slug": {
            "type": "string",
            "examples": [
              "ventas"
            ]
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "description": "Recien instanciado queda en draft; se activa con POST /agents/{agentId}/activate.",
            "enum": [
              "draft",
              "active",
              "paused",
              "archived"
            ]
          },
          "audience": {
            "type": "string",
            "enum": [
              "external",
              "internal"
            ]
          },
          "channels": {
            "description": "Los canales por los que atiende. Vacio significa todos.",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "knowledge": {
            "$ref": "#/components/schemas/ConocimientoInstanciadoDto"
          }
        },
        "required": [
          "id",
          "slug",
          "name",
          "status",
          "audience",
          "channels",
          "createdAt",
          "knowledge"
        ]
      },
      "SacarPlantillaDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Como se llama la plantilla."
          },
          "category": {
            "type": "string",
            "description": "En que parte del catalogo va. Por ejemplo sales o support."
          },
          "slug": {
            "type": "string",
            "description": "Identificador de la plantilla. Por omision se deriva del nombre."
          },
          "description": {
            "type": "string",
            "description": "Para que sirve esta plantilla."
          }
        },
        "required": [
          "name",
          "category"
        ]
      },
      "FaltaParaAlcanzarDto": {
        "type": "object",
        "properties": {
          "codigo": {
            "type": "string",
            "description": "Codigo estable para ramificar: sin_flujo (no esta en ningun flujo), no_es_la_etapa_de_entrada (esta en un flujo pero no lo alcanza una conversacion nueva), sin_canal_api (no tiene el canal api en channels).",
            "enum": [
              "sin_flujo",
              "no_es_la_etapa_de_entrada",
              "sin_canal_api"
            ]
          },
          "mensaje": {
            "type": "string",
            "description": "Que hacer al respecto, para una persona."
          }
        },
        "required": [
          "codigo",
          "mensaje"
        ]
      },
      "AgenteActivadoDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "El id del agente. Es lo que va en agentId."
          },
          "slug": {
            "type": "string",
            "examples": [
              "ventas"
            ]
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "description": "Recien instanciado queda en draft; se activa con POST /agents/{agentId}/activate.",
            "enum": [
              "draft",
              "active",
              "paused",
              "archived"
            ]
          },
          "audience": {
            "type": "string",
            "enum": [
              "external",
              "internal"
            ]
          },
          "channels": {
            "description": "Los canales por los que atiende. Vacio significa todos.",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "readiness": {
            "description": "Lo que todavia impide que alguien lo alcance. Vacio significa que ya lo alcanza cualquiera. Activar no falla por esto: informa.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FaltaParaAlcanzarDto"
            }
          }
        },
        "required": [
          "id",
          "slug",
          "name",
          "status",
          "audience",
          "channels",
          "createdAt",
          "readiness"
        ]
      },
      "AgenteAprovisionadoDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "El id del agente. Es lo que va en agentId."
          },
          "slug": {
            "type": "string",
            "examples": [
              "ventas"
            ]
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "description": "Recien instanciado queda en draft; se activa con POST /agents/{agentId}/activate.",
            "enum": [
              "draft",
              "active",
              "paused",
              "archived"
            ]
          },
          "audience": {
            "type": "string",
            "enum": [
              "external",
              "internal"
            ]
          },
          "channels": {
            "description": "Los canales por los que atiende. Vacio significa todos.",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "slug",
          "name",
          "status",
          "audience",
          "channels",
          "createdAt"
        ]
      },
      "AgenteResumenDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "El id del agente. Es lo que va en agentId."
          },
          "slug": {
            "type": "string",
            "description": "El identificador que se eligio al crearlo. Unico en la organizacion.",
            "examples": [
              "ventas"
            ]
          },
          "name": {
            "type": "string",
            "description": "Como se llama en el panel."
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Para que sirve, si se escribio."
          },
          "status": {
            "type": "string",
            "description": "draft recien creado; active atendiendo; paused no contesta; archived retirado.",
            "enum": [
              "draft",
              "active",
              "paused",
              "archived"
            ]
          },
          "audience": {
            "type": "string",
            "description": "A quien atiende. Por esta API solo se ven agentes external: los internal se administran desde el panel.",
            "enum": [
              "external",
              "internal"
            ]
          },
          "runtimeType": {
            "type": "string",
            "description": "text o voice.",
            "enum": [
              "text",
              "voice"
            ]
          },
          "channels": {
            "description": "Los canales por los que atiende. Vacio significa todos. Para atender por esta API tiene que incluir api.",
            "type": "array",
            "items": {
              "type": "string"
            },
            "examples": [
              [
                "whatsapp",
                "api"
              ]
            ]
          },
          "version": {
            "type": "number",
            "description": "Sube cuando se modifica el agente (PATCH, activar, pausar, dar de baja). Es lo que va en expectedVersion al escribir. No sube al conceder o quitar una accion ni al editar una accion o un servidor: para eso, updatedAt.",
            "examples": [
              3
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Ultimo cambio del agente, de las acciones que tiene concedidas o de lo que hace una de ellas y a donde sale (editar una accion, un servidor MCP o un conector mueve a todos los agentes que la tienen). No refleja las colecciones de conocimiento asignadas ni si un canal esta conectado."
          }
        },
        "required": [
          "id",
          "slug",
          "name",
          "description",
          "status",
          "audience",
          "runtimeType",
          "channels",
          "version",
          "createdAt",
          "updatedAt"
        ]
      },
      "PaginaDeAgentesDto": {
        "type": "object",
        "properties": {
          "limit": {
            "type": "number",
            "description": "Cuantas filas se pidieron. Es el limit del pedido o 50 si no se mando.",
            "examples": [
              50
            ]
          },
          "offset": {
            "type": "number",
            "description": "Cuantas filas se saltearon. Es el offset del pedido o 0.",
            "examples": [
              0
            ]
          },
          "hasMore": {
            "type": "boolean",
            "description": "true si hay mas filas despues de esta pagina. Para seguir, repetir el pedido con offset + limit."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AgenteResumenDto"
            }
          }
        },
        "required": [
          "limit",
          "offset",
          "hasMore",
          "data"
        ]
      },
      "ModeloDelAgenteDto": {
        "type": "object",
        "properties": {
          "provider": {
            "type": [
              "string",
              "null"
            ],
            "description": "El proveedor del modelo: gemini, gemini-live, bedrock, anthropic, deepseek o nova.",
            "examples": [
              "bedrock"
            ]
          },
          "model": {
            "type": [
              "string",
              "null"
            ],
            "description": "El identificador del modelo en ese proveedor."
          },
          "maxOutputTokens": {
            "type": [
              "number",
              "null"
            ],
            "description": "Cuántos tokens puede escribir el modelo en cada llamada. Sin valor, rige el mínimo del motor."
          },
          "effort": {
            "type": [
              "string",
              "null"
            ],
            "description": "Cuánto razona el modelo antes de contestar: low, medium, high, xhigh o max. Sin valor, rige el del modelo.",
            "examples": [
              "medium"
            ]
          },
          "alwaysLoadedTools": {
            "description": "Las herramientas que el modelo ve siempre. Las demás las encuentra buscando cuando las necesita. Sin valor, las ve todas.",
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "provider",
          "model",
          "maxOutputTokens",
          "effort",
          "alwaysLoadedTools"
        ]
      },
      "AccionDelAgenteDto": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "description": "El slug de la accion. Es lo que va al conceder o quitar."
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "description": "Lo que lee el agente para decidir cuando usarla."
          },
          "destinationOrigin": {
            "type": [
              "string",
              "null"
            ],
            "description": "A que tercero sale la accion: solo el origen (esquema, host y puerto), nunca el path, la query ni las cabeceras. Lo tienen las acciones HTTP de la organizacion, las de un servidor MCP (el origen del servidor) y las de un conector (el de su sitio). null si la accion es una herramienta nativa de Lelemon, que no tiene un destino configurado por la organizacion, o si ya no existe. \"(ilegible)\" si la accion sale a un destino pero su direccion no se pudo leer. Solo viene si la credencial tiene actions:read; sin ese alcance el campo no existe.",
            "examples": [
              "https://n8n.tuempresa.cl"
            ]
          }
        },
        "required": [
          "slug",
          "name",
          "description"
        ]
      },
      "CanalDelAgenteDto": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "description": "El slug del canal: whatsapp, web, api..."
          },
          "name": {
            "type": "string"
          },
          "connected": {
            "type": "boolean",
            "description": "true si el canal tiene una conexion activa."
          }
        },
        "required": [
          "slug",
          "name",
          "connected"
        ]
      },
      "ColeccionAsignadaDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "El id de la coleccion. Es lo que va en collectionId."
          },
          "name": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "name"
        ]
      },
      "AgenteDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "El id del agente. Es lo que va en agentId."
          },
          "slug": {
            "type": "string",
            "description": "El identificador que se eligio al crearlo. Unico en la organizacion.",
            "examples": [
              "ventas"
            ]
          },
          "name": {
            "type": "string",
            "description": "Como se llama en el panel."
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Para que sirve, si se escribio."
          },
          "status": {
            "type": "string",
            "description": "draft recien creado; active atendiendo; paused no contesta; archived retirado.",
            "enum": [
              "draft",
              "active",
              "paused",
              "archived"
            ]
          },
          "audience": {
            "type": "string",
            "description": "A quien atiende. Por esta API solo se ven agentes external: los internal se administran desde el panel.",
            "enum": [
              "external",
              "internal"
            ]
          },
          "runtimeType": {
            "type": "string",
            "description": "text o voice.",
            "enum": [
              "text",
              "voice"
            ]
          },
          "channels": {
            "description": "Los canales por los que atiende. Vacio significa todos. Para atender por esta API tiene que incluir api.",
            "type": "array",
            "items": {
              "type": "string"
            },
            "examples": [
              [
                "whatsapp",
                "api"
              ]
            ]
          },
          "version": {
            "type": "number",
            "description": "Sube cuando se modifica el agente (PATCH, activar, pausar, dar de baja). Es lo que va en expectedVersion al escribir. No sube al conceder o quitar una accion ni al editar una accion o un servidor: para eso, updatedAt.",
            "examples": [
              3
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "Ultimo cambio del agente, de las acciones que tiene concedidas o de lo que hace una de ellas y a donde sale (editar una accion, un servidor MCP o un conector mueve a todos los agentes que la tienen). No refleja las colecciones de conocimiento asignadas ni si un canal esta conectado."
          },
          "systemPrompt": {
            "type": "string",
            "description": "Las instrucciones del agente, enteras. Es el texto que se cambia con PATCH."
          },
          "model": {
            "$ref": "#/components/schemas/ModeloDelAgenteDto"
          },
          "maxIterations": {
            "type": "number",
            "description": "Cuantas vueltas puede dar el agente en un turno.",
            "examples": [
              10
            ]
          },
          "maxTokens": {
            "type": "number",
            "description": "Techo de tokens por turno.",
            "examples": [
              100000
            ]
          },
          "actions": {
            "description": "Las acciones que tiene concedidas hoy. Con actions:read, ademas el origen al que llama cada una; nunca la direccion entera.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AccionDelAgenteDto"
            }
          },
          "channelDetails": {
            "description": "Los canales de channels, con su nombre y si estan conectados.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CanalDelAgenteDto"
            }
          },
          "knowledge": {
            "description": "Las colecciones de conocimiento asignadas y habilitadas. Solo viene si la credencial ademas tiene knowledge:read; si no, el campo no esta.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ColeccionAsignadaDto"
            }
          }
        },
        "required": [
          "id",
          "slug",
          "name",
          "description",
          "status",
          "audience",
          "runtimeType",
          "channels",
          "version",
          "createdAt",
          "updatedAt",
          "systemPrompt",
          "model",
          "maxIterations",
          "maxTokens",
          "actions",
          "channelDetails"
        ]
      },
      "ActualizarAgenteDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Como se llama el agente."
          },
          "description": {
            "type": "string",
            "description": "Para que sirve."
          },
          "systemPrompt": {
            "type": "string",
            "description": "Las instrucciones del agente. Queda una version en el historial."
          },
          "llmConfig": {
            "type": "object",
            "description": "Con que modelo contesta: { primary: { provider, model }, fallback?: { provider, model } }."
          },
          "maxTokens": {
            "type": "number",
            "description": "Cuantos tokens puede gastar el agente en un turno, sumando entrada y salida de todas sus vueltas. Si se pasa, el turno se detiene.",
            "minimum": 1000,
            "maximum": 5000000
          },
          "expectedVersion": {
            "type": "number",
            "description": "La version del agente que leiste. Si alguien lo cambio en el medio, la respuesta es 409 y no se escribe nada."
          }
        }
      },
      "AgenteGestionadoDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "active",
              "paused",
              "archived"
            ]
          },
          "version": {
            "type": "number",
            "description": "La version nueva. Es lo que va en expectedVersion del proximo PATCH para no pisar un cambio ajeno.",
            "examples": [
              4
            ]
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "slug",
          "name",
          "description",
          "status",
          "version",
          "updatedAt"
        ]
      },
      "VersionDePromptDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "El id de la version. Es lo que va en versionId para restaurarla."
          },
          "version": {
            "type": "number",
            "description": "El numero de version del agente cuando se escribio.",
            "examples": [
              2
            ]
          },
          "chars": {
            "type": "number",
            "description": "Cuantos caracteres tiene el texto. El texto no viene: un prompt puede pesar cientos de KB.",
            "examples": [
              4821
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "version",
          "chars",
          "createdAt"
        ]
      },
      "HistorialDePromptsDto": {
        "type": "object",
        "properties": {
          "data": {
            "description": "De la mas nueva a la mas vieja. Hasta 50.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VersionDePromptDto"
            }
          }
        },
        "required": [
          "data"
        ]
      },
      "VersionRestauradaDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "El id de la version cuyo texto se copio."
          },
          "version": {
            "type": "number",
            "description": "Su numero de version.",
            "examples": [
              2
            ]
          }
        },
        "required": [
          "id",
          "version"
        ]
      },
      "AgenteRestauradoDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "active",
              "paused",
              "archived"
            ]
          },
          "version": {
            "type": "number",
            "description": "La version nueva. Es lo que va en expectedVersion del proximo PATCH para no pisar un cambio ajeno.",
            "examples": [
              4
            ]
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "restoredFrom": {
            "description": "De que version salio el texto. El agente queda con una version NUEVA que lo copia; la vieja sigue en el historial.",
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "description": "El id de la version cuyo texto se copio."
              },
              "version": {
                "type": "number",
                "description": "Su numero de version.",
                "examples": [
                  2
                ]
              }
            },
            "required": [
              "id",
              "version"
            ]
          }
        },
        "required": [
          "id",
          "slug",
          "name",
          "description",
          "status",
          "version",
          "updatedAt",
          "restoredFrom"
        ]
      },
      "TextoDeAutorizacionDto": {
        "type": "object",
        "properties": {
          "version": {
            "type": "string",
            "examples": [
              "2026-09-1"
            ]
          },
          "texto": {
            "type": "string",
            "description": "El texto que se acepta."
          },
          "detalle": {
            "type": "string",
            "description": "La explicacion larga de lo que ese texto implica."
          }
        },
        "required": [
          "version",
          "texto",
          "detalle"
        ]
      },
      "AutorizacionVigenteDto": {
        "type": "object",
        "properties": {
          "version": {
            "type": "string",
            "description": "La version vigente. Es lo que va en autorizacionVersion al registrar una accion. Va suelta ademas de dentro de vigente para que no haya que deducir cual de los dos textos manda.",
            "examples": [
              "2026-09-1"
            ]
          },
          "vigente": {
            "description": "La autorizacion de entrega del art. 15 bis de la Ley 21.719, que se acepta siempre.",
            "type": "object",
            "properties": {
              "version": {
                "type": "string",
                "examples": [
                  "2026-09-1"
                ]
              },
              "texto": {
                "type": "string",
                "description": "El texto que se acepta."
              },
              "detalle": {
                "type": "string",
                "description": "La explicacion larga de lo que ese texto implica."
              }
            },
            "required": [
              "version",
              "texto",
              "detalle"
            ]
          },
          "fueraDeChile": {
            "description": "La confirmacion extra cuando el destino no esta en Chile. Se acepta con confirmaSalidaDelPais: true.",
            "type": "object",
            "properties": {
              "version": {
                "type": "string",
                "examples": [
                  "2026-09-1"
                ]
              },
              "texto": {
                "type": "string",
                "description": "El texto que se acepta."
              },
              "detalle": {
                "type": "string",
                "description": "La explicacion larga de lo que ese texto implica."
              }
            },
            "required": [
              "version",
              "texto",
              "detalle"
            ]
          },
          "paises": {
            "type": "array",
            "description": "Los valores que acepta destinationCountry.",
            "items": {
              "type": "string",
              "enum": [
                "chile",
                "fuera_de_chile",
                "desconocido"
              ]
            }
          }
        },
        "required": [
          "version",
          "vigente",
          "fueraDeChile",
          "paises"
        ]
      },
      "VariableDeAccionDto": {
        "type": "object",
        "properties": {
          "nombre": {
            "type": "string",
            "description": "El nombre con que llega a tu sistema: letras sin tilde, numeros, \"_\" o \"-\", hasta 64. No puede repetirse ni llamarse como un parametro, sin distinguir mayusculas.",
            "pattern": "^[a-zA-Z0-9_-]{1,64}$",
            "examples": [
              "sesion_id"
            ]
          },
          "variable": {
            "type": "string",
            "enum": [
              "conversacion.id",
              "conversacion.canal",
              "contacto.telefono",
              "contacto.whatsapp_id",
              "agente.id",
              "organizacion.id"
            ],
            "description": "Que dato de Lelemon va. Ausente cuando origen es campo_del_caso. conversacion.id: el id de la conversacion; es otro cuando la anterior se cerro y la persona vuelve a escribir. conversacion.canal: por donde llego. agente.id: el agente que atiende. organizacion.id: tu organizacion. contacto.telefono: el numero desde el que escribe la persona, como lo manda WhatsApp; no va si la persona usa un nombre de usuario. contacto.whatsapp_id: el identificador de WhatsApp de la persona, que Meta manda aunque oculte su numero. Estos dos ultimos solo salen desde agentes que atienden clientes, en acciones POST, PUT, PATCH o DELETE, y con la autorizacion de destino vigente para esa URL (purpose, destinationCountry y autorizacionVersion); sin ella la accion no se guarda.",
            "examples": [
              "conversacion.id"
            ]
          },
          "origen": {
            "type": "string",
            "description": "campo_del_caso cuando el valor sale de un campo del caso (contacts.data), declarado en pipelines.settings.entityFields, en vez de un dato de Lelemon.",
            "examples": [
              "campo_del_caso"
            ]
          },
          "campo": {
            "type": "string",
            "description": "La clave del campo del caso tal como la declara el embudo. Obligatoria cuando origen es campo_del_caso; si el caso no la tiene guardada, la accion no se ejecuta.",
            "examples": [
              "rut_comprador"
            ]
          },
          "opcional": {
            "type": "boolean",
            "description": "Sólo con origen campo_del_caso. Si el campo está vacío en el caso, se omite del pedido en vez de bloquear la accion. Por defecto false.",
            "examples": [
              false
            ]
          }
        },
        "required": [
          "nombre"
        ]
      },
      "CrearAccionHttpDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Como se llama la accion."
          },
          "description": {
            "type": "string",
            "description": "Lo que lee el agente para decidir cuando usarla. Sin esto no la usa nunca."
          },
          "url": {
            "type": "string",
            "description": "A donde llama. No vuelve en ninguna respuesta: de la direccion solo se devuelve el origen.",
            "examples": [
              "https://api.tusistema.cl/tickets"
            ]
          },
          "method": {
            "type": "string",
            "description": "GET, POST, PUT, PATCH o DELETE."
          },
          "slug": {
            "type": "string",
            "description": "Identificador de la accion. Es con lo que se le concede a un agente, y es la llave para reintentar sin duplicar.",
            "examples": [
              "crear_ticket"
            ]
          },
          "timeout": {
            "type": "number",
            "description": "Milisegundos de espera."
          },
          "headers": {
            "type": "object",
            "description": "Nombre de cabecera a valor. Aca viaja tu credencial: se cifra antes de guardarla y no vuelve nunca en una respuesta."
          },
          "parameters": {
            "type": "object",
            "description": "JSON Schema de los parametros que el agente va a completar."
          },
          "variables": {
            "description": "Campos que llena Lelemon al ejecutar, no el agente: el agente no los ve y no los puede inventar. Van en la query si el metodo es GET y en el cuerpo si no. Si falta el dato (por ejemplo, una conversacion sin caso), el campo no va. El nombre no puede repetirse ni ser el de un parametro. Al volver a registrar la misma accion, omitirlas deja las que tenia; [] o null las saca.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VariableDeAccionDto"
            },
            "examples": [
              [
                {
                  "nombre": "sesion_id",
                  "variable": "conversacion.id"
                }
              ]
            ]
          },
          "purpose": {
            "type": "string",
            "description": "Para que es. Parte de la autorizacion de destino (art. 15 bis de la Ley 21.719): si mandas alguno de purpose, destinationCountry o autorizacionVersion, tienen que venir los tres.",
            "examples": [
              "Abrir tickets de soporte con lo que cuenta el cliente."
            ]
          },
          "destinationCountry": {
            "type": "string",
            "description": "Donde esta alojado ese sistema, declarado por ti: chile, fuera_de_chile o desconocido. \"No se\" cuenta como fuera. Parte de la autorizacion de destino.",
            "enum": [
              "chile",
              "fuera_de_chile",
              "desconocido"
            ]
          },
          "autorizacionVersion": {
            "type": "string",
            "description": "La version del texto de autorizacion que aceptas. Se lee en GET /v1/external/actions/authorization. Parte de la autorizacion de destino.",
            "examples": [
              "2026-09-1"
            ]
          },
          "confirmaSalidaDelPais": {
            "type": "boolean",
            "description": "La segunda confirmacion, obligatoria cuando el destino no esta en Chile: los datos salen del pais y eso es una transferencia internacional."
          },
          "paraClientes": {
            "type": "boolean",
            "description": "A que agentes se les puede conceder. Sin mandarlo, a cualquiera: a quien se la das se decide al concederla. true: solo a los que atienden clientes. false: solo a tu asistente interno."
          }
        },
        "required": [
          "name",
          "description",
          "url",
          "method",
          "slug"
        ]
      },
      "VariableGuardadaDto": {
        "type": "object",
        "properties": {
          "nombre": {
            "type": "string",
            "description": "El nombre con que llega a tu sistema.",
            "examples": [
              "sesion_id"
            ]
          },
          "variable": {
            "type": "string",
            "description": "Que dato de Lelemon va: conversacion.id o conversacion.canal, por ejemplo. Ausente cuando origen es campo_del_caso. Una clave que ya no existe se ignora al ejecutar.",
            "examples": [
              "conversacion.id"
            ]
          },
          "origen": {
            "type": "string",
            "description": "campo_del_caso cuando el valor sale de un campo del caso en vez de un dato de Lelemon.",
            "examples": [
              "campo_del_caso"
            ]
          },
          "campo": {
            "type": "string",
            "description": "La clave del campo del caso, tal como la declara el embudo. Sólo cuando origen es campo_del_caso.",
            "examples": [
              "rut_comprador"
            ]
          },
          "opcional": {
            "type": "boolean",
            "description": "Sólo cuando origen es campo_del_caso. Si está vacío en el caso, se omite del pedido en vez de bloquear la acción.",
            "examples": [
              false
            ]
          }
        },
        "required": [
          "nombre"
        ]
      },
      "AccionDto": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "description": "El identificador de la accion. Es lo que va al concederla a un agente y al darla de baja.",
            "examples": [
              "crear_ticket"
            ]
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "description": "Lo que lee el agente para decidir cuando usarla."
          },
          "method": {
            "type": "string",
            "enum": [
              "GET",
              "POST",
              "PUT",
              "PATCH",
              "DELETE"
            ]
          },
          "origin": {
            "type": "string",
            "description": "Solo el origen de la direccion registrada (esquema, host y puerto). La direccion completa no vuelve nunca: en muchos webhooks el path es la credencial.",
            "examples": [
              "https://api.tusistema.cl"
            ]
          },
          "headerNames": {
            "description": "Los nombres de las cabeceras configuradas. Los valores no vuelven nunca.",
            "type": "array",
            "items": {
              "type": "string"
            },
            "examples": [
              [
                "Authorization"
              ]
            ]
          },
          "parameters": {
            "type": "object",
            "additionalProperties": true,
            "description": "El JSON Schema de los parametros que el agente completa al usarla."
          },
          "variables": {
            "description": "Los campos que Lelemon agrega en cada llamada. Vacia si no tiene.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VariableGuardadaDto"
            }
          },
          "audience": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "external",
              "internal",
              null
            ],
            "description": "A que clase de agente se le puede conceder. null significa a cualquiera, y es lo que queda si no mandaste paraClientes al registrarla: a quien se la das se decide al concederla."
          },
          "status": {
            "type": "string",
            "description": "active si esta disponible; inactive si se dio de baja.",
            "examples": [
              "active"
            ]
          }
        },
        "required": [
          "slug",
          "name",
          "description",
          "method",
          "origin",
          "headerNames",
          "parameters",
          "variables",
          "audience",
          "status"
        ]
      },
      "ListaDeAccionesDto": {
        "type": "object",
        "properties": {
          "data": {
            "description": "Las acciones HTTP activas de la organizacion.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AccionDto"
            }
          }
        },
        "required": [
          "data"
        ]
      },
      "AccionDelCatalogoDto": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "description": "El slug de la accion. Es lo que va en PUT y DELETE /agents/{agentId}/actions/{slug}."
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "source": {
            "type": "string",
            "description": "De donde sale: \"Tus sistemas\" para las acciones HTTP que registraste, \"Lelemon\" para las que traemos nosotros, o el nombre del servidor MCP conectado que la anuncia.",
            "examples": [
              "Tus sistemas"
            ]
          },
          "enabled": {
            "type": "boolean",
            "description": "true si el agente la tiene concedida hoy."
          },
          "destinationOrigin": {
            "type": [
              "string",
              "null"
            ],
            "description": "A que tercero sale la accion: solo el origen (esquema, host y puerto), nunca el path, la query ni las cabeceras. Lo tienen las acciones HTTP de la organizacion, las de un servidor MCP (el origen del servidor) y las de un conector (el de su sitio). null si la accion es una herramienta nativa de Lelemon, que no tiene un destino configurado por la organizacion, o si ya no existe. \"(ilegible)\" si la accion sale a un destino pero su direccion no se pudo leer. Solo viene si la credencial tiene actions:read; sin ese alcance el campo no existe.",
            "examples": [
              "https://n8n.tuempresa.cl"
            ]
          }
        },
        "required": [
          "slug",
          "name",
          "description",
          "source",
          "enabled"
        ]
      },
      "CatalogoDeAccionesDelAgenteDto": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AccionDelCatalogoDto"
            }
          }
        },
        "required": [
          "data"
        ]
      },
      "ReportarHechoDto": {
        "type": "object",
        "properties": {
          "evento": {
            "type": "string",
            "description": "Que paso, con el nombre que uses en tu sistema. Es el mismo nombre que declara el trigger que lo escucha.",
            "examples": [
              "primera_empresa_conectada"
            ]
          },
          "externalId": {
            "type": "string",
            "description": "El identificador del usuario final EN TU SISTEMA. Es el mismo con el que se abre una conversacion, asi que la persona es la misma para las dos rutas.",
            "examples": [
              "usuario-1234"
            ]
          },
          "datos": {
            "type": "object",
            "description": "Contexto del hecho. Hoy no se guarda ni cambia la decision; se acepta para no romper tu llamada cuando los triggers miren condiciones."
          }
        },
        "required": [
          "evento",
          "externalId"
        ]
      },
      "ResultadoDeTriggerDto": {
        "type": "object",
        "properties": {
          "triggerId": {
            "type": "string",
            "description": "El trigger que escuchaba este hecho."
          },
          "estado": {
            "type": "string",
            "description": "disparo: el agente le escribio. ya_estaba: a esa persona este trigger ya le habia llegado, y no se le escribio de nuevo. control_humano: alguien de tu equipo tiene la conversacion y no se la piso. error: este trigger no pudo despachar (por ejemplo su agente esta pausado) y los demas siguieron igual.",
            "enum": [
              "disparo",
              "ya_estaba",
              "control_humano",
              "error"
            ]
          }
        },
        "required": [
          "triggerId",
          "estado"
        ]
      },
      "HechoReportadoDto": {
        "type": "object",
        "properties": {
          "triggers": {
            "description": "Una entrada por trigger que escuchaba el hecho. Vacio si ninguno lo escuchaba.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ResultadoDeTriggerDto"
            }
          },
          "duplicado": {
            "type": "boolean",
            "description": "Verdadero cuando no se escribio nada nuevo y algo ya le habia llegado a esa persona. Con dos triggers donde uno disparo es falso: hubo mensaje. Sirve para medir reintentos sin mirar el codigo HTTP."
          },
          "motivo": {
            "type": "string",
            "description": "Por que no se escribio nada. Solo viaja cuando no se escribio NI se habia escrito antes: sin trigger que escuche el hecho, la conversacion en manos de tu equipo, o ningun trigger pudo despachar.",
            "enum": [
              "sin trigger",
              "control humano",
              "error"
            ]
          }
        },
        "required": [
          "triggers",
          "duplicado"
        ]
      },
      "CrearTriggerDto": {
        "type": "object",
        "properties": {
          "agentId": {
            "type": "string",
            "description": "Que agente escribe. Tiene que ser de tu organizacion, estar activo, y ser de los que la API administra."
          },
          "evento": {
            "type": "string",
            "description": "El hecho que despierta este trigger, con el nombre que uses en tu sistema. Es el mismo que mandas a POST /v1/external/events.",
            "examples": [
              "primera_empresa_conectada"
            ]
          },
          "texto": {
            "type": "string",
            "description": "Lo que el agente dice."
          },
          "abrirPanel": {
            "type": "boolean",
            "description": "Si tu aplicacion deberia traer la conversacion al frente al recibir el aviso. Viaja tal cual en el webhook; la decision es tuya.",
            "default": false
          },
          "activo": {
            "type": "boolean",
            "description": "Nace activo. Manda false para crearlo pausado.",
            "default": true
          }
        },
        "required": [
          "agentId",
          "evento",
          "texto"
        ]
      },
      "TriggerDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "agentId": {
            "type": "string",
            "description": "El agente que escribe."
          },
          "evento": {
            "type": "string",
            "description": "El hecho que lo despierta.",
            "examples": [
              "primera_empresa_conectada"
            ]
          },
          "texto": {
            "type": "string",
            "description": "Lo que el agente dice."
          },
          "abrirPanel": {
            "type": "boolean",
            "description": "Si tu aplicacion deberia traer la conversacion al frente. Viaja tal cual en el aviso saliente."
          },
          "activo": {
            "type": "boolean",
            "description": "Un trigger pausado no dispara."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "agentId",
          "evento",
          "texto",
          "abrirPanel",
          "activo",
          "createdAt",
          "updatedAt"
        ]
      },
      "TriggersDto": {
        "type": "object",
        "properties": {
          "triggers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TriggerDto"
            }
          }
        },
        "required": [
          "triggers"
        ]
      },
      "EditarTriggerDto": {
        "type": "object",
        "properties": {
          "texto": {
            "type": "string"
          },
          "abrirPanel": {
            "type": "boolean"
          },
          "activo": {
            "type": "boolean",
            "description": "Un trigger pausado no dispara. Pausarlo no borra las entregas: al prenderlo, a quien ya le llego no le vuelve a llegar."
          }
        }
      },
      "PedidoPorDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "El identificador del usuario en tu sistema a nombre de quien corre la corrida.",
            "maxLength": 200,
            "examples": [
              "pc-user-42"
            ]
          },
          "vence": {
            "type": "number",
            "description": "Hasta cuando vale la firma, en segundos Unix (UTC). Tiene que estar en el futuro y a no mas de 300 segundos.",
            "examples": [
              1789132000
            ]
          },
          "firma": {
            "type": "string",
            "description": "HMAC-SHA256 en hex minuscula (64 caracteres) sobre el texto \"lelemon-run:v1:<id>:<vence>\", con el secreto de servicio que tu sistema comparte con Lelemon como llave.",
            "examples": [
              "3b1f0c4e8d2a7f6b5c9e0a1d4f7b2c8e6a3d9f0b1c5e7a2d4f6b8c0e2a4d6f81"
            ]
          }
        },
        "required": [
          "id",
          "vence",
          "firma"
        ]
      },
      "TopesPedidosDto": {
        "type": "object",
        "properties": {
          "maxVueltas": {
            "type": "number",
            "description": "Cuantas vueltas puede dar. Maximo 60; por defecto 30."
          },
          "maxCostoUsd": {
            "type": "number",
            "description": "Cuanto puede gastar, en dolares. Maximo 10; por defecto 2."
          },
          "maxMinutos": {
            "type": "number",
            "description": "Cuantos minutos puede durar desde que empieza. Maximo 120; por defecto 30."
          }
        }
      },
      "CrearCorridaDto": {
        "type": "object",
        "properties": {
          "meta": {
            "type": "string",
            "description": "Que tiene que lograr el agente. Se lo trata como texto de un usuario.",
            "maxLength": 8000,
            "examples": [
              "Concilia la cartola de agosto de la empresa 76.123.456-7 y dime que quedo pendiente."
            ]
          },
          "pedidoPor": {
            "description": "A nombre de quien corre, firmado por tu sistema. Sin este campo: 400. Con la firma invalida o vencida: 401.",
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "description": "El identificador del usuario en tu sistema a nombre de quien corre la corrida.",
                "maxLength": 200,
                "examples": [
                  "pc-user-42"
                ]
              },
              "vence": {
                "type": "number",
                "description": "Hasta cuando vale la firma, en segundos Unix (UTC). Tiene que estar en el futuro y a no mas de 300 segundos.",
                "examples": [
                  1789132000
                ]
              },
              "firma": {
                "type": "string",
                "description": "HMAC-SHA256 en hex minuscula (64 caracteres) sobre el texto \"lelemon-run:v1:<id>:<vence>\", con el secreto de servicio que tu sistema comparte con Lelemon como llave.",
                "examples": [
                  "3b1f0c4e8d2a7f6b5c9e0a1d4f7b2c8e6a3d9f0b1c5e7a2d4f6b8c0e2a4d6f81"
                ]
              }
            },
            "required": [
              "id",
              "vence",
              "firma"
            ]
          },
          "topes": {
            "$ref": "#/components/schemas/TopesPedidosDto"
          }
        },
        "required": [
          "meta",
          "pedidoPor"
        ]
      },
      "TopesDeLaCorridaDto": {
        "type": "object",
        "properties": {
          "maxVueltas": {
            "type": "number"
          },
          "maxCostoUsd": {
            "type": "number",
            "description": "En dolares."
          },
          "maxMinutos": {
            "type": "number"
          }
        },
        "required": [
          "maxVueltas",
          "maxCostoUsd",
          "maxMinutos"
        ]
      },
      "ResultadoDeLaCorridaDto": {
        "type": "object",
        "properties": {
          "resumen": {
            "type": "string",
            "description": "Lo que el agente declaro al terminar. Es una declaracion: nadie la comprobo todavia."
          },
          "comprobaciones": {
            "description": "Lo que habria que mirar para confirmar que esta hecho, segun el agente.",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "parcial": {
            "type": "string",
            "description": "Lo ultimo que escribio el agente. Viene tambien cuando se detuvo o fallo."
          },
          "error": {
            "type": "string",
            "description": "Por que fallo. Solo con estado fallida."
          }
        }
      },
      "CorridaDto": {
        "type": "object",
        "properties": {
          "runId": {
            "type": "string",
            "description": "El id de la corrida. Es lo que va en la URL para seguirla."
          },
          "agentSlug": {
            "type": "string",
            "examples": [
              "soporte"
            ]
          },
          "estado": {
            "type": "string",
            "enum": [
              "en_curso",
              "esperando_al_humano",
              "terminada_sin_verificar",
              "terminada",
              "detenida",
              "fallida",
              "cancelada"
            ],
            "description": "en_curso: trabajando o en la cola. esperando_al_humano: pauso para preguntar; pregunta trae la pregunta, respondela con POST /runs/{id}/answer. terminada_sin_verificar: el agente dijo que termino y todavia nadie lo comprobo; no la leas como hecha. terminada: la comprobo un verificador (hoy no ocurre). detenida: se acabo un limite; razon dice cual. fallida: error; resultado.error dice cual, o razon \"abandonada\" si nadie contesto la pausa en 30 dias. cancelada: la cancelaste."
          },
          "razon": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "vueltas",
              "costo",
              "tiempo",
              "no_supo_terminar",
              "ciclo",
              "abandonada",
              null
            ],
            "description": "Con estado detenida, que limite la freno. Con estado fallida y razon \"abandonada\", nadie contesto la pausa en 30 dias."
          },
          "pregunta": {
            "type": [
              "string",
              "null"
            ],
            "description": "Con estado esperando_al_humano: lo que el agente pregunto. Respondelo con POST /runs/{id}/answer."
          },
          "vueltas": {
            "type": "number",
            "description": "Vueltas que lleva."
          },
          "costoUsd": {
            "type": "number",
            "description": "Lo que lleva gastado, en dolares."
          },
          "topes": {
            "$ref": "#/components/schemas/TopesDeLaCorridaDto"
          },
          "resultado": {
            "nullable": true,
            "type": "object",
            "properties": {
              "resumen": {
                "type": "string",
                "description": "Lo que el agente declaro al terminar. Es una declaracion: nadie la comprobo todavia."
              },
              "comprobaciones": {
                "description": "Lo que habria que mirar para confirmar que esta hecho, segun el agente.",
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "parcial": {
                "type": "string",
                "description": "Lo ultimo que escribio el agente. Viene tambien cuando se detuvo o fallo."
              },
              "error": {
                "type": "string",
                "description": "Por que fallo. Solo con estado fallida."
              }
            }
          },
          "cancelacionPedida": {
            "type": "boolean",
            "description": "true si pediste cancelarla. Mientras un worker la corre, se cancela al empezar su vuelta siguiente."
          },
          "creadaEn": {
            "type": "string",
            "format": "date-time"
          },
          "iniciadaEn": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "terminadaEn": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "required": [
          "runId",
          "agentSlug",
          "estado",
          "razon",
          "pregunta",
          "vueltas",
          "costoUsd",
          "topes",
          "resultado",
          "cancelacionPedida",
          "creadaEn",
          "iniciadaEn",
          "terminadaEn"
        ]
      },
      "ResponderCorridaDto": {
        "type": "object",
        "properties": {
          "respuesta": {
            "type": "string",
            "description": "La respuesta a lo que pregunto el agente.",
            "maxLength": 4000
          },
          "respondidoPor": {
            "description": "Quien responde, firmado por tu sistema. Sin este campo: 400. Con la firma invalida o vencida: 401.",
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "description": "El identificador del usuario en tu sistema a nombre de quien corre la corrida.",
                "maxLength": 200,
                "examples": [
                  "pc-user-42"
                ]
              },
              "vence": {
                "type": "number",
                "description": "Hasta cuando vale la firma, en segundos Unix (UTC). Tiene que estar en el futuro y a no mas de 300 segundos.",
                "examples": [
                  1789132000
                ]
              },
              "firma": {
                "type": "string",
                "description": "HMAC-SHA256 en hex minuscula (64 caracteres) sobre el texto \"lelemon-run:v1:<id>:<vence>\", con el secreto de servicio que tu sistema comparte con Lelemon como llave.",
                "examples": [
                  "3b1f0c4e8d2a7f6b5c9e0a1d4f7b2c8e6a3d9f0b1c5e7a2d4f6b8c0e2a4d6f81"
                ]
              }
            },
            "required": [
              "id",
              "vence",
              "firma"
            ]
          }
        },
        "required": [
          "respuesta",
          "respondidoPor"
        ]
      },
      "AgendaDto": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "description": "Cómo se nombra esta agenda en las otras rutas.",
            "examples": [
              "fundador"
            ]
          },
          "name": {
            "type": "string",
            "description": "Su nombre, para mostrarlo.",
            "examples": [
              "Con el fundador"
            ]
          },
          "timezone": {
            "type": "string",
            "description": "El huso en el que la organización definió sus horarios.",
            "examples": [
              "America/Santiago"
            ]
          },
          "slotDurationMinutes": {
            "type": "number",
            "description": "Cuánto dura cada bloque, en minutos.",
            "examples": [
              20
            ]
          }
        },
        "required": [
          "slug",
          "name",
          "timezone",
          "slotDurationMinutes"
        ]
      },
      "AgendasDto": {
        "type": "object",
        "properties": {
          "agendas": {
            "description": "Las agendas activas de la organización. Vacío significa que no hay ninguna configurada todavía, no que algo haya fallado.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AgendaDto"
            }
          }
        },
        "required": [
          "agendas"
        ]
      },
      "BloqueLibreDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Identificador del bloque. Es lo que se manda de vuelta para reservarlo.",
            "examples": [
              "2026-09-17T14:00:00.000Z"
            ]
          },
          "start": {
            "type": "string",
            "description": "Cuándo empieza, en ISO 8601 con zona.",
            "examples": [
              "2026-09-17T14:00:00.000Z"
            ]
          },
          "end": {
            "type": "string",
            "description": "Cuándo termina, en ISO 8601 con zona.",
            "examples": [
              "2026-09-17T14:20:00.000Z"
            ]
          }
        },
        "required": [
          "id",
          "start",
          "end"
        ]
      },
      "DisponibilidadDto": {
        "type": "object",
        "properties": {
          "slots": {
            "description": "Los bloques libres, ordenados de más cercano a más lejano. Vacío significa que no hay ninguno en la ventana pedida, no que algo haya fallado.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BloqueLibreDto"
            }
          }
        },
        "required": [
          "slots"
        ]
      },
      "ReservarBloqueDto": {
        "type": "object",
        "properties": {
          "agenda": {
            "type": "string",
            "description": "En qué agenda reservar, por su slug. El mismo con el que pediste los horarios.",
            "examples": [
              "fundador"
            ]
          },
          "slotStart": {
            "type": "string",
            "description": "Cuándo empieza, en ISO 8601 con zona. Es el id de un bloque devuelto por la disponibilidad.",
            "examples": [
              "2026-09-17T14:00:00.000Z"
            ]
          },
          "attendeeEmail": {
            "type": "string",
            "description": "Correo de quien agenda. Ahí le llega la invitación.",
            "examples": [
              "persona@ejemplo.cl"
            ]
          },
          "attendeeName": {
            "type": "string",
            "description": "Nombre de quien agenda, como aparece en la invitación.",
            "examples": [
              "Ana Pérez"
            ]
          },
          "attendeePhone": {
            "type": "string",
            "description": "Teléfono de contacto, si lo tienes."
          },
          "companyName": {
            "type": "string",
            "description": "Empresa de quien agenda. Va en la descripción del evento."
          },
          "context": {
            "type": "string",
            "description": "Para qué es la reunión. Va en la descripción del evento, para que quien atienda llegue sabiendo."
          }
        },
        "required": [
          "agenda",
          "slotStart",
          "attendeeEmail",
          "attendeeName"
        ]
      },
      "ReunionAgendadaDto": {
        "type": "object",
        "properties": {
          "meetingId": {
            "type": "string",
            "description": "El id de la reunión. Es lo que se guarda para referirse a ella."
          },
          "scheduledFor": {
            "type": "string",
            "description": "Cuándo quedó, en ISO 8601 con zona.",
            "examples": [
              "2026-09-17T14:00:00.000Z"
            ]
          },
          "durationMinutes": {
            "type": "number",
            "description": "Cuánto dura, en minutos.",
            "examples": [
              20
            ]
          },
          "meetLink": {
            "type": "string",
            "description": "El link de la videollamada. Puede no venir si la agenda está configurada sin videollamada.",
            "examples": [
              "https://meet.google.com/abc-defg-hij"
            ]
          }
        },
        "required": [
          "meetingId",
          "scheduledFor",
          "durationMinutes"
        ]
      },
      "EmbudoResumenDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "El id del embudo. Es lo que va en pipelineId."
          },
          "name": {
            "type": "string",
            "description": "Como se llama en el panel."
          },
          "status": {
            "type": "string",
            "description": "draft recien creado; active en uso; paused pausado; archived retirado.",
            "enum": [
              "draft",
              "active",
              "paused",
              "archived"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "name",
          "status",
          "createdAt"
        ]
      },
      "PaginaDeEmbudosDto": {
        "type": "object",
        "properties": {
          "limit": {
            "type": "number",
            "description": "Cuantas filas se pidieron. Es el limit del pedido o 50 si no se mando.",
            "examples": [
              50
            ]
          },
          "offset": {
            "type": "number",
            "description": "Cuantas filas se saltearon. Es el offset del pedido o 0.",
            "examples": [
              0
            ]
          },
          "hasMore": {
            "type": "boolean",
            "description": "true si hay mas filas despues de esta pagina. Para seguir, repetir el pedido con offset + limit."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EmbudoResumenDto"
            }
          }
        },
        "required": [
          "limit",
          "offset",
          "hasMore",
          "data"
        ]
      },
      "EtapaDelEmbudoDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "El id de la etapa."
          },
          "name": {
            "type": "string",
            "description": "Como se llama en el panel."
          },
          "orderIndex": {
            "type": "number",
            "description": "El orden en el que aparece en el embudo."
          },
          "isEntry": {
            "type": "boolean",
            "description": "true si es la etapa de entrada."
          },
          "isTerminal": {
            "type": "boolean",
            "description": "true si cierra el caso al llegar ahi."
          },
          "statusType": {
            "type": [
              "string",
              "null"
            ],
            "description": "Que significa la etapa en el embudo: todo por hacer, in_progress en curso, done terminado. null si el embudo no lo declara. Es descriptivo: no cierra ni mueve el caso.",
            "enum": [
              "todo",
              "in_progress",
              "done",
              null
            ]
          },
          "camposObligatorios": {
            "description": "Claves de los campos del embudo (fields[].key) que el caso tiene que tener completos para entrar a esta etapa. Lista vacia si la etapa no exige nada. Un movimiento hecho por una persona, por la API o por el agente se rechaza con 422 mientras falte alguno; el cierre automatico y las transiciones del flujo no se bloquean.",
            "type": "array",
            "items": {
              "type": "string"
            },
            "examples": [
              [
                "rut",
                "email"
              ]
            ]
          }
        },
        "required": [
          "id",
          "name",
          "orderIndex",
          "isEntry",
          "isTerminal",
          "camposObligatorios"
        ]
      },
      "OpcionDelCampoDto": {
        "type": "object",
        "properties": {
          "value": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "whenParent": {
            "description": "Solo en un campo con dependsOn: los valores del campo padre para los que esta opcion existe. Ausente quiere decir que vale para cualquier valor del padre.",
            "type": "array",
            "items": {
              "type": "string"
            },
            "examples": [
              [
                "metropolitana"
              ]
            ]
          }
        },
        "required": [
          "value",
          "label"
        ]
      },
      "CampoDelEmbudoDto": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "La clave del campo, tal como se guarda en el contacto."
          },
          "label": {
            "type": "string",
            "description": "Como se muestra en el panel."
          },
          "type": {
            "type": "string",
            "description": "El tipo del campo.",
            "enum": [
              "text",
              "email",
              "phone",
              "url",
              "textarea",
              "file",
              "select"
            ]
          },
          "required": {
            "type": "boolean",
            "description": "true si hace falta para avanzar."
          },
          "options": {
            "description": "Las opciones, si el tipo es select.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OpcionDelCampoDto"
            }
          },
          "dependsOn": {
            "type": "string",
            "description": "La clave del campo del que dependen las opciones de este. Ausente quiere decir que las opciones son fijas. Solo valen las opciones cuyo whenParent nombra el valor que ese campo tiene en el caso, mas las que no traen whenParent; un valor fuera de ahi se rechaza al escribir.",
            "examples": [
              "region"
            ]
          },
          "sensitive": {
            "type": "boolean",
            "description": "true si es un dato sensible. No sale con su valor en GET /conversations/{id}."
          }
        },
        "required": [
          "key",
          "label",
          "type",
          "sensitive"
        ]
      },
      "EmbudoDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "El id del embudo. Es lo que va en pipelineId."
          },
          "name": {
            "type": "string",
            "description": "Como se llama en el panel."
          },
          "status": {
            "type": "string",
            "description": "draft recien creado; active en uso; paused pausado; archived retirado.",
            "enum": [
              "draft",
              "active",
              "paused",
              "archived"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "stages": {
            "description": "Las etapas vivas, ordenadas.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EtapaDelEmbudoDto"
            }
          },
          "fields": {
            "description": "Los campos que declara el embudo. Son definiciones, sin valores.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CampoDelEmbudoDto"
            }
          }
        },
        "required": [
          "id",
          "name",
          "status",
          "createdAt",
          "stages",
          "fields"
        ]
      },
      "OpcionDto": {
        "type": "object",
        "properties": {
          "value": {
            "type": "string",
            "description": "Lo que se guarda en el caso. Unico dentro del campo, sin distinguir mayusculas ni espacios al borde.",
            "maxLength": 80
          },
          "label": {
            "type": "string",
            "description": "Lo que se muestra. Si no viene, vale lo mismo que value. Al escribir un caso se acepta cualquiera de los dos y se guarda el value.",
            "maxLength": 80
          },
          "whenParent": {
            "description": "Solo en un campo con dependsOn: los valores del campo padre para los que esta opcion existe. Si no viene, la opcion vale para cualquier valor del padre.",
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "value"
        ]
      },
      "CampoConOpcionesDto": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "La clave del campo, tal como la devuelve GET /v1/external/pipelines/{id}. Tiene que existir en el embudo y ser de tipo lista."
          },
          "options": {
            "description": "Las opciones que el campo queda teniendo. Reemplazan por completo a las que tenia. Una lista vacia lo deja sin opciones.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OpcionDto"
            }
          }
        },
        "required": [
          "key",
          "options"
        ]
      },
      "ReemplazarOpcionesDto": {
        "type": "object",
        "properties": {
          "fields": {
            "description": "Los campos que se reemplazan. Los que no se nombran quedan como estaban. Padre e hijo de una cascada se mandan en el mismo pedido: se validan juntos.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CampoConOpcionesDto"
            }
          }
        },
        "required": [
          "fields"
        ]
      },
      "CampoReemplazadoDto": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "La clave del campo."
          },
          "options": {
            "type": "number",
            "description": "Cuantas opciones quedo teniendo."
          }
        },
        "required": [
          "key",
          "options"
        ]
      },
      "OpcionesReemplazadasDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "El embudo que se escribio."
          },
          "changed": {
            "type": "boolean",
            "description": "false cuando lo que llego era igual a lo que ya habia: no se escribio nada. Repetir el mismo pedido devuelve false."
          },
          "fields": {
            "description": "Como quedo cada campo nombrado en el pedido.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CampoReemplazadoDto"
            }
          }
        },
        "required": [
          "id",
          "changed",
          "fields"
        ]
      },
      "RespuestaRapidaGuardadaDto": {
        "type": "object",
        "properties": {
          "atajo": {
            "type": "string",
            "description": "El atajo, sin la barra.",
            "examples": [
              "saludo"
            ]
          },
          "texto": {
            "type": "string",
            "description": "El texto tal como se guardo, con {agente} y {cliente} sin reemplazar: se reemplazan al insertarla en el chat."
          }
        },
        "required": [
          "atajo",
          "texto"
        ]
      },
      "RespuestasRapidasDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "El embudo."
          },
          "respuestas": {
            "description": "En el orden en que el panel las muestra. Vacia si el embudo no tiene.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RespuestaRapidaGuardadaDto"
            }
          }
        },
        "required": [
          "id",
          "respuestas"
        ]
      },
      "RespuestaRapidaDto": {
        "type": "object",
        "properties": {
          "atajo": {
            "type": "string",
            "description": "Lo que el equipo escribe despues de la barra para insertarla: /saludo es el atajo saludo. De 1 a 30 letras minusculas, numeros, - o _, sin la barra. Unico en el embudo.",
            "maxLength": 30,
            "examples": [
              "saludo"
            ]
          },
          "texto": {
            "type": "string",
            "description": "El texto que se inserta en el chat, hasta 4096 caracteres. {agente} se reemplaza por el nombre de la persona del equipo que la inserta y {cliente} por el nombre del contacto; si falta el dato se deja vacio, sin dejar la llave ni espacios de sobra. Lo demas se inserta tal cual.",
            "maxLength": 4096,
            "examples": [
              "Hola {cliente}, soy {agente}. En que te ayudo?"
            ]
          }
        },
        "required": [
          "atajo",
          "texto"
        ]
      },
      "ReemplazarRespuestasRapidasDto": {
        "type": "object",
        "properties": {
          "respuestas": {
            "description": "Las respuestas rapidas que el embudo queda teniendo, hasta 50. Reemplazan por completo a las que tenia. Una lista vacia deja el embudo sin respuestas. Se validan igual que en el panel.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RespuestaRapidaDto"
            }
          }
        },
        "required": [
          "respuestas"
        ]
      },
      "RespuestasRapidasReemplazadasDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "El embudo."
          },
          "respuestas": {
            "description": "En el orden en que el panel las muestra. Vacia si el embudo no tiene.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RespuestaRapidaGuardadaDto"
            }
          },
          "changed": {
            "type": "boolean",
            "description": "false cuando lo que llego era igual a lo que ya habia: no se escribio nada. Repetir el mismo pedido devuelve false."
          }
        },
        "required": [
          "id",
          "respuestas",
          "changed"
        ]
      },
      "CrearSkillDto": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "description": "El identificador que eliges tú. Es lo que el agente nombra al cargarla, y la llave de idempotencia: dos llamadas con el mismo slug devuelven la misma skill.",
            "maxLength": 100,
            "examples": [
              "cerrar-el-mes"
            ]
          },
          "nombre": {
            "type": "string",
            "description": "Como se llama la skill.",
            "maxLength": 255,
            "examples": [
              "Cerrar el mes"
            ]
          },
          "descripcion": {
            "type": "string",
            "description": "Para que sirve, en una linea. Es lo unico de la skill que viaja en el prompt del agente, en cada mensaje: por eso es corta y dice CUANDO usarla, no como.",
            "maxLength": 500,
            "examples": [
              "Los pasos del cierre mensual de una empresa."
            ]
          },
          "contenido": {
            "type": "string",
            "description": "El instructivo completo, en texto. No viaja en el prompt: el agente lo carga con cargar_skill cuando va a hacer esa tarea.",
            "maxLength": 20000
          },
          "requiere": {
            "description": "Slugs de otras skills del mismo agente que el agente recibe completas, antes que esta, cada vez que la carga. Sirve para una base que varias comparten sin repetirla. Un solo nivel: una skill requerida no puede requerir otra.",
            "type": "array",
            "items": {
              "type": "string"
            },
            "examples": [
              [
                "contabilidad-estandar"
              ]
            ]
          }
        },
        "required": [
          "slug",
          "nombre",
          "descripcion",
          "contenido"
        ]
      },
      "SkillDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "El id de la skill. El agente no lo usa: la nombra por su slug."
          },
          "slug": {
            "type": "string",
            "examples": [
              "cerrar-el-mes"
            ]
          },
          "nombre": {
            "type": "string",
            "examples": [
              "Cerrar el mes"
            ]
          },
          "descripcion": {
            "type": "string",
            "description": "Lo unico de la skill que viaja en el prompt del agente, en cada mensaje.",
            "examples": [
              "Los pasos del cierre mensual de una empresa."
            ]
          }
        },
        "required": [
          "id",
          "slug",
          "nombre",
          "descripcion"
        ]
      },
      "PaginaDeSkillsDto": {
        "type": "object",
        "properties": {
          "limit": {
            "type": "number",
            "description": "Cuantas filas se pidieron. Es el limit del pedido o 50 si no se mando.",
            "examples": [
              50
            ]
          },
          "offset": {
            "type": "number",
            "description": "Cuantas filas se saltearon. Es el offset del pedido o 0.",
            "examples": [
              0
            ]
          },
          "hasMore": {
            "type": "boolean",
            "description": "true si hay mas filas despues de esta pagina. Para seguir, repetir el pedido con offset + limit."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SkillDto"
            }
          }
        },
        "required": [
          "limit",
          "offset",
          "hasMore",
          "data"
        ]
      },
      "SkillConContenidoDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "El id de la skill. El agente no lo usa: la nombra por su slug."
          },
          "slug": {
            "type": "string",
            "examples": [
              "cerrar-el-mes"
            ]
          },
          "nombre": {
            "type": "string",
            "examples": [
              "Cerrar el mes"
            ]
          },
          "descripcion": {
            "type": "string",
            "description": "Lo unico de la skill que viaja en el prompt del agente, en cada mensaje.",
            "examples": [
              "Los pasos del cierre mensual de una empresa."
            ]
          },
          "contenido": {
            "type": "string",
            "description": "El instructivo completo: lo que el agente lee cuando la carga con cargar_skill. No viaja en su prompt.",
            "examples": [
              "# Cerrar el mes\n1. Revisar los movimientos."
            ]
          },
          "requiere": {
            "description": "Las skills que el agente recibe completas, antes que esta, cada vez que la carga.",
            "type": "array",
            "items": {
              "type": "string"
            },
            "examples": [
              [
                "contabilidad-estandar"
              ]
            ]
          }
        },
        "required": [
          "id",
          "slug",
          "nombre",
          "descripcion",
          "contenido",
          "requiere"
        ]
      },
      "ActualizarSkillDto": {
        "type": "object",
        "properties": {
          "nombre": {
            "type": "string",
            "maxLength": 255
          },
          "descripcion": {
            "type": "string",
            "maxLength": 500
          },
          "contenido": {
            "type": "string",
            "maxLength": 20000
          },
          "requiere": {
            "description": "Slugs de otras skills del mismo agente que el agente recibe completas, antes que esta, cada vez que la carga. Sirve para una base que varias comparten sin repetirla. Un solo nivel: una skill requerida no puede requerir otra.",
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "MetricasDeConversacionDto": {
        "type": "object",
        "properties": {
          "conversationId": {
            "type": "string",
            "description": "El id de la conversación."
          },
          "pipelineId": {
            "type": "string"
          },
          "pipelineName": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "description": "Estado actual: pending, active, waiting, completed, failed, abandoned o expired."
          },
          "controlStatus": {
            "type": "string",
            "description": "Quién la tiene ahora: agent, escalated o human."
          },
          "outcome": {
            "type": [
              "string",
              "null"
            ],
            "description": "Por qué terminó, si terminó con un motivo."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Cuándo entró."
          },
          "escalatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "La primera vez que el agente la derivó a una persona."
          },
          "takenAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "La primera vez que una persona la tomó."
          },
          "takenBy": {
            "type": [
              "string",
              "null"
            ],
            "description": "El id de quien la tomó primero. null si la tomaron desde el celular."
          },
          "closedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Cuándo se cerró. null si sigue abierta."
          },
          "closedBy": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "ai",
              "human",
              "system",
              null
            ],
            "description": "Quién la cerró: el agente, una persona o el sistema (inactividad, seguimiento agotado)."
          },
          "resolvedByAi": {
            "type": "boolean",
            "description": "true si la cerró el agente sin derivarla ni que nadie la tomara."
          },
          "waitSeconds": {
            "type": [
              "number",
              "null"
            ],
            "description": "Segundos entre la última derivación y que una persona la tomara, la primera vez que pasó."
          },
          "aiSeconds": {
            "type": "number",
            "description": "Segundos que la atendió el agente."
          },
          "humanSeconds": {
            "type": "number",
            "description": "Segundos que la atendió una persona."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "El último cambio. Es el campo por el que filtra el rango."
          }
        },
        "required": [
          "conversationId",
          "pipelineId",
          "pipelineName",
          "status",
          "controlStatus",
          "outcome",
          "createdAt",
          "escalatedAt",
          "takenAt",
          "takenBy",
          "closedAt",
          "closedBy",
          "resolvedByAi",
          "waitSeconds",
          "aiSeconds",
          "humanSeconds",
          "updatedAt"
        ]
      },
      "PaginaDeMetricasDto": {
        "type": "object",
        "properties": {
          "limit": {
            "type": "number",
            "description": "Cuantas filas se pidieron. Es el limit del pedido o 50 si no se mando.",
            "examples": [
              50
            ]
          },
          "offset": {
            "type": "number",
            "description": "Cuantas filas se saltearon. Es el offset del pedido o 0.",
            "examples": [
              0
            ]
          },
          "hasMore": {
            "type": "boolean",
            "description": "true si hay mas filas despues de esta pagina. Para seguir, repetir el pedido con offset + limit."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MetricasDeConversacionDto"
            }
          }
        },
        "required": [
          "limit",
          "offset",
          "hasMore",
          "data"
        ]
      },
      "AdjuntoDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "El id del adjunto. En la ficha es el id del archivo; en el chat es el id del mensaje que lo trajo."
          },
          "nombre": {
            "type": "string",
            "description": "El nombre original del archivo. Los medios del chat no guardan el nombre que tenian en el telefono: salen con el nombre con que se guardaron.",
            "examples": [
              "Contrato firmado.pdf"
            ]
          },
          "mime": {
            "type": "string",
            "examples": [
              "application/pdf"
            ]
          },
          "tamano": {
            "type": [
              "number",
              "null"
            ],
            "description": "Peso en bytes. null si no se sabe: los medios del chat no lo guardan."
          },
          "fecha": {
            "type": "string",
            "description": "Cuando llego al chat o se adjunto a la ficha.",
            "format": "date-time"
          },
          "origen": {
            "type": "string",
            "description": "chat: lo mando el cliente por el chat (imagen, audio o video). ficha: lo que esta en la ficha del caso, que mezcla los documentos que el cliente mando por el chat, que quedan solo aqui, con los archivos que subio el equipo; subidoPor los distingue.",
            "enum": [
              "chat",
              "ficha"
            ]
          },
          "subidoPor": {
            "type": "string",
            "description": "Quien lo subio. cliente: lo mando la persona por el chat. equipo: lo cargo alguien del equipo en la ficha. Todo lo de origen chat es del cliente; en la ficha se distingue por como entro el archivo.",
            "enum": [
              "cliente",
              "equipo"
            ]
          },
          "url": {
            "type": "string",
            "description": "Direccion firmada para descargar el archivo, sin credencial. Vive 10 minutos: pide la lista de nuevo cuando venza. Baja con su nombre en Content-Disposition; solo se ven en linea los pdf, imagenes, audio y video, lo demas baja como archivo."
          },
          "venceEn": {
            "type": "string",
            "description": "Hasta cuando sirve la url.",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "nombre",
          "mime",
          "tamano",
          "fecha",
          "origen",
          "subidoPor",
          "url",
          "venceEn"
        ]
      },
      "PaginaDeAdjuntosDto": {
        "type": "object",
        "properties": {
          "limit": {
            "type": "number",
            "description": "Cuantas filas se pidieron. Es el limit del pedido o 50 si no se mando.",
            "examples": [
              50
            ]
          },
          "offset": {
            "type": "number",
            "description": "Cuantas filas se saltearon. Es el offset del pedido o 0.",
            "examples": [
              0
            ]
          },
          "hasMore": {
            "type": "boolean",
            "description": "true si hay mas filas despues de esta pagina. Para seguir, repetir el pedido con offset + limit."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AdjuntoDto"
            }
          }
        },
        "required": [
          "limit",
          "offset",
          "hasMore",
          "data"
        ]
      },
      "DisponibilidadDelMiembroDto": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "description": "El email de la persona."
          },
          "estado": {
            "type": "string",
            "description": "Lo que quedo marcado, por la persona o por la API. No considera el horario de la organizacion: eso es disponibleParaRepartir.",
            "enum": [
              "disponible",
              "ausente"
            ]
          },
          "disponibleParaRepartir": {
            "type": "boolean",
            "description": "Si el reparto le asignaria un caso nuevo ahora mismo. Es false si esta ausente, y tambien si esta disponible pero la organizacion tiene horario de atencion y ya cerro: el auto-away por horario manda, salvo que la persona haya pedido seguir un rato mas (extendidoHasta en el futuro). Poner disponible fuera de horario no cambia eso."
          },
          "cambiadoEl": {
            "type": [
              "string",
              "null"
            ],
            "description": "Cuando se cambio el estado por ultima vez. null si nunca.",
            "format": "date-time"
          },
          "extendidoHasta": {
            "type": [
              "string",
              "null"
            ],
            "description": "Hasta cuando pidio la persona seguir recibiendo casos pasado el horario. null si no hay extension.",
            "format": "date-time"
          }
        },
        "required": [
          "email",
          "estado",
          "disponibleParaRepartir",
          "cambiadoEl",
          "extendidoHasta"
        ]
      },
      "CambiarDisponibilidadDto": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "description": "El email de la persona del equipo. No distingue mayusculas. Tiene que ser un miembro activo de tu organizacion.",
            "examples": [
              "ana@empresa.cl"
            ]
          },
          "estado": {
            "type": "string",
            "description": "disponible: puede recibir casos nuevos. ausente: el reparto la salta.",
            "enum": [
              "disponible",
              "ausente"
            ]
          }
        },
        "required": [
          "email",
          "estado"
        ]
      }
    }
  }
}
