Referencia

Chat

Conversaciones, turnos, streaming e interacciones. Es el corazón de la API.

Listar chats

GETapi.niucore.com/api/v1/chats

Scope: chat:read

Devuelve los chats de esta credencial, del más reciente al más antiguo.

Namespace por credencial

Un chat creado desde la aplicación, o por otra credencial del mismo usuario, no aparece acá. En la aplicación pasa lo contrario: los chats de la credencial se ven, se leen y se pueden continuar desde la UI.

Consulta

is_favoriteboolean

Filtra por marcados como favoritos.

pageintegerpor defecto 1

Página, empezando en 1.

sizeintegerpor defecto 50

Elementos por página. Máximo 200.

Devuelve

idstring (uuid)

Identificador del chat.

namestring | null

Título de la conversación.

is_favoriteboolean

Marcado como favorito.

permission_modestring

manual o auto.

model_idinteger | null

Modelo por defecto del chat.

created_atstring (date-time)

Creación.

updated_atstring (date-time)

Última actividad.

curl -sS -X GET 'https://api.niucore.com/api/v1/chats?page=1&size=20' \
  -H "Authorization: Bearer $NIUCORE_ACCESS_TOKEN"
Respuesta
{
  "status": "success",
  "message": "Operación exitosa",
  "message_code": "api_v1_ok",
  "data": [
    {
      "id": "3f6b1a90-6c2d-4d84-a0b1-9d0f5e7c2ab3",
      "name": "Soporte — ticket 8842",
      "is_favorite": false,
      "permission_mode": "auto",
      "model_id": 17,
      "created_at": "2026-09-01T14:02:11Z",
      "updated_at": "2026-09-01T14:09:44Z"
    }
  ],
  "meta": {
    "request_id": "9f1c0a3e-7b2d-4f18-9a55-2c6e0d1b4a77",
    "page": 1,
    "size": 20,
    "total": 1
  }
}

Crear un chat

POSTapi.niucore.com/api/v1/chats

Scope: chat:write

Crea un chat vacío y devuelve su id.

Sugerencia

No es obligatorio: enviar un turno a un chat_id que no existe también lo crea. Este endpoint existe para el integrador que necesita el id antes de tener el primer mensaje.

Cuerpo

namestring

Título. Máximo 200 caracteres.

model_idinteger

Modelo por defecto. Los ids válidos salen de GET /chat/catalog.

permission_modestringpor defecto manual

Modo por defecto del chat. Cada turno puede pedir el suyo.

manualauto

curl -sS -X POST 'https://api.niucore.com/api/v1/chats' \
  -H "Authorization: Bearer $NIUCORE_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Soporte — ticket 8842",
    "permission_mode": "auto"
  }'
Respuesta
{
  "status": "success",
  "message": "Operación exitosa",
  "message_code": "api_v1_ok",
  "data": {
    "id": "3f6b1a90-6c2d-4d84-a0b1-9d0f5e7c2ab3",
    "name": "Soporte — ticket 8842",
    "is_favorite": false,
    "permission_mode": "auto",
    "model_id": 17,
    "created_at": "2026-09-01T14:02:11Z",
    "updated_at": "2026-09-01T14:09:44Z"
  },
  "meta": {
    "request_id": "9f1c0a3e-7b2d-4f18-9a55-2c6e0d1b4a77"
  }
}

Ver un chat

GETapi.niucore.com/api/v1/chats/{chat_id}

Scope: chat:read

Devuelve un chat de la credencial.

Ruta

chat_idstring (uuid)obligatorio

Identificador del chat.

Errores

  • 404api_v1_not_foundEl chat no existe o es de otra credencial. Los dos casos responden igual.
curl -sS -X GET 'https://api.niucore.com/api/v1/chats/3f6b1a90-6c2d-4d84-a0b1-9d0f5e7c2ab3' \
  -H "Authorization: Bearer $NIUCORE_ACCESS_TOKEN"
Respuesta
{
  "status": "success",
  "message": "Operación exitosa",
  "message_code": "api_v1_ok",
  "data": {
    "id": "3f6b1a90-6c2d-4d84-a0b1-9d0f5e7c2ab3",
    "name": "Soporte — ticket 8842",
    "is_favorite": false,
    "permission_mode": "auto",
    "model_id": 17,
    "created_at": "2026-09-01T14:02:11Z",
    "updated_at": "2026-09-01T14:09:44Z"
  },
  "meta": {
    "request_id": "9f1c0a3e-7b2d-4f18-9a55-2c6e0d1b4a77"
  }
}

