Configurar el agente que conversa
Publicar el borrador, conectar herramientas y atender solicitudes.
GET /ai-agents lista los agentes que responden por el workspace cuando nadie del equipo escribe primero. Cada uno trae un system_prompt y las herramientas que puede llamar. También trae los skills que sabe aplicar y la base de conocimiento que puede citar.
Leer pide ai_agents:read; escribir, ai_agents:write.
Borrador y versión en vivo
Cada agente tiene dos caras. El system_prompt y las herramientas ya wireadas son lo que corre ahora mismo. Todo lo que empieza con draft_ es un cambio a medio hacer que nadie más ve todavía.
curl https://api.vitrinadev.com/api/v1/ai-agents/a3a3a3a3-0000-4000-8000-000000000001 \
-H "Authorization: Bearer $VITRINA_KEY"{
"data": {
"id": "a3a3a3a3-0000-4000-8000-000000000001",
"name": "Asistente de ventas",
"status": "active",
"is_default": true,
"system_prompt": "Eres un asistente de ventas. Responde de forma breve y cercana.",
"published_version_id": "a3a3a3a3-1000-4000-8000-000000000007",
"draft_system_prompt": null,
"draft_updated_at": null,
"created_at": "2026-08-04T19:07:05.226Z",
"updated_at": "2026-09-17T14:49:02.893Z"
}
}Vitrina elige y administra el modelo de IA de cada agente. No es parte de la API: no se lee ni se manda.
Editar el borrador
curl -X PUT https://api.vitrinadev.com/api/v1/ai-agents/a3a3a3a3-0000-4000-8000-000000000001/draft \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"system_prompt": "Eres un asistente de ventas. Ahora también ofreces financiamiento.",
"knowledge_tags": ["horarios", "garantia", "financiamiento"]
}'Nada de esto llega al runtime todavía. GET /ai-agents/{id}/draft lee el borrador pendiente y DELETE lo descarta sin tocar lo que está en vivo. Ojo con PUT /ai-agents/{id}/system-prompt: ese no pasa por el borrador, escribe el prompt en vivo de inmediato.
Publicar
curl -X POST https://api.vitrinadev.com/api/v1/ai-agents/a3a3a3a3-0000-4000-8000-000000000001/publish \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{}'Publicar copia el borrador a lo que corre, guarda una versión (GET /ai-agents/{id}/versions) y dispara ai_agent.publish.
Publicar nunca corre, espera ni depende de las evaluaciones: se ejecutan solo cuando alguien las pide.
Dos editores a la vez
Si más de una persona o integración edita el mismo agente (varias claves API, la app y un script), manda la versión que leíste en cada escritura. Sin ella, el segundo en guardar pisa al primero sin aviso.
Cada lectura trae su versión en el cuerpo y en el header X-Config-Version (también ETag):
| Recurso | Se lee en | Campo | Se devuelve en |
|---|---|---|---|
| Borrador | GET /ai-agents/{id}/draft | draft_version | expected_version en PUT /ai-agents/{id}/draft, expected_draft_version en POST /ai-agents/{id}/publish |
| Agente en vivo | GET /ai-agents/{id} o GET /ai-agents/{id}/system-prompt | config_version | expected_version en PUT /ai-agents/{id}/system-prompt, expected_config_version en POST /ai-agents/{id}/publish |
| Skill | GET /skills/{id} | version | expected_version en PUT /skills/{id} |
| Archivo de conocimiento | GET /kb-files | version | campo de formulario expected_version en PUT /kb-files/{id}/content |
En vez del campo puedes mandar el header If-Match: "12".
curl -X PUT https://api.vitrinadev.com/api/v1/ai-agents/a3a3a3a3-0000-4000-8000-000000000001/draft \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{ "system_prompt": "…", "expected_version": 10 }'Si alguien guardó después de tu lectura, la respuesta es 409 y no se escribe nada:
{
"error": {
"code": "VERSION_CONFLICT",
"message": "The draft changed since you read it — someone else saved first. Nothing was written.",
"conflict": {
"resource": "ai_agent.draft",
"reason": "stale_version",
"expected_version": 10,
"current_version": 11,
"changed_fields": ["system_prompt"],
"read_path": "/api/v1/ai-agents/a3a3a3a3-0000-4000-8000-000000000001/draft",
"hint": "GET the draft, merge your edit onto it, and retry with the new draft_version."
}
}
}Vuelve a leer, junta tu cambio con el del otro y reintenta con la versión nueva.
El publish tiene un segundo freno. Si el borrador se empezó sobre una configuración en vivo que después cambió (alguien usó PUT /ai-agents/{id}/system-prompt mientras el borrador esperaba), publicarlo borraría ese cambio, así que responde 409 con reason: "draft_base_stale". Para publicar igual, después de revisar, manda expected_config_version con la versión en vivo actual. También puedes descartar el borrador y armarlo de nuevo.
Mandar la versión es opcional por compatibilidad: una escritura sin ella sigue funcionando igual que antes y queda registrada. Pero sin ella no hay protección.
Cada edición de un skill deja una fila inmutable en GET /skills/{id}/versions, con el contenido completo, quién lo escribió (usuario o clave API), por dónde (rest, mcp, coach…) y cuándo. Y cada respuesta del agente registra qué versión la produjo: GET /conversations/{id}/agent-runs trae ai_agent_version y las versiones de skills y archivos que usó ese turno.
POST /ai-agents/{id}/versions/{version}/restore carga una versión pasada al borrador, para revisarla antes de publicar. POST /ai-agents/{id}/versions/{version}/rollback hace las dos cosas de una vez: carga y publica, y deja esa versión en vivo de inmediato.
Skills
Un skill es un playbook reutilizable que se adjunta a uno o más agentes: «cómo ofrecer horarios», «cómo pedir el número de referencia antes de confirmar». Vive en una biblioteca del workspace y no dentro de un agente en particular.
curl -X PUT https://api.vitrinadev.com/api/v1/skills/a4a4a4a4-0000-4000-8000-000000000001 \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Cuando el cliente quiera agendar, ofrece SOLO horarios de la sucursal más cercana y confirma antes de reservar."
}'Editar un skill es solo contenido y aplica al instante. No hay borrador ni publish para un skill: el cambio llega a cada agente que lo tiene adjunto en su próximo turno. Manda expected_version (ver Dos editores a la vez); cada edición queda en el historial GET /skills/{id}/versions.
POST /ai-agents/{id}/skills adjunta uno que ya existe y DELETE /ai-agents/{id}/skills/{skillId} lo desadjunta sin borrarlo. DELETE /skills/{id} sí lo borra, con un borrado suave. Sale de la biblioteca y de todo agente al instante.
Herramientas
GET /ai-agents/tools-catalog lista lo que este workspace puede wirear hoy, filtrado por rubro e integraciones conectadas. PUT /ai-agents/{id}/tools reemplaza el set completo con tool_names, y aplica en vivo, no al borrador: el próximo turno del agente ya ve el cambio.
Herramientas personalizadas
Cuando la API que el agente necesita llamar es la del propio workspace, no hace falta escribir código. El Tool Store enseña el llamado como una plantilla.
curl -X POST https://api.vitrinadev.com/api/v1/custom-tools \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "consultar_clima",
"description": "Consulta el clima actual de una ciudad",
"parameters": [
{ "name": "ciudad", "type": "string", "description": "Nombre de la ciudad", "required": true }
],
"request_template": {
"method": "GET",
"url": "https://api.example.com/weather?city=${param.ciudad}"
}
}'Los valores de los parámetros se interpolan en la URL, los headers o el body como ${param.NOMBRE}. Una credencial se referencia como ${secret.NOMBRE} y nunca se escribe en la plantilla. POST /custom-tools/{id}/test hace la llamada real, porque no hay modo sandbox, y guarda cada invocación redactada para auditoría.
Cuando la API de destino pide autenticación, la credencial vive aparte, en /tool-credentials:
curl -X POST https://api.vitrinadev.com/api/v1/tool-credentials \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "weather_api_key", "kind": "api_key", "value": "wapi_live_abc123wx91" }'{
"data": {
"id": "a7a7a7a7-0000-4000-8000-000000000002",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"name": "weather_api_key",
"kind": "api_key",
"value_preview": "••••wx91",
"metadata": {},
"created_by": "11111111-0000-4000-8000-000000000001",
"created_at": "2026-09-12T10:00:00.000Z",
"updated_at": "2026-09-12T10:00:00.000Z",
"last_used_at": "2026-09-21T18:04:11.000Z",
"rotation_grace_until": null
}
}El valor en texto plano nunca vuelve a mostrarse. Cada lectura trae solo value_preview, unos caracteres enmascarados, igual que una API key. Rotar es un PATCH con un value nuevo. El valor viejo sigue funcionando 24 horas más, así una llamada en curso no se rompe a mitad de la rotación.
Base de conocimiento
Un agente cita lo que tiene adjunto y no toda la biblioteca del workspace:
curl https://api.vitrinadev.com/api/v1/ai-agents/a3a3a3a3-0000-4000-8000-000000000001/knowledge \
-H "Authorization: Bearer $VITRINA_KEY"POST /ai-agents/{id}/knowledge sube un archivo nuevo directo al agente, en multipart, o adjunta uno que ya existe en la biblioteca con { "kb_file_id": "…" }. DELETE /ai-agents/{id}/knowledge/{fileId} desadjunta, y el archivo sigue existiendo y sirviendo a cualquier otro agente. Cómo se sube, genera y reemplaza un archivo está en Base de conocimiento.
Canal de voz
curl https://api.vitrinadev.com/api/v1/ai-agents/a3a3a3a3-0000-4000-8000-000000000001/voice-channel \
-H "Authorization: Bearer $VITRINA_KEY"Trae el estado de la línea, el saludo, la voz elegida de un catálogo curado y el número de derivación a un humano. Del saludo solo se edita el segmento de marca. El aviso de que es IA y de que la llamada se graba lo compone el servidor, y no se puede quitar. voice_id es una clave de ese catálogo y no un id propio del workspace.
PUT guarda el cambio y lo empuja a la capa de voz. Si el empuje falla queda pendiente, y POST /ai-agents/{id}/voice-channel/propagate reintenta.
Solicitudes: cuando un cliente pide un cambio
Una Solicitud (agent_change_request) es un requerimiento que alguien planteó con sus propias palabras: «no uses guiones», «si algo no está disponible, dile que lo vamos a conseguir». Es distinta de un hallazgo que la plataforma detectó sola.
curl -X POST https://api.vitrinadev.com/api/v1/ai-agents/a3a3a3a3-0000-4000-8000-000000000001/change-requests \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{ "verbatim": "No uses guiones largos, suena robótico.", "reporter_kind": "client_via_member" }'También se puede archivar como audio (multipart/form-data, campo file). Se transcribe, y la transcripción es el verbatim, así que nada queda guardado que no se pudiera convertir a texto.
El ciclo de vida:
| Estado | Qué significa |
|---|---|
received | Se registró lo que dijo el cliente |
grounded | Se confirmó en qué conversación pasó |
reproduced | Un escenario reprodujo el problema contra la versión reclamada |
proposed | Se propuso un arreglo |
applied | El arreglo se aplicó |
verified | Se confirmó que el arreglo funciona |
ya_cumple | El agente ya lo hacía bien y la queja era sobre una versión vieja |
harness | El arreglo es de la plataforma y no del prompt de este agente |
POST .../change-requests/{crId}/ground busca en qué conversaciones pasó, sin tocar nada. POST .../ground/confirm es la respuesta humana a esa búsqueda. POST .../scenario autoría un escenario de prueba desde la solicitud y lo corre contra la versión reclamada. POST .../propose investiga y guarda una propuesta de arreglo, una vez que el escenario reprodujo el problema en rojo.
Los eventos
| Evento | Cuándo |
|---|---|
ai_agent.publish | Un borrador se publicó como configuración en vivo |
skill.created / .updated / .deleted | Cambió la biblioteca de skills |
custom_tool.created / .updated / .deleted | Cambió el Tool Store |
ai_agent.publish lleva ai_agent_id, name y updated_at, lo justo para saber qué agente cambió y cuándo. El contenido de la publicación se lee con GET /ai-agents/{id}/versions.