SAV Informática Docs Contactar
Todos los productos

Documentación técnicaAPI · 1.7.4

AGOS Agentic OS — Referencia de la API REST

Referencia completa de la API REST de AGOS Agentic OS para equipos que implementan e integran la plataforma.

En este artículo

Esta guía documenta todos los endpoints de la API de AGOS Agentic OS: autenticación, permisos, formatos, límites, errores y ejemplos de uso. Está dirigida a los equipos que implementan AGOS en un cliente y a los integradores que conectan AGOS con un ERP, un portal web u otro sistema.

Nota: Versión de la API: v1 (AGOS 1.7.3 o superior). La especificación OpenAPI 3.0 completa, legible por máquina, está en api/agos-openapi.json. Puede importarse en Postman, Insomnia, Azure API Management o cualquier generador de clientes OpenAPI.


Conceptos básicos

ConceptoDescripción
AgenteAsistente especializado en un dominio (por ejemplo, “Medicamentos” o “Talento humano”). Tiene instrucciones, identidad, memoria, skills, su propio índice de conocimiento y, opcionalmente, herramientas MCP y un modelo propio. Se identifica por id (entero) y key (clave técnica en minúsculas, por ejemplo soporte-app).
ConocimientoDocumentos PDF, guiones de video (.md/.txt) y FAQs indexados para un agente. El agente responde solo con esa información.
EnrutadorGrupo aislado de agentes. Decide qué agente responde según las palabras de enrutamiento; si ninguno coincide, responde el agente de respaldo.
WidgetChat embebible en cualquier sitio web. Puede publicarse para un agente o para un enrutador. Se identifica por un publicationId público.
Licencia de usoClave asignada a cada usuario externo (por ejemplo, un cliente del ERP) para usar un widget, con plan, cupo mensual y vencimiento opcional.
PlanFree, Standard o Premium. Define cuotas, tamaño máximo de mensaje, longitud de respuesta y prioridad en la cola.
Licencia de la instalaciónToken firmado por SAV Informática que habilita la instalación de AGOS. Sin licencia vigente, el chat y los widgets responden 402.

URL base

La API se publica en el mismo origen que el panel de administración, bajo el prefijo /api:

http(s)://<servidor-agos>/api/...

En una instalación estándar el panel escucha en el puerto 3000 (http://<servidor-agos>:3000) y su proxy reenvía /api/ al orquestador. El puerto interno del orquestador (8080) no se publica fuera del servidor.

Explorador interactivo (Swagger)

La documentación interactiva está en:

http(s)://<servidor-agos>/api/docs
  • Solo la pueden abrir usuarios con el rol Administrador que hayan iniciado sesión en el panel desde el mismo navegador. Sin sesión responde 401; con otro rol, 403.
  • Los endpoints con candado exigen autenticación. Cada uno documenta en su respuesta 403 el permiso que necesita (desde AGOS 1.7.4).
  • Try it out funciona con la sesión del panel. Para probar como lo haría un sistema externo, use el botón Authorize y pegue un token obtenido con POST /api/auth/login.
  • La especificación JSON está en /api/docs/v1/swagger.json, con la misma restricción de acceso.

Autenticación

AGOS usa JWT (JSON Web Tokens) firmados por el servidor. Hay dos formas de enviarlos:

ClienteMecanismoCuándo usarlo
Sistemas e integraciones (ERP, backend, scripts)Encabezado Authorization: Bearer <token>Siempre que quien llama no sea un navegador con sesión del panel.
Panel de administración (navegador)Cookie agos_session (HttpOnly, SameSite=Strict) + encabezado X-Agos-Csrf en POST/PUT/DELETEUso interno del panel. No se recomienda para integraciones.

Los endpoints públicos del widget (/api/public/widgets/...) no usan JWT: se autorizan con el licenseId del usuario final (ver Widgets públicos).

Obtener un token

POST /api/auth/login
Content-Type: application/json

{
  "email": "integracion@empresa.com",
  "password": "********",
  "useCookie": false
}

Respuesta 200 OK:

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "plan": "Standard"
}

Use el token en cada llamada:

GET /api/admin/agents
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Reglas del token