Renombrar o marcar favorito

PATCHapi.niucore.com/api/v1/chats/{chat_id}

Scope: chat:write

Actualiza el nombre y la marca de favorito. El modelo y el modo de permisos no se editan acá: viajan por turno.

Ruta

chat_idstring (uuid)obligatorio

Identificador del chat.

Cuerpo

namestring

Nuevo título.

is_favoriteboolean

Marca o desmarca el favorito.

curl -sS -X PATCH 'https://api.niucore.com/api/v1/chats/3f6b1a90-6c2d-4d84-a0b1-9d0f5e7c2ab3' \
  -H "Authorization: Bearer $NIUCORE_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Soporte — ticket 8842 (resuelto)",
    "is_favorite": true
  }'
Respuesta
{
  "status": "success",
  "message": "Operación exitosa",
  "message_code": "api_v1_ok",
  "data": {
    "id": "3f6b1a90-6c2d-4d84-a0b1-9d0f5e7c2ab3",
    "name": "Soporte — ticket 8842 (resuelto)",
    "is_favorite": true,
    "permission_mode": "auto",
    "model_id": 17,
    "created_at": "2026-09-01T14:02:11Z",
    "updated_at": "2026-09-01T14:09:44Z"
  },
  "meta": {
    "request_id": "9f1c0a3e-7b2d-4f18-9a55-2c6e0d1b4a77"
  }
}

Borrar un chat

DELETEapi.niucore.com/api/v1/chats/{chat_id}

Scope: chat:write

Borrado lógico, igual que en la aplicación: el chat desaparece de los dos lados.

Atención

Los mensajes borrados siguen contando para el consumo ya facturado. Borrar una conversación no borra lo que costó.

Ruta

chat_idstring (uuid)obligatorio

Identificador del chat.

curl -sS -X DELETE 'https://api.niucore.com/api/v1/chats/3f6b1a90-6c2d-4d84-a0b1-9d0f5e7c2ab3' \
  -H "Authorization: Bearer $NIUCORE_ACCESS_TOKEN"
Respuesta
{
  "status": "success",
  "message": "Operación exitosa",
  "message_code": "api_v1_ok",
  "data": {
    "id": "3f6b1a90-6c2d-4d84-a0b1-9d0f5e7c2ab3",
    "deleted": true
  },
  "meta": {
    "request_id": "9f1c0a3e-7b2d-4f18-9a55-2c6e0d1b4a77"
  }
}

Listar mensajes

GETapi.niucore.com/api/v1/chats/{chat_id}/messages

Scope: chat:read

Devuelve los turnos del chat en orden cronológico. Cada elemento trae prompt (lo enviado) y content (lo respondido).

Ruta

chat_idstring (uuid)obligatorio

Identificador del chat.

Consulta

pageintegerpor defecto 1

Página, empezando en 1.

sizeintegerpor defecto 50

Elementos por página. Máximo 200.

Devuelve

idinteger

Identificador del turno.

chat_idstring (uuid)

Chat al que pertenece.

rolestring

Siempre assistant.

contentstring

Respuesta del modelo.

promptstring

Lo que se envió. Solo en el listado de mensajes.

statusstring

completed, cancelled, error o pending. En el historial, un turno de la API que cerró en message.cancelled se lee cancelled.

usageobject

Consumo del turno: prompt_tokens, completion_tokens, total_tokens, reasoning_tokens (enteros) más model y provider. Es null si el turno no llegó a consumir nada. La forma es idéntica en el stream, en la respuesta sincrónica, en el replay y acá.

stepsarray

Pasos de razonamiento y herramientas ejecutadas: { key, label, label_code, status, timestamp, duration?, detail?, tokens?, tool_calls? }. Informativo — la forma puede crecer, así que leé los campos que te importan e ignorá el resto.

sourcesarray

Fragmentos de bibliotecas usados como contexto.

anonymizationobject | null

Reemplazos aplicados por el modo incógnito, si la empresa lo tiene activo.

