Tema
HTTP sin streaming
Un turno de agente desde tu backend: mandas un mensaje, esperas, y recibes la respuesta completa de una vez. Sin SSE y sin navegador.
Es la vía para procesos batch, webhooks de otros sistemas o bots de mensajería — cualquier cosa que no sea la pantalla de un usuario final.
No está en el OpenAPI
A diferencia del resto de /api/*, estos dos endpoints no aparecen en /api/openapi.json ni en el navegador de /api/docs de tu instancia. Esta página es su referencia.
Diferencias con el canal público
/embed/* (widget y SDK) | POST /api/agents/…/invoke | |
|---|---|---|
| Autenticación | Ninguna | Access token |
| Respuesta | Streaming, token a token | Completa, de una vez |
| Identidad | La genera el navegador | La eliges tú (subjectId) |
| Desde el navegador | Sí | No — no hay CORS |
| Errores del modelo | Mensaje de catálogo | El error real |
| Hilos | Canal widget / js_sdk | Canal api, separado |
Ese último punto importa: las conversaciones que abras por aquí no comparten hilo con las del widget o el SDK, aunque uses el mismo subjectId.
GET /api/agents/{identifier}
Comprueba que el agente existe y que tu token puede usarlo.
bash
curl https://ur.tuempresa.com/api/agents/soporte \
-H "Authorization: Bearer $UR_TOKEN"json
{ "name": "Soporte", "identifier": "soporte" }Aquí va el slug, no el UUID
En el widget y el SDK puedes usar indistintamente el identifier o el id del agente. En estos dos endpoints solo se resuelve por identifier.
POST /api/agents/{identifier}/invoke
bash
curl -X POST https://ur.tuempresa.com/api/agents/soporte/invoke \
-H "Authorization: Bearer $UR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"message": "¿Cuál es el plazo de devolución?",
"subjectId": "usuario-4821"
}'Cuerpo
| Campo | Tipo | Descripción |
|---|---|---|
message | string | Obligatorio. No puede estar vacío. |
subjectId | string | Obligatorio. Identidad estable de tu usuario final. |
subjectId es lo que da memoria a la conversación
Es lo que permite reanudar el hilo entre llamadas: manda el mismo valor y el agente recuerda lo hablado; manda uno nuevo y empieza de cero. Lo natural es el id de usuario de tu sistema.
No es opcional a propósito — sin él no habría forma de continuar una conversación.
Respuesta
json
{
"agent": "soporte",
"threadId": "01931f2c-...",
"response": [{ "type": "text", "text": "El plazo de devolución es de 30 días..." }],
"toolCalls": [
{ "name": "documentacion", "args": { "query": "devoluciones" }, "result": "..." }
]
}| Campo | Descripción |
|---|---|
agent | El identifier del agente |
threadId | Id del hilo. El mismo subjectId devuelve siempre el mismo |
response | Bloques de contenido de la respuesta. El texto está en los de type: "text" |
toolCalls | Tools invocadas en este turno, con sus argumentos y su resultado |
notice | Solo en el primer mensaje de un hilo, si el agente tiene mensaje automático |
Cuando el mensaje se bloquea
Un mensaje filtrado por seguridad devuelve 200, no un error, con un campo extra:
json
{
"agent": "soporte",
"threadId": "01931f2c-...",
"response": [{ "type": "text", "text": "No puedo ayudarte con eso." }],
"toolCalls": [],
"security": { "blocked": true, "checkType": "forbidden_words" }
}Esta es la única vía donde puedes distinguir un bloqueo programáticamente: por el canal público es indistinguible de una respuesta normal, a propósito.
Errores
| Código | Cuándo |
|---|---|
400 | Falta message o subjectId, JSON mal formado, o el agente no tiene modelo configurado |
401 | Token ausente o inválido |
403 | El token no tiene permiso sobre este agente |
404 | No existe ningún agente con ese identifier |
429 | Superado el límite de 60 peticiones por minuto |
502 | Falló el modelo |
Aquí sí ves el error real
Al contrario que el canal público —que devuelve siempre un mensaje de catálogo para no filtrar detalles internos—, este canal es para desarrolladores y el 502 trae el mensaje de error real del proveedor. La excepción es cuando el proveedor está saturado: ahí se usa el mensaje del catálogo.
Los límites por agente también aplican
Recorte de longitud, límite por hora, ráfaga y filtros de contenido se aplican igual que en el canal público. Aquí la identidad para contar es el subjectId que tú mandas.
Ver Seguridad y límites.