ReglaDetalle
AlgoritmoHS256, firmado con la clave secreta de la instalación.
Vigencia8 horas por defecto (configurable por el operador con Jwt__ExpirationHours). Al vencer, la API responde 401: vuelva a llamar a login.
Revocación inmediataEn cada petición el servidor comprueba que el usuario siga existiendo, no esté bloqueado y no haya cambiado su sello de seguridad. Cambiar la contraseña, bloquear al usuario o modificar sus roles invalida al instante todos sus tokens.
ContenidoIdentificador del usuario, correo, plan, roles y todos los permisos (permission) de sus roles. Si cambian los permisos de un rol, el usuario debe volver a iniciar sesión para recibirlos.
Bloqueo por intentosLas contraseñas incorrectas cuentan para el bloqueo temporal de la cuenta (ASP.NET Identity).
Límite de intentosPOST /api/auth/* admite 10 peticiones por minuto por dirección IP.

Importante: Recomendación para integraciones: cree un usuario exclusivo para cada sistema integrado (por ejemplo, erp-integracion@cliente.com) y asígnele un rol personalizado solo con los permisos que necesita (ver Roles y permisos). No use la cuenta de un administrador. Guarde la contraseña en un almacén de secretos y renueve el token antes de que venga.

Sesión del navegador (panel)

Con "useCookie": true el servidor no devuelve el token en el cuerpo: lo guarda en la cookie agos_session y responde el perfil de la sesión:

{
  "plan": "Standard",
  "session": {
    "id": "6f1c...",
    "email": "admin@empresa.com",
    "roles": ["Administrador"],
    "plan": "Standard",
    "expiresAt": "2026-10-13T23:15:00+00:00"
  }
}

Con la cookie, las peticiones POST, PUT y DELETE deben incluir el encabezado X-Agos-Csrf: 1; sin él se rechazan con 401. Si una petición trae Authorization, la cookie se ignora.


Permisos y roles

AGOS aplica mínimo privilegio: cada endpoint exige un permiso concreto. Los permisos se agrupan en roles y viajan dentro del token.

Catálogo de permisos

MóduloPermisoPermite
Agentesagents.readVer agentes y su configuración
Agentesagents.writeCrear, editar y activar o desactivar agentes
Conocimientoknowledge.readVer documentos, FAQs e índices
Conocimientoknowledge.writeSubir y eliminar documentos y FAQs
Enrutadoresrouters.readVer enrutadores
Enrutadoresrouters.writeCrear, editar y publicar enrutadores y sus widgets
Conectoresconnectors.readVer conectores
Conectoresconnectors.writeCrear, editar y sincronizar conectores
Herramientas MCPmcp.readVer servidores MCP
Herramientas MCPmcp.writeRegistrar, editar y probar servidores MCP
Memoriamemory.readVer perfiles de memoria compartidos
Memoriamemory.writeCrear y editar perfiles de memoria
Skillsskills.readVer skills compartidas
Skillsskills.writeCrear y editar skills compartidas
Modelosmodels.readVer proveedores, modelos y configuración de voz
Modelosmodels.writeConfigurar proveedores, modelos y voz
Planesplanlimits.readVer límites por plan y licencias de uso de widgets
Planesplanlimits.writeEditar límites por plan y licencias de uso de widgets
Identidadbranding.readVer la configuración de marca
Identidadbranding.writeEditar logo, nombre y favicon
Usuariosusers.readVer cuentas de usuario
Usuariosusers.writeCrear, bloquear y eliminar cuentas, y restablecer claves
Roles y permisosroles.readVer roles y asignaciones
Roles y permisosroles.writeCrear roles personalizados y asignar o revocar roles
Auditoríaaudit.readVer el registro de auditoría de seguridad
Métricasmetrics.readVer métricas y exportar el historial de conversaciones
Licencialicense.readVer el estado de la licencia de la instalación
Licencialicense.writeCargar o quitar el token de licencia
Guardrailsguardrails.readVer las reglas de seguridad globales
Guardrailsguardrails.writeEditar las reglas de seguridad globales
Plantilla de respuestaprompttemplate.readVer las plantillas de respuesta (RAG y agéntica)
Plantilla de respuestaprompttemplate.writeEditar las plantillas de respuesta

Roles base

RolPermisos
AdministradorTodos.
ContribuidorLectura y escritura en Agentes, Conocimiento, Enrutadores, Conectores, Herramientas MCP, Memoria, Skills y Modelos, más metrics.read. Sin acceso a usuarios, roles, planes, marca, auditoría, licencia ni guardrails.
LectorTodos los permisos .read.

Los roles base se resincronizan con el catálogo en cada arranque y no se pueden editar ni eliminar. Para otras combinaciones, cree roles personalizados.

Cómo se evalúan los permisos

  • Algunos controladores exigen un permiso a nivel de clase y otro en la acción. Se exigen ambos. Por ejemplo, POST /api/admin/connectors/{id}/sync requiere connectors.read y connectors.write. En el catálogo de endpoints aparecen todos los permisos requeridos.
  • Los endpoints marcados como Sesión solo exigen un token válido, sin permiso específico (por ejemplo, el chat).
  • Sin token o con token inválido la respuesta es 401; con token válido pero sin el permiso, 403.

Convenciones de la API

TemaConvención
FormatoJSON UTF-8 (Content-Type: application/json), salvo cargas de archivos (multipart/form-data), audio (audio/wav), PDF y CSV.
Nombres de camposcamelCase. Mayúsculas y minúsculas no importan al enviar.
EnumeracionesSe envían y reciben como texto: "Free", "OpenAi", "OAuth2ClientCredentials".
FechasISO 8601 en UTC, por ejemplo 2026-10-13T15:30:00Z. Los campos de solo fecha usan yyyy-MM-dd.
IdentificadoresAgentes, enrutadores, servidores MCP, proveedores, perfiles, skills y licencias de uso usan id entero. Usuarios y roles usan cadenas (GUID). Documentos y conectores usan GUID sin guiones.
Claves técnicas (key)Si no se envían, se generan a partir del nombre: minúsculas, sin tildes y con guiones (Canal Clínico → canal-clinico). Mínimo 2 caracteres y únicas por tipo.
Paginación?page=1&size=50 (página desde 1). Respuesta: { "total", "page", "size", "items": [] }.
ErroresVea Códigos de estado y errores.

Límites de uso y protección

ControlAlcanceLímiteRespuesta
Intentos de autenticación/api/auth/*10 peticiones/minuto por IP429
Ráfagas de chatPOST /api/chat, POST /api/chat/stream20 peticiones/minuto por usuario (configurable por el operador con RateLimiting__ChatPerMinute)429 con Retry-After: 60
Cuota diariaPOST /api/chat, POST /api/chat/streamdailyMessages del plan del usuario429 con Retry-After: 86400
Tamaño del mensajeChat y widgetmaxInputChars del plan; el exceso se recorta, no se rechaza—
Longitud de la respuestaChat y widgetmaxOutputTokens del plan (0 = sin límite)—
Espera en colaChat y widgetqueueTimeoutSeconds del plan503
Widget público/api/public/widgets/* (chat, transcripción, voz)20 peticiones/minuto por IP429
Cupo mensual del widgetPOST /api/public/widgets/{id}/chatmonthlyInputTokenLimit del plan de la licencia de uso429
Audio autenticadoPOST /api/audio/transcribe, POST /api/audio/speak10 peticiones/minuto por usuario429
Licencia de la instalación/api/chat, /api/public, /api/audioLicencia Valid, ExpiringSoon o GracePeriod402
Tamaño de archivosDocumentos: 50 MB · Audio: 15 MB · Imágenes de chat: 5 MB · Recursos de marca: 5 MB · Avatar del widget: 100 KB—400 / 413

Todas las respuestas incluyen cabeceras de seguridad OWASP: X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy, Content-Security-Policy y, con HTTPS, Strict-Transport-Security.


Catálogo de endpoints

Leyenda de la columna Permiso: Público = sin autenticación · Sesión = token válido, sin permiso específico · x.read, x.write = se exigen todos los permisos listados.

1. Autenticación (/api/auth)

MétodoRutaPermisoDescripción
POST/api/auth/loginPúblicoInicia sesión. Body: { email, password, useCookie }. Devuelve { token, plan }, o con useCookie: true la cookie y { plan, session }. Credenciales inválidas: 401.
POST/api/auth/logoutPúblicoBorra la cookie de sesión del navegador. 204. Los tokens Bearer no se invalidan: caducan solos o al cambiar el sello de seguridad del usuario.
POST/api/auth/registerPúblicoAutorregistro con plan Free. Deshabilitado por defecto (404); el operador lo activa con Auth__AllowSelfRegistration=true.

2. Chat (/api/chat)

MétodoRutaPermisoDescripción
POST/api/chatSesiónEnvía una pregunta y espera la respuesta completa.
POST/api/chat/streamSesiónIgual, pero devuelve la respuesta en fragmentos por Server-Sent Events.
GET/api/chat/queueSesiónPeticiones en cola: { "length": 3 }.
POST/api/chat/{id}/feedbackSesiónCalifica una respuesta propia. id = messageId. Body { "score": 1 } (útil) o { "score": -1 } (no útil).

Petición (POST /api/chat y /api/chat/stream):

{
  "message": "¿Cómo registro una devolución de mercancía?",
  "agentKey": "soporte-app",
  "imageBase64": null,
  "history": [
    { "role": "user", "text": "Hola" },
    { "role": "assistant", "text": "Hola, ¿en qué te ayudo?" }
  ]
}
CampoObligatorioDescripción
messageSíPregunta del usuario. Si supera maxInputChars del plan, se recorta.
agentKeyNoEnvía la pregunta directamente a ese agente. Si se omite, decide el enrutador por defecto o, si no hay ninguno, la clasificación global por palabras clave. Una clave inexistente responde 400.
imageBase64NoImagen PNG, JPEG o WebP de hasta 5 MB, en base64 puro o como data URL (data:image/png;base64,...). El modelo de visión de la instalación la describe y esa descripción se suma a la pregunta.
historyNoConversación previa para dar contexto a preguntas de seguimiento. Roles user o assistant. Se usan los últimos 6 turnos, de hasta 1.500 caracteres cada uno. Un seguimiento sin palabras clave se queda con el agente que venía respondiendo.

Respuesta 200 OK (POST /api/chat):

{
  "text": "Para registrar una devolución...\n\n**Fuente:** Manual de devoluciones, pág. 4",
  "personality": "soporte-app",
  "tokensUsed": 412,
  "elapsedMs": 2310.5,
  "messageId": 98231,
  "sources": [
    {
      "fileName": "Manual-Devoluciones.pdf",
      "page": 4,
      "score": 0.83,
      "title": "Manual de devoluciones",
      "videoUrl": null,
      "videoId": null,
      "startSeconds": null
    }
  ]
}
  • text viene en Markdown (puede incluir tablas, listas y diagramas Mermaid).
  • personality es la clave del agente que respondió.
  • En sources, los campos videoUrl, videoId y startSeconds solo vienen cuando la fuente es un guion de video; permiten abrir el video en el minuto citado.

Streaming (POST /api/chat/stream). La respuesta es text/event-stream:

data: {"event":"queue_position","length":0}

data: Para registrar

data:  una devolución...

data: [DONE]
  1. El primer evento indica la posición en la cola.
  2. Cada evento siguiente trae un fragmento de texto plano que se concatena al anterior.
  3. data: [DONE] cierra la respuesta.

El streaming no devuelve messageId ni sources. Si los necesita, use POST /api/chat.

Errores del chat: 400 (imagen inválida o agente inexistente), 401, 402 (licencia de la instalación), 429 (ráfaga o cuota diaria), 503 (cola llena):

{ "error": "Cuota diaria agotada", "plan": "Free", "retryAfter": "Mañana a las 00:00 UTC" }
{ "error": "Servidor ocupado. Intente nuevamente.", "queueTimeout": 90 }

Nota: Las respuestas de agentes sin herramientas MCP se guardan en caché 30 minutos para preguntas idénticas con el mismo contexto. Editar la persona del agente invalida la caché automáticamente.

3. Agentes

MétodoRutaPermisoDescripción
GET/api/admin/agentsagents.readLista los agentes con toda su configuración.
POST/api/admin/agentsagents.writeCrea un agente.
PUT/api/admin/agents/{id}agents.writeEdita nombre, descripción, palabras de enrutamiento, instrucciones, identidad, memoria y skills propias.
PUT/api/admin/agents/{id}/modelagents.writeAsigna un modelo propio al agente.
PUT/api/admin/agents/{id}/activeagents.writeActiva o desactiva el agente. Body { "active": false }. El agente de respaldo global no se puede desactivar (400).

Agente (elemento de GET /api/admin/agents):

{
  "id": 12,
  "key": "soporte-app",
  "name": "Soporte de la aplicación",
  "description": "Resuelve dudas de uso del ERP",
  "keywords": "factura|inventario|devolución",
  "systemPrompt": "Responde SOLO con la información de los manuales...",
  "elasticIndex": "idx-soporte-app",
  "isActive": true,
  "isFallback": false,
  "priority": 10,
  "isPublished": true,
  "publicationId": "3f9c0b7e4d1a4c40a2b1f0c6a9e7d512",
  "textModel": null,
  "modelProviderId": null,
  "identity": "Eres Alicia, asistente virtual de...",
  "memory": "- Horario de soporte: 8:00 a 18:00",
  "skillsJson": "[{\"name\":\"Guía paso a paso\",\"description\":\"...\",\"content\":\"...\"}]"
}

Crear (POST):

{
  "name": "Soporte de la aplicación",
  "key": "soporte-app",
  "description": "Resuelve dudas de uso del ERP",
  "keywords": "factura, inventario, devolución",
  "systemPrompt": "Responde SOLO con la información de los manuales indexados.",
  "priority": 10
}

Editar (PUT /api/admin/agents/{id}):

{
  "name": "Soporte de la aplicación",
  "description": "Resuelve dudas de uso del ERP",
  "keywords": "factura|inventario|devolución",
  "systemPrompt": "## Reglas\n1. Responde solo con los documentos...",
  "identity": "Eres **Alicia**, asistente virtual de ...",
  "memory": "- Horario de atención: lunes a sábado 7:00-21:00",
  "skillsJson": "[{\"name\":\"Consulta de medicamento\",\"description\":\"Formato de respuesta\",\"content\":\"[Saludo]. El [medicamento]...\"}]"
}
CampoReglas
nameObligatorio. El panel lo limita a 120 caracteres.
descriptionObligatoria al crear. El panel la limita a 500 caracteres.
keywordsPalabras o frases separadas por , o |. Se normalizan a minúsculas, sin duplicados, y se guardan separadas por |. El enrutador elige el agente si el mensaje contiene alguna. Con varias coincidencias gana la mayor priority.
systemPromptObligatorio. Instrucciones del agente en Markdown; el panel las limita a 4.000 caracteres.
identity / memoryMarkdown libre. La identidad se antepone a las instrucciones; la memoria son datos fijos que el agente recuerda siempre.
skillsJsonArreglo JSON de { name, description, content }, hasta 8.000 caracteres en total.
priorityDesempate entre agentes cuando varios coinciden. Más alto = preferido.

Cómo se compone la persona: en cada respuesta, AGOS arma el mensaje de sistema con las secciones # Identidad → # Instrucciones (el systemPrompt) → # Memoria (perfiles heredados más la memoria propia) → # Habilidades (skills compartidas más las propias). Luego aplica las reglas de seguridad y la plantilla de respuesta.

Modelo propio (PUT /api/admin/agents/{id}/model):

{ "providerId": 3, "textModel": null }

Use providerId para un proveedor externo registrado (Modelos de IA) o textModel para un modelo local de Ollama. Enviar ambos en null vuelve al modelo global. Respuesta: { id, key, name, textModel, modelProviderId }.

4. Conocimiento: documentos y FAQs

MétodoRutaPermisoDescripción
POST/api/admin/agents/{id}/documentsknowledge.writeSube un documento para indexar (asíncrono).
GET/api/admin/agents/{id}/documentsknowledge.readTrabajos de indexación del agente y su estado.
GET/api/admin/agents/{id}/knowledgeknowledge.readContenido indexado del agente: documentos, FAQs, fragmentos y páginas.
GET/api/admin/agents/{id}/documents/{jobId}/fileknowledge.readDescarga el PDF original (application/pdf, se muestra en el navegador).
DELETE/api/admin/agents/{id}/documents/{jobId}knowledge.writeElimina el documento del índice y del almacenamiento.
POST/api/admin/agents/{id}/faqsknowledge.writeCrea una pregunta frecuente indexada.
DELETE/api/admin/agents/{id}/faqs/{faqId}knowledge.writeElimina una FAQ (faqId empieza por faq-).

Subir un documento (multipart/form-data):

CampoObligatorioDescripción
fileSí.pdf, .md o .txt, no vacío, hasta 50 MB. Los PDF escaneados se procesan con OCR.
titleSíNombre visible de la fuente; el agente lo cita en sus respuestas.
sourceSíÁrea, norma o proveedor de origen.
tagsNoEtiquetas separadas por coma.
metadataJsonNoObjeto JSON con metadata adicional. Por defecto {}.
curl -X POST "https://agos.empresa.com/api/admin/agents/12/documents" \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@Manual-Devoluciones.pdf" \
  -F "title=Manual de devoluciones" \
  -F "source=Operaciones" \
  -F "tags=devoluciones,2026" \
  -F 'metadataJson={"version":"3.1","actualizado":"2026-10-01"}'

Respuesta 202 Accepted. El indexador procesa el archivo en segundo plano:

{
  "jobId": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
  "agentId": 12,
  "agentKey": "soporte-app",
  "elasticIndex": "idx-soporte-app",
  "fileName": "Manual-Devoluciones.pdf",
  "storedFileName": "a1b2c3d4e5f60718293a4b5c6d7e8f90.pdf",
  "title": "Manual de devoluciones",
  "source": "Operaciones",
  "tags": ["devoluciones", "2026"],
  "metadataJson": "{\"version\":\"3.1\",\"actualizado\":\"2026-10-01\"}",
  "uploadedAt": "2026-10-13T15:30:00Z",
  "status": "Pending",
  "error": null,
  "chunks": 0
}

Estados del trabajo: Pending → Processing → Indexed (con chunks > 0) o Failed (con error). Consulte GET .../documents para seguir el avance. Un documento fallido no se reintenta: elimínelo y vuelva a subirlo.

Guiones de video (.md/.txt). Encabezado opcional y marcas de tiempo:

---
video_url: https://www.youtube.com/watch?v=XXXXXXXXXXX
title: Cómo registrar una devolución
---
[00:00] Introducción al módulo de devoluciones...
[01:30] Para crear la devolución, abra el menú...

Las respuestas que citan el guion devuelven videoId y startSeconds para abrir el video en el minuto exacto. Las marcas admiten [mm:ss], [h:mm:ss] y (min 2:10).

Crear una FAQ:

{
  "question": "¿Cuál es el horario de soporte?",
  "answer": "De lunes a viernes de 8:00 a 18:00.",
  "tags": ["soporte"],
  "metadata": { "area": "mesa de ayuda" }
}

Respuesta 201 con { id: "faq-...", question, answer, tags, metadata }.

Conocimiento indexado (GET .../knowledge): { agentId, index, healthy, documents: [...] }. Cada documento trae jobId, title, sourceFile, source, tags, metadata, chunks, pages, preview, hasFile (si el original está disponible para descargar) e indexedChunks (chunkId, page, text).

5. Enrutadores

MétodoRutaPermisoDescripción
GET/api/admin/routersrouters.readLista los enrutadores.
POST/api/admin/routersrouters.read, routers.writeCrea un enrutador.
PUT/api/admin/routers/{id}routers.read, routers.writeEdita un enrutador.
DELETE/api/admin/routers/{id}routers.read, routers.writeElimina un enrutador. 204.
POST/api/admin/routers/{id}/testrouters.read, routers.writeSimula qué agente respondería un mensaje, sin guardar nada.
{
  "name": "Canal clientes",
  "key": "canal-clientes",
  "description": "Atención a clientes de la droguería",
  "isActive": true,
  "isDefault": false,
  "fallbackAgentId": 5,
  "allowedAgentIds": [5, 12, 14]
}

Algoritmo de enrutamiento:

  1. Si la petición trae agentKey, responde ese agente.
  2. Se elige el enrutador: el indicado (widget de enrutador) o el marcado isDefault.
  3. Dentro de allowedAgentIds, gana el primer agente cuyas palabras clave aparezcan en el mensaje (mayor priority primero). Si el mensaje no coincide, se prueban los dos mensajes anteriores del usuario, para mantener el hilo.
  4. Si nadie coincide, responde fallbackAgentId; si no está definido, el agente de respaldo global.
  5. Si no existe ningún enrutador, la clasificación se hace entre todos los agentes activos.

Solo puede haber un enrutador isDefault. Respuesta de test: { "agentKey": "soporte-app", "agentName": "Soporte de la aplicación", "isFallback": false }.

6. Widgets web (administración)

MétodoRutaPermisoDescripción
GET/api/admin/agents/{agentId}/widgetagents.readConfiguración del widget del agente.
PUT/api/admin/agents/{agentId}/widgetagents.read, agents.writePublica o despublica el widget y guarda su apariencia.
POST/api/admin/agents/{agentId}/widget/rotateagents.read, agents.writeGenera un publicationId nuevo; el enlace anterior deja de funcionar.
GET/api/admin/routers/{routerId}/widgetrouters.readIgual, para el widget de un enrutador.
PUT/api/admin/routers/{routerId}/widgetrouters.read, routers.writeIgual, para un enrutador.
POST/api/admin/routers/{routerId}/widget/rotaterouters.read, routers.writeIgual, para un enrutador.

Petición PUT:

{
  "isPublished": true,
  "settings": {
    "title": "Alicia",
    "subtitle": "Asistente de la droguería",
    "welcomeMessage": "Hola, soy Alicia. ¿En qué puedo ayudarte?",
    "placeholder": "Escribe tu pregunta...",
    "avatarUrl": null,
    "fontFamily": "manrope",
    "primaryColor": "#0B6E75",
    "accentColor": "#F2B84B",
    "backgroundColor": "#F7FBFA",
    "textColor": "#17313A",
    "position": "bottom-right",
    "shape": "soft",
    "animation": "rise",
    "width": 400,
    "height": 680,
    "voice": "ef_dora",
    "customCredit": false,
    "creditName": null,
    "privacyUrl": null
  }
}
CampoValores permitidos
title / placeholderObligatorios, hasta 80 caracteres.
subtitleHasta 120 caracteres.
welcomeMessageObligatorio, hasta 300 caracteres.
fontFamilymanrope, space-grotesk, playfair, nunito.
ColoresHexadecimal #RRGGBB.
positionbottom-right, bottom-left, inline.
shapesoft, rounded, sharp.
animationrise, fade, scale, none.
width × height320–560 × 480–900 px.
avatarUrlURL HTTP(S) o data URI PNG/JPEG/WebP de hasta 100 KB.
voiceIdentificador de voz instalada (ver Voz y dictado).
customCredit, creditName, privacyUrlPie de página propio del cliente. Requiere que la licencia de la instalación incluya widget_white_label; si no, 403.

Respuesta: { publicationId, isPublished, settings, canCustomizeCredit }.

7. Widgets públicos (/api/public/widgets)

Estos endpoints los usa el widget embebido. No requieren JWT: se autorizan con la licencia de uso (licenseId) del usuario final. Solo funcionan si el agente o enrutador está publicado y activo; si no, 404.

MétodoRutaAutorizaciónDescripción
GET/api/public/widgets/{publicationId}PúblicoNombre y apariencia del widget: { agentName, settings, audioEnabled }.
POST/api/public/widgets/{publicationId}/validate-licenselicenseIdValida una licencia antes de abrir el chat: { "valid": true }.
POST/api/public/widgets/{publicationId}/chatlicenseIdEnvía un mensaje.
POST/api/public/widgets/{publicationId}/transcribelicenseIdConvierte audio en texto (dictado).
POST/api/public/widgets/{publicationId}/speaklicenseIdConvierte texto en voz (audio/wav).

Chat del widget:

{
  "message": "¿Tienen protector solar para niños?",
  "licenseId": "cliente004218",
  "history": [],
  "voice": false
}
{
  "text": "Sí, contamos con protectores solares...",
  "inputTokensUsed": 12,
  "outputTokensUsed": 180,
  "inputTokensRemaining": 999788,
  "monthlyInputTokenLimit": 1000000,
  "videos": []
}
  • Con voice: true, el asistente responde breve y sin formato, pensado para leerse en voz alta.
  • inputTokensRemaining es null cuando el plan no tiene cupo mensual (monthlyInputTokenLimit = 0).
  • Por privacidad, el widget público solo devuelve citas de video; los nombres de documentos internos no se exponen.

Errores de licencia (401):

{ "error": "La licencia no está activa o no es válida." }
{ "error": "La licencia venció el 2026-12-31. Solicita su renovación a tu organización.", "expired": true }

Cupo agotado (429):

{
  "error": "Se agotó el cupo mensual de tokens de entrada de esta licencia.",
  "inputTokensRemaining": 0,
  "monthlyInputTokenLimit": 1000000
}

Transcribir (multipart/form-data): audioFile (tipo audio/*, hasta 15 MB) y licenseId. Responde el texto reconocido. Voz: body { "text": "...", "voice": "ef_dora", "licenseId": "..." }, responde audio/wav. Ambos devuelven 404 si el audio no está habilitado en la instalación y 503 si el servicio de voz no está listo.

8. Licencias de uso de widgets

MétodoRutaPermisoDescripción
GET/api/admin/widget-licensesplanlimits.readLista las licencias de uso con su consumo del mes.
GET/api/admin/widget-licenses/{id}planlimits.readDetalle de una licencia.
GET/api/admin/widget-licenses/{id}/usage?days=30planlimits.readConsumo diario: { license, daily: [{ date, inputTokens, outputTokens, messages, voiceMessages }] }.
POST/api/admin/widget-licensesplanlimits.read, planlimits.writeCrea una licencia de uso.
PUT/api/admin/widget-licenses/{id}planlimits.read, planlimits.writeCambia nombre, plan, estado o vencimiento.
POST/api/admin/widget-licenses/{id}/rotateplanlimits.read, planlimits.writeGenera una clave nueva; la anterior deja de funcionar.

Crear:

{
  "externalUserId": "CLIENTE-004218",
  "licenseId": "cliente004218",
  "displayName": "Droguería Centro",
  "plan": "Standard",
  "expiresAt": "2026-12-31T23:59:59Z"
}
CampoReglas
externalUserIdIdentificador del usuario en el ERP. Único (409 si se repite).
licenseIdClave que el usuario final envía al widget, hasta 160 caracteres. Única (409).
planFree, Standard o Premium. Define el cupo mensual y la prioridad.
expiresAtOpcional, fecha futura en UTC. null = no vence.
isActiveSolo en PUT. false revoca la licencia al instante.

Respuesta: { id, licenseId, externalUserId, displayName, plan, isActive, expiresAt, isExpired, monthlyInputTokenLimit, inputTokensUsed, outputTokensUsed, messageCount, createdAt, lastUsedAt }. El consumo se reinicia el día 1 de cada mes (UTC).

9. Conectores de fuentes

Los conectores importan PDF desde repositorios corporativos al conocimiento de un agente.

MétodoRutaPermisoDescripción
GET/api/admin/connectorsconnectors.readLista los conectores (nunca devuelve credenciales).
POST/api/admin/connectorsconnectors.read, connectors.writeCrea un conector.
PUT/api/admin/connectors/{id}connectors.read, connectors.writeEdita un conector. Enviar credentials vacías conserva las guardadas.
GET/api/admin/connectors/{id}/previewconnectors.readLista lo que hay en el origen sin importar nada: [{ name, sizeBytes, lastModified, isSupported }].
POST/api/admin/connectors/{id}/syncconnectors.read, connectors.writeImporta los PDF nuevos o modificados: { connector, imported }.
{
  "agentId": 12,
  "name": "Manuales en SharePoint",
  "type": "SharePoint",
  "container": "b!AbC123...",
  "endpoint": "",
  "settings": { "folderId": "01ABCDEF..." },
  "credentials": { "tenantId": "...", "clientId": "...", "clientSecret": "..." }
}
typecontainerendpointsettingscredentials
BlobStorageContenedor—prefixconnectionString (si se omite, usa el almacenamiento de la instalación)
AmazonS3BucketURL de servicio compatible con S3 (opcional)region (por defecto us-east-1), prefix, forcePathStyleaccessKeyId, secretAccessKey, sessionToken (opcional)
GoogleDriveID de carpeta—folderIdserviceAccountJson (compartir la carpeta con la cuenta de servicio)
DropboxRuta de carpeta——accessToken
OneDrive / SharePointID de la unidad (drive)—driveId, folderId (por defecto root)tenantId, clientId, clientSecret (aplicación de Entra ID con permiso de lectura de archivos)
WebSitemap—URL del sitemap XML——
FtpCarpetaHost (ftp://host:21)useSsl, recursive (por defecto true)username, password
  • Las credenciales se cifran en el servidor y nunca se devuelven.
  • La sincronización compara la versión de cada archivo (ETag, revisión o fecha), así que solo importa archivos nuevos o modificados.
  • Cada archivo importado crea un trabajo de indexación igual que una carga manual.
  • La sincronización se ejecuta a demanda: para hacerla periódica, programe un llamado a POST .../sync desde su orquestador de tareas.

Más detalle en Conectores externos.

10. Servidores MCP (herramientas)

Los servidores Model Context Protocol dan a los agentes herramientas para consultar sistemas en tiempo real, como inventario, precios o pedidos.

MétodoRutaPermisoDescripción
GET/api/admin/mcp-serversmcp.readLista los servidores (sin secretos).
POST/api/admin/mcp-serversmcp.read, mcp.writeRegistra un servidor.
PUT/api/admin/mcp-servers/{id}mcp.read, mcp.writeEdita un servidor. secret vacío conserva el guardado.
DELETE/api/admin/mcp-servers/{id}mcp.read, mcp.writeElimina un servidor. 204.
PUT/api/admin/mcp-servers/{id}/activemcp.read, mcp.writeActiva o desactiva: { "active": true }.
POST/api/admin/mcp-servers/{id}/testmcp.read, mcp.writeConecta y lista herramientas: { success, error, tools: [{ name, description }] }.
GET/api/admin/mcp-servers/agents/{agentId}mcp.readServidores asignados al agente: { "serverIds": [1, 4] }.
PUT/api/admin/mcp-servers/agents/{agentId}mcp.read, agents.writeReemplaza la asignación: { "serverIds": [1, 4] }.
{
  "name": "Inventario ERP",
  "key": "inventario-erp",
  "description": "Existencias y precios en tiempo real",
  "transportType": "Http",
  "url": "https://erp.empresa.com/mcp",
  "authType": "OAuth2ClientCredentials",
  "authHeaderName": null,
  "oAuthTokenUrl": "https://login.empresa.com/oauth2/token",
  "oAuthClientId": "agos",
  "oAuthScope": "inventory.read",
  "headers": { "X-Tenant": "centro" },
  "secret": "<client-secret>"
}
CampoValores
transportTypeHttp (Streamable HTTP) o Sse (transporte heredado).
authTypeNone, ApiKey (envía secret en authHeaderName, por defecto Authorization) u OAuth2ClientCredentials (obtiene y renueva el token automáticamente).
secretAPI key o client secret. Se cifra y nunca se devuelve.

Un agente con servidores MCP asignados usa el modo agéntico: el modelo decide qué herramientas llamar, AGOS las ejecuta y redacta la respuesta con los resultados. Estas respuestas no se guardan en caché. Para usar el modo agéntico, el modelo del agente debe admitir llamadas a herramientas.

11. Memoria compartida

Perfiles de memoria reutilizables (contexto corporativo, horarios, políticas) que varios agentes heredan.

MétodoRutaPermisoDescripción
GET/api/admin/memory-profilesmemory.readLista los perfiles.
POST/api/admin/memory-profilesmemory.read, memory.writeCrea un perfil: { name, key, description, content } (content en Markdown).
PUT/api/admin/memory-profiles/{id}memory.read, memory.writeEdita un perfil.
DELETE/api/admin/memory-profiles/{id}memory.read, memory.writeElimina un perfil.
GET/api/admin/memory-profiles/agents/{agentId}memory.readPerfiles heredados por el agente.
PUT/api/admin/memory-profiles/agents/{agentId}memory.read, agents.writeReemplaza los perfiles del agente: { "profileIds": [1, 2] }.

12. Skills compartidas

Habilidades reutilizables (procedimientos, formatos de respuesta, escalamientos) que varios agentes usan.

MétodoRutaPermisoDescripción
GET/api/admin/skillsskills.readLista las skills.
POST/api/admin/skillsskills.read, skills.writeCrea una skill: { name, key, description, content }.
PUT/api/admin/skills/{id}skills.read, skills.writeEdita una skill.
DELETE/api/admin/skills/{id}skills.read, skills.writeElimina una skill.
GET/api/admin/skills/agents/{agentId}skills.readSkills compartidas del agente.
PUT/api/admin/skills/agents/{agentId}skills.read, agents.writeReemplaza las skills del agente: { "skillIds": [3] }.

Las skills propias de un agente se guardan en skillsJson del agente (ver Agentes).

13. Modelos de IA

Proveedores externos (OpenAI, Azure AI Foundry, Anthropic, Ollama remoto):

MétodoRutaPermisoDescripción
GET/api/admin/model-providersmodels.readLista los proveedores. Indica hasApiKey, nunca la clave.
POST/api/admin/model-providersmodels.read, models.writeRegistra un proveedor.
PUT/api/admin/model-providers/{id}models.read, models.writeEdita un proveedor. apiKey vacío conserva la guardada.
DELETE/api/admin/model-providers/{id}models.read, models.writeElimina un proveedor.
PUT/api/admin/model-providers/{id}/activemodels.read, models.writeLo convierte en el modelo global de generación: { "active": true }.
POST/api/admin/model-providers/{id}/testmodels.read, models.writePrueba la conexión: { success, sample } o { success: false, error }.
{
  "name": "Azure OpenAI producción",
  "type": "AzureFoundry",
  "baseUrl": "https://mi-recurso.openai.azure.com",
  "model": "mi-despliegue",
  "apiVersion": "2024-10-21",
  "apiKey": "<clave>"
}

type: Ollama, OpenAi (o compatible con OpenAI), AzureFoundry, Anthropic. apiKey es obligatoria al crear, salvo para Ollama. Los embeddings (búsqueda semántica) siempre se calculan con el modelo local de la instalación.

Modelos locales (Ollama de la instalación):

MétodoRutaPermisoDescripción
GET/api/admin/modelsmodels.readModelos instalados y modelo activo.
PUT/api/admin/models/activemodels.read, models.writeCambia el modelo local activo: { "model": "qwen2.5:7b" }.
POST/api/admin/models/pullmodels.read, models.writeDescarga un modelo en segundo plano: { "model": "qwen2.5:7b" }.
GET/api/admin/models/pull-statusmodels.readProgreso de las descargas en curso.

14. Voz y dictado

MétodoRutaPermisoDescripción
GET/api/audio/statusSesión{ enabled, ready, sttModel, ttsModel }.
POST/api/audio/transcribeSesiónDictado: multipart/form-data con audioFile (audio/*, hasta 15 MB).
POST/api/audio/speakSesiónTexto a voz: { "text": "...", "voice": "ef_dora" } → audio/wav.
GET/api/audio/voicesSesiónVoces disponibles e instaladas.
GET/api/audio/configmodels.readConfiguración: modelo de dictado, dispositivo (CPU/GPU) y voz por defecto.
PUT/api/audio/configmodels.writeCambia la configuración: { sttModel, sttDevice, defaultVoice }.
POST/api/audio/voices/{voiceId}/installmodels.writeDescarga una voz adicional.
DELETE/api/audio/voices/{voiceId}models.writeElimina una voz descargada.

Identificadores de voz: voces incluidas (ef_dora, em_alex, em_santa) y voces descargables piper:<nombre>. Si la instalación no tiene audio habilitado, estos endpoints responden 404. Más detalle en Voz y dictado.

15. Usuarios

MétodoRutaPermisoDescripción
GET/api/admin/users?page=1&size=50&search=users.readLista paginada. Cada elemento: { id, email, userName, plan, createdAt, isActive, roles, quotaUsedToday, dailyLimit }.
POST/api/admin/usersusers.writeCrea un usuario: { email, password, plan, isAdmin }.
DELETE/api/admin/users/{id}users.writeElimina un usuario. No se puede eliminar al último Administrador.
PUT/api/admin/users/{id}/adminusers.writeConcede o quita el rol Administrador: { "isAdmin": true }.
PUT/api/admin/users/{id}/passwordusers.writeRestablece la contraseña: { "newPassword": "..." }. Invalida sus sesiones.
PUT/api/admin/users/{id}/planusers.writeCambia el plan: { "plan": "Premium" }.
PUT/api/admin/users/{id}/activeusers.writeBloquea o desbloquea: { "active": false }. Invalida sus sesiones al instante.
GET/api/admin/users/{id}/quotausers.readCuota diaria: { id, plan, remaining, limit, used }.

16. Roles y permisos

MétodoRutaPermisoDescripción
GET/api/admin/roles/permissionsroles.readCatálogo de permisos agrupado por módulo.
GET/api/admin/rolesroles.readRoles con sus permisos e indicador isSystem.
POST/api/admin/rolesroles.read, roles.writeCrea un rol personalizado: { "name": "Integración ERP", "claims": ["knowledge.read", "knowledge.write"] }.
PUT/api/admin/roles/{id}roles.read, roles.writeEdita un rol personalizado. Los roles base no se editan.
DELETE/api/admin/roles/{id}roles.read, roles.writeElimina un rol personalizado sin usuarios asignados.
GET/api/admin/roles/assignmentsroles.readAsignaciones temporales vigentes.
POST/api/admin/roles/assignroles.read, roles.writeAsigna un rol: { "userId": "...", "roleName": "Contribuidor", "expiresAt": "2026-10-20T00:00:00Z" }. Con expiresAt el rol se retira solo al vencer (acceso temporal).
POST/api/admin/roles/revokeroles.read, roles.writeRevoca un rol: { "userId": "...", "roleName": "Contribuidor" }. No permite dejar la instalación sin Administradores ni que un administrador se quite su propio rol.

17. Límites por plan

MétodoRutaPermisoDescripción
GET/api/admin/plan-limitsplanlimits.readLímites de los tres planes.
GET/api/admin/plan-limits/{plan}planlimits.readLímites de un plan (Free, Standard, Premium).
PUT/api/admin/plan-limits/{plan}planlimits.read, planlimits.writeEdita los límites.
{
  "dailyMessages": 100,
  "maxInputChars": 8000,
  "monthlyInputTokenLimit": 1000000,
  "maxOutputTokens": 1000,
  "queueTimeoutSeconds": 30,
  "queuePriority": 1
}
CampoSignificadoValores iniciales (Free / Standard / Premium)
dailyMessagesMensajes por usuario por día (chat autenticado).20 / 100 / 500
maxInputCharsLongitud máxima del mensaje; el exceso se recorta.2.000 / 8.000 / 16.384
maxOutputTokensLongitud máxima de la respuesta. 0 = sin límite.300 / 1.000 / 2.048
queueTimeoutSecondsEspera máxima en cola antes de responder 503.90 / 30 / 10
queuePriorityPrioridad en la cola: menor = antes.2 / 1 / 0
monthlyInputTokenLimitCupo mensual de tokens de entrada por licencia de uso de widget. 0 = sin límite.Lo configura cada instalación

18. Identidad visual (branding)

MétodoRutaPermisoDescripción
GET/api/brandingPúblico{ productName, clientName, logoUrl, faviconUrl } (lo usa la pantalla de inicio de sesión).
GET/api/branding/assets/{fileName}PúblicoSirve un logo o favicon cargado.
POST/api/admin/branding/assetsbranding.writeCarga una imagen (file: PNG, JPG, WebP o ICO, hasta 5 MB). Respuesta { "url": "/api/branding/assets/..." }.
PUT/api/admin/brandingbranding.writeActualiza { productName (≤120), clientName (≤160), logoUrl, faviconUrl }.

19. Seguridad: guardrails y plantillas de respuesta

Configuración global que se aplica a todos los agentes. Todos responden { "text": "<vigente>", "defaultText": "<de fábrica>" }.

MétodoRutaPermisoDescripción
GET/api/admin/security/guardrailsguardrails.readReglas de seguridad (antiinyección, confidencialidad).
PUT/api/admin/security/guardrailsguardrails.writeEdita las reglas: { "text": "..." } (hasta 8.000 caracteres, no vacío).
POST/api/admin/security/guardrails/resetguardrails.writeRestaura las reglas de fábrica.
GET/api/admin/security/prompt-templateprompttemplate.readPlantilla RAG (respuestas con documentos).
PUT/api/admin/security/prompt-templateprompttemplate.writeEdita la plantilla. Debe contener {{system_prompt}}, {{context}} y {{message}}.
POST/api/admin/security/prompt-template/resetprompttemplate.writeRestaura la plantilla de fábrica.
GET/api/admin/security/agentic-prompt-templateprompttemplate.readPlantilla del modo agéntico (MCP).
PUT/api/admin/security/agentic-prompt-templateprompttemplate.writeEdita la plantilla. Debe contener {{system_prompt}} y {{context}}.
POST/api/admin/security/agentic-prompt-template/resetprompttemplate.writeRestaura la plantilla de fábrica.

Marcadores: {{system_prompt}} = persona completa del agente · {{context}} = fragmentos recuperados de los documentos · {{message}} = pregunta del usuario con el contexto de la conversación.

20. Licencia de la instalación

MétodoRutaPermisoDescripción
GET/api/admin/licenselicense.readEstado de la licencia.
POST/api/admin/licenselicense.read, license.writeAplica un token de licencia: { "token": "eyJ..." }. Token inválido: 400 { error }.
DELETE/api/admin/licenselicense.read, license.writeQuita la licencia. 204.
{
  "status": "Valid",
  "productCode": "sav-agos",
  "tenantId": "cliente-001",
  "tenantName": "Empresa Cliente S.A.S.",
  "licenseId": "LIC-2026-0001",
  "seats": 50,
  "maxAgents": 20,
  "features": ["widget_white_label"],
  "issuedAt": "2026-01-01T00:00:00Z",
  "expiresAt": "2027-01-01T00:00:00Z",
  "lastValidatedAt": "2026-10-13T15:00:00Z"
}
statusServicioSignificado
NotConfiguredBloqueadoNo hay licencia cargada.
ValidActivoLicencia vigente.
ExpiringSoonActivoVence pronto; renovar.
GracePeriodActivoVenció hace poco; periodo de gracia.
ExpiredBloqueadoVenció y terminó la gracia.
InvalidBloqueadoFirma o datos inválidos.

La licencia se valida sin conexión (firma RS256 con la clave pública de SAV Informática). La administración sigue disponible aunque el servicio esté bloqueado, para poder cargar una licencia nueva.

21. Métricas, auditoría e índices

MétodoRutaPermisoDescripción
GET/api/admin/metrics?days=7&agentKey=&tzOffsetMinutes=-300metrics.readPanel de métricas (days de 1 a 365; tzOffsetMinutes = desfase del huso horario del visor).
GET/api/admin/conversations/export?from=2026-10-01&to=2026-10-13metrics.readHistorial de conversaciones en CSV (UTF-8 con BOM, apto para Excel). Rango máximo de un año; por defecto, los últimos 30 días.
GET/api/admin/audit-log?page=1&size=50audit.readRegistro de auditoría de seguridad (bloqueos, cambios de rol, restablecimientos, eliminaciones), paginado.
GET/api/admin/indicesknowledge.readÍndices de conocimiento y su estado.

Métricas: { range, summary, previous, daily, heatmap, latencyBuckets, agents, topQuestions, topLicenses, agentOptions }. summary incluye conversaciones, respuestas, usuarios, tokens, tiempos (promedio, p50, p95, p99), calificaciones y consumo del widget. previous es el periodo anterior equivalente, para comparar. Más detalle en Métricas y consumo.

Columnas del CSV: conversation_id, message_id, created_at_utc, user_email, agent, role, content, tokens, response_ms, feedback. Los valores se protegen contra inyección de fórmulas.

22. Salud del servicio

MétodoRutaPermisoDescripción
GET/healthPúblico{ "status": "healthy", "timestamp": "..." }. Solo se expone dentro del servidor (sondeos de Docker o del balanceador), no a través del panel.

Guías de integración

Integrar un ERP o un portal con el chat (backend a backend)

sequenceDiagram
    participant ERP as ERP / Backend
    participant AGOS as AGOS API
    ERP->>AGOS: POST /api/auth/login {email, password}
    AGOS-->>ERP: { token }
    loop Cada pregunta
        ERP->>AGOS: POST /api/chat (Bearer token) {message, agentKey, history}
        AGOS-->>ERP: { text, sources, messageId }
    end
    ERP->>AGOS: POST /api/chat/{messageId}/feedback {score}
  1. Cree un usuario de integración con un plan acorde al volumen (la cuota diaria es por usuario).
  2. Inicie sesión y guarde el token. Renuévelo ante un 401, o antes de que se cumplan 8 horas.
  3. Envíe history con los últimos turnos para que el agente entienda los seguimientos.
  4. Muestre text como Markdown.
TOKEN=$(curl -s -X POST https://agos.empresa.com/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"erp-integracion@empresa.com","password":"'"$AGOS_PASSWORD"'"}' | jq -r .token)

curl -s -X POST https://agos.empresa.com/api/chat \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"message":"¿Cómo anulo una factura?","agentKey":"soporte-app"}' | jq .
using System.Net.Http.Json;

var http = new HttpClient { BaseAddress = new Uri("https://agos.empresa.com") };
var login = await http.PostAsJsonAsync("/api/auth/login",
    new { email = "erp-integracion@empresa.com", password = Environment.GetEnvironmentVariable("AGOS_PASSWORD") });
login.EnsureSuccessStatusCode();
var token = (await login.Content.ReadFromJsonAsync<LoginResponse>())!.Token;

http.DefaultRequestHeaders.Authorization = new("Bearer", token);
var reply = await http.PostAsJsonAsync("/api/chat", new { message = "¿Cómo anulo una factura?", agentKey = "soporte-app" });
var chat = await reply.Content.ReadFromJsonAsync<ChatResponse>();
Console.WriteLine(chat!.Text);

record LoginResponse(string Token, string Plan);
record ChatResponse(string Text, string Personality, int TokensUsed, double ElapsedMs, int MessageId);

Incrustar el widget en un sitio web o en el ERP

  1. Publique el widget del agente o del enrutador (PUT .../widget con isPublished: true) y copie el publicationId.
  2. Cree una licencia de uso por cada usuario final (POST /api/admin/widget-licenses).
  3. Inserte el iframe y pase la licencia del usuario en license-id:
<iframe
  src="https://agos.empresa.com/widget/3f9c0b7e4d1a4c40a2b1f0c6a9e7d512?license-id=cliente004218"
  title="Asistente"
  allow="microphone"
  style="position:fixed;bottom:24px;right:24px;width:min(400px,calc(100vw - 32px));height:min(680px,calc(100vh - 32px));border:0;background:transparent;z-index:2147483000"
  loading="lazy"></iframe>
  • Sin license-id, el widget pide la clave al usuario.
  • allow="microphone" solo es necesario si se usa la voz, y el navegador exige que el sitio use HTTPS.
  • Para un widget dentro de la página (posición inline), use width:100% y la altura configurada.

Más detalle en Licencias de uso en widgets.

Construir su propio frontend de chat

Use los endpoints públicos del widget desde su backend, o directamente desde el navegador si el frontend se sirve en el mismo dominio que AGOS (la API no habilita CORS):

const base = 'https://agos.empresa.com/api/public/widgets/3f9c0b7e4d1a4c40a2b1f0c6a9e7d512';
await fetch(`${base}/validate-license`, {
  method: 'POST', headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ licenseId: 'cliente004218' })
});
const res = await fetch(`${base}/chat`, {
  method: 'POST', headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ message: '¿Tienen domicilios?', licenseId: 'cliente004218', history: [] })
});
const { text, inputTokensRemaining } = await res.json();

Sincronizar documentos desde otro sistema

Para mantener el conocimiento de un agente al día desde un sistema externo:

  1. Cargue cada documento con POST /api/admin/agents/{id}/documents. Guarde el jobId devuelto junto con el identificador del documento en su sistema.
  2. Cuando el documento cambie, elimine la versión anterior (DELETE .../documents/{jobId}) y cargue la nueva.
  3. Siga el avance con GET .../documents hasta ver Indexed o Failed.

Si los documentos están en SharePoint, OneDrive, Blob Storage, S3, Google Drive, Dropbox, FTP o un sitemap, use un conector y programe POST .../sync.

Rol mínimo recomendado para el usuario de integración: agents.read, knowledge.read y knowledge.write.

Consultar datos en tiempo real (precios, existencias)

No sincronice datos que cambian a cada minuto como documentos. Exponga esas consultas como un servidor MCP y asígnelo al agente (Servidores MCP). El agente las consultará en el momento de responder.


Códigos de estado y errores

CódigoSignificado en AGOS
200 OKOperación correcta.
201 CreatedRecurso creado (agentes, FAQs, enrutadores, licencias de uso, MCP, proveedores, roles, conectores).
202 AcceptedDocumento recibido; se indexará en segundo plano.
204 No ContentEliminación o cierre de sesión correctos.
400 Bad RequestDatos inválidos. Incluye el motivo.
401 UnauthorizedSin token, token vencido o revocado, falta X-Agos-Csrf con la cookie, o licencia de uso del widget inválida o vencida.
402 Payment RequiredLa licencia de la instalación no está vigente (chat, widgets y audio).
403 ForbiddenEl usuario no tiene el permiso requerido, o la licencia no incluye la función (marca blanca).
404 Not FoundEl recurso no existe, el widget no está publicado, el audio no está habilitado o el autorregistro está desactivado.
409 ConflictClave o identificador duplicado.
413 Payload Too LargeEl archivo supera el tamaño máximo.
429 Too Many RequestsLímite de ráfaga, cuota diaria o cupo mensual agotado. Respete Retry-After cuando venga.
503 Service UnavailableCola de inferencia llena, o servicio de voz o modelo no disponible. Reintente con espera exponencial.

Formato de los errores. La mayoría de los errores devuelven JSON con el campo error:

{ "error": "Mensaje legible para el usuario." }

Algunos traen campos adicionales: licenseStatus (402), expired (401 del widget), plan y retryAfter (cuota diaria), queueTimeout (503), inputTokensRemaining (cupo del widget).

Excepciones:

  • Algunas validaciones de administración devuelven el mensaje como texto plano ("El título y la fuente son obligatorios.").
  • Los errores de ASP.NET Identity (contraseñas, roles) devuelven un arreglo de mensajes.
  • Los errores de formato del JSON (tipo o campo inválido) siguen el estándar ProblemDetails (application/problem+json) con el detalle en errors.

Los clientes deben tratar los tres formatos.


Preguntas frecuentes

¿La API exige autenticación? Sí. Todos los endpoints, salvo login, logout, register (deshabilitado por defecto), /api/branding y los del widget público, exigen un token válido y, en la mayoría de los casos, un permiso específico. Los del widget público exigen una licencia de uso válida.

¿Hay API keys para sistemas? Hoy las integraciones se autentican con un usuario dedicado y su token JWT (8 horas). Están previstas API keys de servicio con permisos acotados y revocables; consulte las notas de versión.

¿Por qué recibo 401 en un POST desde el navegador si ya inicié sesión? Con la cookie del panel, los métodos que modifican datos exigen el encabezado X-Agos-Csrf. Las integraciones deben usar Authorization: Bearer.

¿Por qué el chat responde 402? La licencia de la instalación no está vigente. Un administrador puede revisarla en GET /api/admin/license y cargar una nueva con POST /api/admin/license.

¿Puedo llamar a la API desde JavaScript en otro dominio? No directamente: la API no habilita CORS. Llámela desde su backend, o sirva su frontend en el mismo dominio que AGOS mediante un proxy inverso.

¿Cómo obtengo la especificación para generar un cliente? Use api/agos-openapi.json, o descárguela de /api/docs/v1/swagger.json en su instalación con sesión de administrador.


Documentos relacionados

¿Necesitas ayuda con AGOS?Contactar con SAV