metadataobject | null

El metadata que mandó el integrador en el turno, devuelto tal cual.

created_atstring (date-time)

Fecha de creación.

curl -sS -X GET 'https://api.niucore.com/api/v1/chats/3f6b1a90-6c2d-4d84-a0b1-9d0f5e7c2ab3/messages?page=1&size=50' \
  -H "Authorization: Bearer $NIUCORE_ACCESS_TOKEN"
Respuesta
{
  "status": "success",
  "message": "Operación exitosa",
  "message_code": "api_v1_ok",
  "data": [
    {
      "id": 90211,
      "chat_id": "3f6b1a90-6c2d-4d84-a0b1-9d0f5e7c2ab3",
      "role": "assistant",
      "content": "El pedido 8842 se despachó el 28 de agosto y llegó el 1 de septiembre.",
      "prompt": "¿Qué pasó con el pedido 8842?",
      "status": "completed",
      "usage": {
        "prompt_tokens": 1840,
        "completion_tokens": 96,
        "total_tokens": 1936,
        "reasoning_tokens": 0,
        "provider": "OPENAI",
        "model": "gpt-4o"
      },
      "steps": [],
      "sources": [],
      "anonymization": null,
      "metadata": {
        "ticket": "8842"
      },
      "created_at": "2026-09-01T14:09:44Z"
    }
  ],
  "meta": {
    "request_id": "9f1c0a3e-7b2d-4f18-9a55-2c6e0d1b4a77",
    "page": 1,
    "size": 50,
    "total": 1
  }
}

Enviar un turno

POSTapi.niucore.com/api/v1/chats/{chat_id}/messages

Scope: chat:write

Envía un mensaje y bloquea hasta la respuesta completa. Solo admite permission_mode: "auto".

Idempotency-Key obligatorio

Sin el header el request se rechaza con 400 api_v1_idempotency_key_required. Nunca se genera uno por vos: eso convertiría cada reintento en un turno nuevo — y en un cobro nuevo.

Un turno puede durar minutos. El servidor lo corta a los 30 minutos con 504; poné el timeout de tu cliente HTTP por encima de eso o usá el envío por stream.

Con manual el turno se para a pedir permiso, y por este canal no hay forma de contestarle: por eso se rechaza con 409 en vez de dejar al cliente esperando algo que nunca llega. Para modo manual, usá el stream.

Ruta

chat_idstring (uuid)obligatorio

Identificador del chat. Si no existe, se crea con ese id.

Encabezados

Idempotency-Keystringobligatorio

Identificador único del intento. Reintentar con la misma key devuelve el mismo resultado sin reejecutar el turno.

Cuerpo

contentstringobligatorio

El mensaje. Máximo 64.000 caracteres.

model_idinteger

Modelo a usar en este turno. Sin él, el del chat o el de la empresa.

permission_modestringpor defecto manual

En este endpoint tiene que ser auto.

auto

attachmentsobject

Recursos que el turno adjunta. Cada tipo exige además el scope de lectura de su recurso.

workspace_idsinteger[]

Bibliotecas. Exige libraries:read.

file_idsinteger[]

Archivos. Exige libraries:read.

skill_idsinteger[]

Habilidades. Exige skills:read.

mcp_server_idsinteger[]

Conectores. Exige connectors:use.

metadataobject

Datos propios para correlacionar de tu lado. Se devuelven tal cual y no llegan al modelo. Máximo 2048 bytes serializados.

Devuelve

idinteger

Identificador del turno.

chat_idstring (uuid)

Chat al que pertenece.

rolestring

Siempre assistant.

contentstring

Respuesta del modelo.

promptstring

Lo que se envió. Solo en el listado de mensajes.

statusstring

completed, cancelled, error o pending. En el historial, un turno de la API que cerró en message.cancelled se lee cancelled.

usageobject

Consumo del turno: prompt_tokens, completion_tokens, total_tokens, reasoning_tokens (enteros) más model y provider. Es null si el turno no llegó a consumir nada. La forma es idéntica en el stream, en la respuesta sincrónica, en el replay y acá.

stepsarray

Pasos de razonamiento y herramientas ejecutadas: { key, label, label_code, status, timestamp, duration?, detail?, tokens?, tool_calls? }. Informativo — la forma puede crecer, así que leé los campos que te importan e ignorá el resto.

