VitrinaAPI

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.

Video · 1:19Nadie se queda sin respuesta

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):

RecursoSe lee enCampoSe devuelve en
BorradorGET /ai-agents/{id}/draftdraft_versionexpected_version en PUT /ai-agents/{id}/draft, expected_draft_version en POST /ai-agents/{id}/publish
Agente en vivoGET /ai-agents/{id} o GET /ai-agents/{id}/system-promptconfig_versionexpected_version en PUT /ai-agents/{id}/system-prompt, expected_config_version en POST /ai-agents/{id}/publish
SkillGET /skills/{id}versionexpected_version en PUT /skills/{id}
Archivo de conocimientoGET /kb-filesversioncampo 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:

EstadoQué significa
receivedSe registró lo que dijo el cliente
groundedSe confirmó en qué conversación pasó
reproducedUn escenario reprodujo el problema contra la versión reclamada
proposedSe propuso un arreglo
appliedEl arreglo se aplicó
verifiedSe confirmó que el arreglo funciona
ya_cumpleEl agente ya lo hacía bien y la queja era sobre una versión vieja
harnessEl 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

EventoCuándo
ai_agent.publishUn borrador se publicó como configuración en vivo
skill.created / .updated / .deletedCambió la biblioteca de skills
custom_tool.created / .updated / .deletedCambió 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.

En esta página