sourcesarray

Fragmentos de bibliotecas usados como contexto.

anonymizationobject | null

Reemplazos aplicados por el modo incógnito, si la empresa lo tiene activo.

metadataobject | null

El metadata que mandó el integrador en el turno, devuelto tal cual.

created_atstring (date-time)

Fecha de creación.

Errores

  • 400api_v1_idempotency_key_requiredFalta el header Idempotency-Key.
  • 403api_token_scope_not_allowedSe adjuntó un recurso sin el scope de lectura correspondiente. data.required_scopes dice cuáles faltan.
  • 409api_v1_sync_requires_autopermission_mode no es auto.
  • 409api_v1_idempotency_conflictLa misma key se usó antes con otro cuerpo.
  • 409api_v1_turn_in_progressEl turno de esa key todavía corre.
  • 409api_v1_chat_busyEse chat ya tiene un turno en curso, venga de la API o de la aplicación. data.message_id identifica cuál.
  • 422api_v1_content_too_longcontent supera el máximo.
  • 422api_v1_too_many_attachmentsDemasiados adjuntos.
  • 502upstream_errorEl turno falló del otro lado. El message_code es el código del turno (upstream_error u otro que venga del modelo) y data trae chat_id y message_id. Es el mismo cuerpo que te devuelve el replay de la key.
  • 502interaction_in_auto_modeEl turno pidió una interacción (por ejemplo, conectar un conector) que el envío sincrónico no puede contestar, y se cortó. Usá el stream.
  • 504turn_timeoutEl turno superó su tiempo máximo y se cortó. data trae chat_id y message_id.
  • 503api_v1_core_unavailableEl turno terminó pero no se pudo confirmar su guardado (data.code: "persistence_failed"). Consultá el estado con la misma key antes de reintentar.
  • 409api_v1_core_unavailableOtra ejecución cerró este turno (data.code: "idempotency_outcome_unknown"). Repetí el request con la misma key para leer el resultado que quedó.
curl -sS -X POST 'https://api.niucore.com/api/v1/chats/3f6b1a90-6c2d-4d84-a0b1-9d0f5e7c2ab3/messages' \
  -H "Authorization: Bearer $NIUCORE_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 8f0a1c72-5c9e-4a1b-8f31-2d0b7c6e4a95" \
  -d '{
    "content": "Resumí el estado del pedido 8842 y decime si hay reclamos abiertos.",
    "permission_mode": "auto",
    "attachments": {
      "workspace_ids": [
        31
      ]
    },
    "metadata": {
      "ticket": "8842"
    }
  }'
Respuesta
{
  "status": "success",
  "message": "Operación exitosa",
  "message_code": "api_v1_ok",
  "data": {
    "id": 90212,
    "chat_id": "3f6b1a90-6c2d-4d84-a0b1-9d0f5e7c2ab3",
    "role": "assistant",
    "content": "El pedido 8842 se despachó el 28 de agosto…",
    "status": "completed",
    "usage": {
      "prompt_tokens": 2410,
      "completion_tokens": 188,
      "total_tokens": 2598,
      "reasoning_tokens": 0,
      "provider": "OPENAI",
      "model": "gpt-4o"
    },
    "steps": [
      {
        "key": "search_documents",
        "label": "Buscando en tus bibliotecas",
        "label_code": "step.tool.running",
        "status": "completed",
        "timestamp": "2026-09-01T14:09:41Z",
        "duration": 1240
      }
    ],
    "sources": [
      {
        "workspace_id": 31,
        "file_id": 902,
        "score": 0.83
      }
    ],
    "anonymization": null,
    "metadata": {
      "ticket": "8842"
    }
  },
  "meta": {
    "request_id": "9f1c0a3e-7b2d-4f18-9a55-2c6e0d1b4a77"
  }
}

Enviar un turno (stream)

POSTapi.niucore.com/api/v1/chats/{chat_id}/messages/stream

Scope: chat:write

Igual que el envío sincrónico, pero devuelve Server-Sent Events a medida que el turno avanza. Es el único camino para permission_mode: "manual".

La respuesta es text/event-stream. Cada evento trae event: y data: con un JSON. El detalle de cada tipo está en Eventos del stream.

Nota

El turno termina igual aunque el cliente se desconecte: el mensaje se persiste y el resultado queda guardado bajo la Idempotency-Key. Volver a enviar la misma key reentrega ese resultado sin reejecutar nada.

Ruta

chat_idstring (uuid)obligatorio

Identificador del chat. Si no existe, se crea.

Encabezados

Idempotency-Keystringobligatorio

Identificador único del intento.

Cuerpo

contentstringobligatorio

El mensaje.

model_idinteger

Modelo del turno.

permission_modestringpor defecto manual

manual hace que el turno pida permiso antes de ejecutar herramientas.

manualauto

attachmentsobject

Igual que en el envío sincrónico.

metadataobject

Datos propios.

Errores

  • 409api_v1_chat_busyEse chat ya tiene un turno en curso, venga de la API o de la aplicación.
  • 409api_v1_idempotency_conflictLa key se usó con otro cuerpo.
curl -sS -N -X POST 'https://api.niucore.com/api/v1/chats/3f6b1a90-6c2d-4d84-a0b1-9d0f5e7c2ab3/messages/stream' \
  -H "Authorization: Bearer $NIUCORE_ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -H 'Accept: text/event-stream' \
  -H 'Idempotency-Key: 8f0a1c72-5c9e-4a1b-8f31-2d0b7c6e4a95' \
  -d '{
    "content": "Resumí el estado del pedido 8842.",
    "permission_mode": "manual",
    "attachments": { "workspace_ids": [31] }
  }'
Respuesta
event: message.start
data: {"chat_id":"3f6b1a90-6c2d-4d84-a0b1-9d0f5e7c2ab3","message_id":90213,"request_id":"9f1c0a3e-7b2d-4f18-9a55-2c6e0d1b4a77"}

event: message.delta
data: {"text":"El pedido 8842","replace":false}

event: message.delta
data: {"text":" se despachó el 28 de agosto.","replace":false}

event: message.step
data: {"steps":[{"key":"search_documents","label":"Buscando en tus bibliotecas","label_code":"step.tool.running","status":"completed","timestamp":"2026-09-01T14:09:41Z","duration":1240}]}

event: ping
data: {"ts":1767222015.42}

event: message.completed
data: {"id":90213,"chat_id":"3f6b1a90-6c2d-4d84-a0b1-9d0f5e7c2ab3","role":"assistant","content":"El pedido 8842 se despachó el 28 de agosto.","usage":{"prompt_tokens":2410,"completion_tokens":188,"total_tokens":2598,"provider":"OPENAI","model":"gpt-4o"},"steps":[],"sources":[],"anonymization":null,"metadata":{"ticket":"8842"}}

Catálogo del chat

GETapi.niucore.com/api/v1/chat/catalog

Scope: chat:read

Qué puede usar un turno de esta credencial: modelos, bibliotecas, habilidades y conectores, ya recortados por los permisos del usuario y por el alcance de la credencial.

Nota

Los bloques son condicionales por scope: una credencial sin libraries:read no recibe la clave libraries — no la recibe vacía, no la recibe. Una lista vacía se leería como «no hay ninguna», y eso sería falso.

Sugerencia

Los modelos que devuelve son solo los de tipo COMPLETION. Un modelo de embedding nunca aparece acá: mandarlo como model_id produciría un turno que falla del otro lado.

Devuelve

modelsarray

Modelos habilitados (id, name, code, type).

permission_modesstring[]

Siempre ["manual", "auto"].

librariesarray

Solo con libraries:read.

skillsarray

Solo con skills:read.

connectorsarray

Solo con connectors:use.

curl -sS -X GET 'https://api.niucore.com/api/v1/chat/catalog' \
  -H "Authorization: Bearer $NIUCORE_ACCESS_TOKEN"
Respuesta
{
  "status": "success",
  "message": "Operación exitosa",
  "message_code": "api_v1_ok",
  "data": {
    "models": [
      {
        "id": 17,
        "name": "GPT-4o",
        "code": "gpt-4o",
        "type": "COMPLETION"
      },
      {
        "id": 23,
        "name": "Claude Sonnet",
        "code": "claude-sonnet-4",
        "type": "COMPLETION"
      }
    ],
    "permission_modes": [
      "manual",
      "auto"
    ],
    "libraries": [
      {
        "id": 31,
        "name": "Procedimientos de soporte"
      }
    ],
    "skills": [
      {
        "id": 147,
        "name": "Redactar respuesta a cliente"
      }
    ],
    "connectors": [
      {
        "id": 8,
        "name": "gmail",
        "display_name": "Gmail"
      }
    ]
  },
  "meta": {
    "request_id": "9f1c0a3e-7b2d-4f18-9a55-2c6e0d1b4a77"
  }
}

Aprobar herramientas

POSTapi.niucore.com/api/v1/chats/{chat_id}/actions/approve-tool

Scope: chat:write

Responde a un evento message.action_required de tipo tool_approval: una decisión por cada herramienta que el turno pidió ejecutar.

Los tool_use_id salen de payload.tools, donde cada entrada es { tool_use_id, name, server, input }. El action_id es el que vino en el evento: es opaco y solo sirve para ese turno y esa credencial.

Atención

Una interacción se resuelve una sola vez y vence a los 110 segundos. Un segundo intento devuelve 409.

Ruta

chat_idstring (uuid)obligatorio

El chat del turno.

Cuerpo

action_idstringobligatorio

El action_id del evento.

decisionsobject[]obligatorio

Una decisión por herramienta. Mínimo una.

tool_use_idstringobligatorio

Id de la herramienta, del payload.

approvedbooleanobligatorio

true la ejecuta, false la salta.

Errores

  • 404api_v1_action_not_foundLa interacción no existe, venció o no es de esta credencial.
  • 409api_v1_action_not_pendingYa se resolvió, otro request la reclamó primero, o su resultado quedó desconocido. No la reintentes.
  • 409api_v1_action_result_unknownEl servicio de conversación recibió la decisión y la rechazó (data.upstream_status). La interacción queda cerrada: consultá el turno.
  • 503api_v1_service_unavailableLa decisión no llegó a salir (meta.error_details.retry_action: "retry"). La interacción vuelve a pending: reintentar es seguro.
  • 503api_v1_action_result_unknownLa decisión salió y no hubo respuesta: no se sabe si se aplicó (retry_action: "check_status"). No la reenvíes; seguí el stream o listá los mensajes.
  • 503api_v1_interaction_backend_unavailableNo se pueden resolver interacciones ahora. No es un 404: es una indisponibilidad, y reintentar tiene sentido.
curl -sS -X POST 'https://api.niucore.com/api/v1/chats/3f6b1a90-6c2d-4d84-a0b1-9d0f5e7c2ab3/actions/approve-tool' \
  -H "Authorization: Bearer $NIUCORE_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "action_id": "act_9c2f1e7a4b6d",
    "decisions": [
      {
        "tool_use_id": "toolu_01A9f",
        "approved": true
      },
      {
        "tool_use_id": "toolu_01B3k",
        "approved": false
      }
    ]
  }'
Respuesta
{
  "status": "success",
  "message": "Operación exitosa",
  "message_code": "api_v1_ok",
  "data": {
    "action_id": "act_9c2f1e7a4b6d",
    "state": "resolved"
  },
  "meta": {
    "request_id": "9f1c0a3e-7b2d-4f18-9a55-2c6e0d1b4a77"
  }
}

Responder un plan

POSTapi.niucore.com/api/v1/chats/{chat_id}/actions/respond-plan

Scope: chat:write

Aprueba o rechaza el plan que propuso el turno (kind: "plan_approval").

Cuándo llega. No hace falta un modo especial: un turno en manual o en auto emite plan_approval cuando el modelo decide llamar a su herramienta de planificación antes de actuar. Pedirlo en el mensaje —«antes de hacer nada proponé un plan de pasos y esperá mi aprobación»— lo hace más probable, pero no lo garantiza: la decisión es del modelo y depende de cuál esté configurado. Tratá esta interacción como una que puede aparecer, no como una que podés forzar. Lo que NO existe en v1 es el permission_mode: "plan" de la aplicación.

El plan viaja en payload.plan: { title, context, steps, notes }, donde cada paso es { step, action, tool? }. tool solo aparece si ese paso va a usar una herramienta.

Ruta

chat_idstring (uuid)obligatorio

El chat del turno.

Cuerpo

action_idstringobligatorio

El action_id del evento.

actionstringobligatorio

La decisión.

approvereject

Errores

  • 404api_v1_action_not_foundLa interacción no existe, venció o no es de esta credencial.
  • 409api_v1_action_not_pendingYa se resolvió, otro request la reclamó primero, o su resultado quedó desconocido. No la reintentes.
  • 409api_v1_action_result_unknownEl servicio de conversación recibió la decisión y la rechazó (data.upstream_status). La interacción queda cerrada: consultá el turno.
  • 503api_v1_service_unavailableLa decisión no llegó a salir (meta.error_details.retry_action: "retry"). La interacción vuelve a pending: reintentar es seguro.
  • 503api_v1_action_result_unknownLa decisión salió y no hubo respuesta: no se sabe si se aplicó (retry_action: "check_status"). No la reenvíes; seguí el stream o listá los mensajes.
  • 503api_v1_interaction_backend_unavailableNo se pueden resolver interacciones ahora. No es un 404: es una indisponibilidad, y reintentar tiene sentido.
curl -sS -X POST 'https://api.niucore.com/api/v1/chats/3f6b1a90-6c2d-4d84-a0b1-9d0f5e7c2ab3/actions/respond-plan' \
  -H "Authorization: Bearer $NIUCORE_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "action_id": "act_4d8b2c0f9e11",
    "action": "approve"
  }'
Respuesta
{
  "status": "success",
  "message": "Operación exitosa",
  "message_code": "api_v1_ok",
  "data": {
    "action_id": "act_4d8b2c0f9e11",
    "state": "resolved"
  },
  "meta": {
    "request_id": "9f1c0a3e-7b2d-4f18-9a55-2c6e0d1b4a77"
  }
}

Responder una pregunta

POSTapi.niucore.com/api/v1/chats/{chat_id}/actions/respond-elicitation

Scope: chat:write

Contesta la pregunta que hizo el turno (kind: "elicitation"). Es la elicitation del protocolo MCP: la levanta un conector, no una herramienta nativa de NiuCore.

El payload de la interacción es { message, mode, requested_schema }. requested_schema es un JSON Schema y content tiene que ser un objeto plano que lo cumpla: sus propiedades son los campos que el conector pide, no una lista de opciones.

Ruta

chat_idstring (uuid)obligatorio

El chat del turno.

Cuerpo

action_idstringobligatorio

El action_id del evento.

actionstringobligatorio

accept responde, decline se niega, cancel aborta la pregunta.

acceptdeclinecancel

contentobject

La respuesta. Objeto plano que cumple payload.requested_schema. Obligatorio con action: "accept".

Errores

  • 404api_v1_action_not_foundLa interacción no existe, venció o no es de esta credencial.
  • 409api_v1_action_not_pendingYa se resolvió, otro request la reclamó primero, o su resultado quedó desconocido. No la reintentes.
  • 409api_v1_action_result_unknownEl servicio de conversación recibió la decisión y la rechazó (data.upstream_status). La interacción queda cerrada: consultá el turno.
  • 503api_v1_service_unavailableLa decisión no llegó a salir (meta.error_details.retry_action: "retry"). La interacción vuelve a pending: reintentar es seguro.
  • 503api_v1_action_result_unknownLa decisión salió y no hubo respuesta: no se sabe si se aplicó (retry_action: "check_status"). No la reenvíes; seguí el stream o listá los mensajes.
  • 503api_v1_interaction_backend_unavailableNo se pueden resolver interacciones ahora. No es un 404: es una indisponibilidad, y reintentar tiene sentido.
curl -sS -X POST 'https://api.niucore.com/api/v1/chats/3f6b1a90-6c2d-4d84-a0b1-9d0f5e7c2ab3/actions/respond-elicitation' \
  -H "Authorization: Bearer $NIUCORE_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "action_id": "act_11ff03ac7d52",
    "action": "accept",
    "content": {
      "email": "ada@acme.test"
    }
  }'
Respuesta
{
  "status": "success",
  "message": "Operación exitosa",
  "message_code": "api_v1_ok",
  "data": {
    "action_id": "act_11ff03ac7d52",
    "state": "resolved"
  },
  "meta": {
    "request_id": "9f1c0a3e-7b2d-4f18-9a55-2c6e0d1b4a77"
  }
}

Cancelar una conexión pendiente

POSTapi.niucore.com/api/v1/chats/{chat_id}/actions/cancel-auth

Scope: chat:write

Desbloquea un turno parado en kind: "auth_required" sin conectar nada. El modelo sigue adelante sin esa herramienta.

No hay confirm-auth público

Completar el OAuth de un conector exige un navegador y una persona, y eso pasa en la aplicación. Desde la API solo se puede cancelar, o pedirle al usuario que conecte el servicio en NiuCore.

Ruta

chat_idstring (uuid)obligatorio

El chat del turno.

Cuerpo

action_idstringobligatorio

El action_id del evento.

Errores

  • 404api_v1_action_not_foundLa interacción no existe, venció o no es de esta credencial.
  • 409api_v1_action_not_pendingYa se resolvió, otro request la reclamó primero, o su resultado quedó desconocido. No la reintentes.
  • 409api_v1_action_result_unknownEl servicio de conversación recibió la decisión y la rechazó (data.upstream_status). La interacción queda cerrada: consultá el turno.
  • 503api_v1_service_unavailableLa decisión no llegó a salir (meta.error_details.retry_action: "retry"). La interacción vuelve a pending: reintentar es seguro.
  • 503api_v1_action_result_unknownLa decisión salió y no hubo respuesta: no se sabe si se aplicó (retry_action: "check_status"). No la reenvíes; seguí el stream o listá los mensajes.
  • 503api_v1_interaction_backend_unavailableNo se pueden resolver interacciones ahora. No es un 404: es una indisponibilidad, y reintentar tiene sentido.
curl -sS -X POST 'https://api.niucore.com/api/v1/chats/3f6b1a90-6c2d-4d84-a0b1-9d0f5e7c2ab3/actions/cancel-auth' \
  -H "Authorization: Bearer $NIUCORE_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "action_id": "act_7b1e5f30ca84"
  }'
Respuesta
{
  "status": "success",
  "message": "Operación exitosa",
  "message_code": "api_v1_ok",
  "data": {
    "action_id": "act_7b1e5f30ca84",
    "state": "resolved"
  },
  "meta": {
    "request_id": "9f1c0a3e-7b2d-4f18-9a55-2c6e0d1b4a77"
  }
}

Cancelar el turno

POSTapi.niucore.com/api/v1/chats/{chat_id}/actions/cancel

Scope: chat:write

Corta el turno en curso del chat. Sin action_id cancela el turno vivo, que es lo habitual cuando se decide abortar.

Sugerencia

Es idempotente: cancelar un turno que ya terminó responde 200 igual. Un cliente que reintenta porque no vio la respuesta no tiene por qué distinguir «lo cancelé yo» de «ya estaba cancelado».

El 200 significa que la cancelación se entregó. Si no se pudo entregar recibís 502 y el turno sigue corriendo: reintentá, y mientras tanto seguí leyendo el stream. Un turno cancelado termina con message.cancelled, y el replay de esa Idempotency-Key devuelve status: "cancelled".

Ruta

chat_idstring (uuid)obligatorio

El chat a cancelar.

Cuerpo

action_idstring

Opcional. Con él se exige que la interacción sea del turno de esta credencial.

Errores

  • 404api_v1_not_foundEl chat no existe o es de otra credencial. Cancelar no crea el chat, a diferencia de enviar un turno.
  • 502api_v1_core_unavailableLa cancelación no se pudo entregar al turno. El turno sigue corriendo: reintentá.
curl -sS -X POST 'https://api.niucore.com/api/v1/chats/3f6b1a90-6c2d-4d84-a0b1-9d0f5e7c2ab3/actions/cancel' \
  -H "Authorization: Bearer $NIUCORE_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'
Respuesta
{
  "status": "success",
  "message": "Operación exitosa",
  "message_code": "api_v1_ok",
  "data": {
    "chat_id": "3f6b1a90-6c2d-4d84-a0b1-9d0f5e7c2ab3",
    "cancelled": true
  },
  "meta": {
    "request_id": "9f1c0a3e-7b2d-4f18-9a55-2c6e0d1b4a77"
  }
}