API de IA
Referencia para desarrolladores

API de IA de la plataforma

Un endpoint compatible con OpenAI para que tus propias aplicaciones usen los modelos, las cuotas y la trazabilidad de SIAC. Si tu SDK habla con OpenAI, ya sabe hablar con SIAC: solo cambias la base URL y la clave.

Compatible OpenAIStreaming SSEHuella de consumo
terminal — tu app
$ curl -s $SIAC_IA_BASE_URL/models \
  -H "Authorization: Bearer $SIAC_IA_API_KEY"
{"object":"list","data":[{"id":"glm-5.2", ...
La misma API que ya usan opencode, los complementos y los agentes — ahora para tu propio código.
← Centro de manuales

Qué es esta API

El mismo motor de IA de SIAC, para tu propio código

La plataforma expone un endpoint compatible con la API de OpenAI. La base URL siempre termina en /v1:

https://ia.siac.com.co/v1

Eso significa que puedes usar los SDK oficiales de OpenAI (Python, Node, etc.), o cualquier herramienta que hable ese protocolo, sin adaptadores. Cada petición se registra, se cobra con la tarifa de tu organización y respeta las cuotas y modelos aprobados para tu usuario.

Dónde salen las credenciales

  • Dentro de un servidor de Dev Studio: no tienes que crear nada. Las credenciales ya están inyectadas en ~/.siac/ai.env, con estas variables:
    SIAC_IA_BASE_URL=https://ia.siac.com.co/v1
    SIAC_IA_API_KEY=sk-...
    # Alias para herramientas que esperan los nombres de OpenAI:
    OPENAI_BASE_URL=https://ia.siac.com.co/v1
    OPENAI_API_KEY=sk-...
    Los procesos del servidor cargan ese archivo solos; en tus scripts basta con leer las variables de entorno.
  • Fuera de un servidor (tu laptop, otro proveedor, un script suelto): crea una clave en Ajustes → Claves de API del panel. La clave completa solo se muestra una vez. Más detalle en el manual de Ajustes.

Autenticación

Bearer token, y la clave nunca en el frontend

Todas las peticiones llevan la clave en la cabecera estándar:

Authorization: Bearer sk-...

Regla de oro: la clave vive solo en el backend. Nunca la incrustes en JavaScript del navegador, en una app móvil ni en código que se distribuya: cualquiera podría extraerla y consumir con tu cuota. Si tu interfaz necesita IA, haz que el frontend llame a un endpoint propio de tu backend, y que sea el backend quien hable con /v1.

Una clave hereda el límite de consumo, el horario y los modelos permitidos del usuario que la creó. Si la clave se filtra, revócala en Ajustes: pierde acceso de inmediato.

Descubrir modelos

GET /v1/models — el catálogo manda

Devuelve los modelos aprobados para tu usuario, en el formato estándar de OpenAI, con campos extra propios de SIAC:

curl -s https://ia.siac.com.co/v1/models \
  -H "Authorization: Bearer $SIAC_IA_API_KEY"
{
  "object": "list",
  "data": [
    {
      "id": "glm-5.2",
      "object": "model",
      "display_name": "GLM-5.2",
      "context_limit": 202752,
      "input_per_1m_usd": 0.83,
      "output_per_1m_usd": 3.34
    }
  ]
}
Campo extra Qué significa
display_nameNombre legible del modelo, para mostrar en tu interfaz.
context_limitTamaño máximo de contexto, en tokens.
input_per_1m_usdPrecio efectivo por millón de tokens de entrada, ya con la tarifa de tu organización.
output_per_1m_usdPrecio efectivo por millón de tokens de salida.
Regla: elige el modelo consultando el catálogo, no de memoria. Los modelos disponibles y sus precios cambian con el tiempo; si tu código fija un id que ya no está aprobado, recibirás un 403. Lo robusto es listar /v1/models al arrancar (o cachearlo unos minutos) y elegir según context_limit y precio.

Chat completions

POST /v1/chat/completions — igual que OpenAI, con costo incluido

curl

curl -s https://ia.siac.com.co/v1/chat/completions \
  -H "Authorization: Bearer $SIAC_IA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "X-SIAC-Origin: api" \
  -H "X-SIAC-Feature: clasificador-tickets" \
  -d '{
    "model": "glm-5.2",
    "messages": [
      {"role": "system", "content": "Clasificas tickets de soporte en: facturacion, tecnico, ventas."},
      {"role": "user", "content": "No me llega la factura de julio"}
    ]
  }'

La respuesta es la estándar de OpenAI, más un campo aditivo cost_usd con el costo efectivo de ese request (tokens de entrada y salida, con tu tarifa):

{
  "id": "chatcmpl-...",
  "choices": [{ "message": { "role": "assistant", "content": "facturacion" }, ... }],
  "usage": { "prompt_tokens": 41, "completion_tokens": 3, "total_tokens": 44 },
  "cost_usd": 0.000044
}

Python (SDK de OpenAI)

import os
from openai import OpenAI

client = OpenAI(
    base_url=os.environ["SIAC_IA_BASE_URL"],  # termina en /v1
    api_key=os.environ["SIAC_IA_API_KEY"],
    default_headers={"X-SIAC-Origin": "worker"},
)

resp = client.chat.completions.create(
    model="glm-5.2",
    messages=[{"role": "user", "content": "Resume este pedido: ..."}],
    extra_headers={"X-SIAC-Feature": "clasificador-tickets", "X-SIAC-Action": "resumen"},
)
print(resp.choices[0].message.content)

Node (SDK de OpenAI)

import OpenAI from "openai";

const client = new OpenAI({
  baseURL: process.env.SIAC_IA_BASE_URL, // termina en /v1
  apiKey: process.env.SIAC_IA_API_KEY,
  defaultHeaders: { "X-SIAC-Origin": "web-chat" },
});

const resp = await client.chat.completions.create(
  { model: "glm-5.2", messages: [{ role: "user", content: "Hola" }] },
  { headers: { "X-SIAC-Feature": "asesor-repuestos" } }
);
console.log(resp.choices[0].message.content);

Streaming (SSE)

Pasa "stream": true y la respuesta llega como Server-Sent Events: una secuencia de eventos data:, cada uno un fragmento (delta) del mensaje, y un data: [DONE] final. Cada evento termina en una línea en blanco (\n\n) — si escribes tu propio parser o un proxy intermedio, respeta ese separador o los fragmentos llegarán vacíos o pegados.

data: {"id":"chatcmpl-...","choices":[{"delta":{"content":"Hola"}}]}

data: {"id":"chatcmpl-...","choices":[{"delta":{"content":", ¿en qué"}}]}

data: [DONE]

Con los SDK no tienes que parsear nada:

# Python
stream = client.chat.completions.create(
    model="glm-5.2",
    messages=[{"role": "user", "content": "Escribe un haiku"}],
    stream=True,
)
for chunk in stream:
    delta = chunk.choices[0].delta.content
    if delta:
        print(delta, end="", flush=True)

Herramientas (tools)

El campo tools usa el formato estándar de OpenAI (function calling): el modelo responde con tool_calls, tu código ejecuta la función y devuelve el resultado en un mensaje con role: "tool".

{
  "model": "glm-5.2",
  "messages": [{"role": "user", "content": "¿Cuánto vale el filtro F-204?"}],
  "tools": [{
    "type": "function",
    "function": {
      "name": "buscar_repuesto",
      "description": "Busca un repuesto por referencia y devuelve precio y stock",
      "parameters": {
        "type": "object",
        "properties": { "referencia": { "type": "string" } },
        "required": ["referencia"]
      }
    }
  }]
}
Todo lo que ya sabes del ecosistema OpenAI —SDKs, frameworks de agentes, function calling— funciona igual. SIAC solo añade control, precio efectivo y trazabilidad.

Modelo Automático

siac-auto: el router elige por ti

Si tu plan lo incluye, verás en el catálogo el modelo siac-auto. Al usarlo, la plataforma clasifica la tarea y elige el modelo más adecuado por costo y capacidad (una clasificación rápida no necesita el mismo modelo que un análisis largo).

resp = client.chat.completions.create(
    model="siac-auto",
    messages=[{"role": "user", "content": "..."}],
)

Para que sepas qué decidió el router, la respuesta incluye dos cabeceras:

  • X-SIAC-Model — el modelo real que atendió el request.
  • X-SIAC-Task — el tipo de tarea que detectó el router.
El costo (cost_usd) siempre corresponde al modelo real elegido, y así queda registrado en tu consumo.

Errores y límites

Qué significan y cómo reaccionar
Código Qué pasó Qué hacer
401Clave inválida, revocada o rotada.No reintentes: consigue una clave válida (Ajustes, o el ai.env actualizado del servidor).
403El modelo pedido no está permitido para tu usuario.Consulta /v1/models y usa uno del catálogo, o pide que te habiliten el modelo.
429Cuota del proveedor del modelo agotada por ahora.Reintenta con backoff exponencial con tope (ej. 1 s, 2 s, 4 s… hasta 30 s) o cambia a otro modelo del catálogo.
Además, tu organización puede configurar topes de costo (límite mensual por usuario) y horarios permitidos. Fuera de esos límites la API rechaza el request con un mensaje claro; tu código debe tratarlos como errores no reintentables hasta que cambie la condición.

Huella de consumo

Declara quién gasta, en qué y para qué — la parte más importante

Cada llamada puede (y debe) declarar tres niveles de contexto con cabeceras de petición. Con eso, el dueño del negocio ve en qué se van los tokens de su servidor, sin leer código:

Cabecera Nivel Ejemplos
X-SIAC-OriginCanal o superficie por donde entra el trabajo.web-chat, api, worker, whatsapp, panel-admin
X-SIAC-FeatureFunción de negocio que consume.asesor-repuestos, clasificador-tickets
X-SIAC-ActionEtapa dentro de la función (opcional).triage, respuesta, resumen, vision

El servidor y el modelo no se declaran: se atribuyen solos a partir de la clave de API y del request. Tú solo aportas los tres niveles de arriba.

Formato de los slugs

  • kebab-case: minúsculas, sin acentos ni espacios, palabras separadas por guiones.
  • Máximo 40 caracteres.
  • Un slug inválido se ignora y ese consumo aparece como sin-etiqueta en el dashboard. Nunca produce un error: etiquetar mal jamás rompe tu aplicación.

Alternativa por body

Si no puedes tocar las cabeceras (por ejemplo, un framework que no las expone), puedes mandar lo mismo dentro del body:

{
  "model": "glm-5.2",
  "messages": [...],
  "metadata": { "origin": "worker", "feature": "clasificador-tickets", "action": "triage" }
}

La plataforma consume ese campo; jamás llega al proveedor del modelo.

Patrón recomendado

Un único módulo de IA por proyecto: el origen fijo en default_headers (se declara una vez) y la función por llamada en extra_headers:

# Python — ia.py, el único lugar del proyecto que habla con la API
import os
from openai import OpenAI

client = OpenAI(
    base_url=os.environ["SIAC_IA_BASE_URL"],
    api_key=os.environ["SIAC_IA_API_KEY"],
    default_headers={"X-SIAC-Origin": "worker"},  # fijo para todo el proyecto
)

def completar(feature: str, messages: list, action: str | None = None, **kw):
    headers = {"X-SIAC-Feature": feature}
    if action:
        headers["X-SIAC-Action"] = action
    return client.chat.completions.create(
        model=kw.pop("model", "glm-5.2"),
        messages=messages,
        extra_headers=headers,
        **kw,
    )

# En el resto del código:
completar("clasificador-tickets", msgs, action="triage")
// Node — ia.js, equivalente
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: process.env.SIAC_IA_BASE_URL,
  apiKey: process.env.SIAC_IA_API_KEY,
  defaultHeaders: { "X-SIAC-Origin": "web-chat" }, // fijo para todo el proyecto
});

export function completar(feature, messages, { action, ...opts } = {}) {
  const headers = { "X-SIAC-Feature": feature };
  if (action) headers["X-SIAC-Action"] = action;
  return client.chat.completions.create(
    { model: "glm-5.2", messages, ...opts },
    { headers }
  );
}

Cómo diseñar buenos slugs

  • El origen es la superficie, no la función. Si el mismo asesor atiende por la web y por WhatsApp, la función sigue siendo asesor-repuestos; lo que cambia es el origen (web-chat vs. whatsapp).
  • La función es una palabra de negocio, que el dueño reconocería sin leer código (asesor-repuestos, no handler-v2), y estable en el tiempo: si la renombras cada semana, las series históricas se parten.
  • Nunca pongas ids de usuario o de conversación, timestamps ni datos personales en un slug. Rompen la agregación (cada valor único crea una serie nueva) y disparan el freno de cardinalidad: el excedente cae en el cajón «otros» y pierdes justo el detalle que buscabas.
Todo esto alimenta la pestaña Uso de IA de la ficha del servidor en Dev Studio: el dueño del negocio ve barras por día, desglose origen → función → acción → modelo, y el costo de cada cosa. Si tu app etiqueta bien, ese reporte se explica solo — la versión no técnica está en el manual Huella de IA.

Otros endpoints

RAG de Proyectos y piezas de marca
  • POST /v1/knowledge/search — busca en las bases de conocimiento de Proyectos de tu organización (los documentos que subes en el módulo Proyectos) y devuelve los pasajes más relevantes con su fuente. Útil para dar contexto real a tus prompts (RAG) sin montar tu propio índice.
  • POST /v1/brand/piece — genera piezas con tu kit de marca (colores, logo y tipografía de tu organización) listas para usar en documentos o presentaciones.
Ambos usan la misma autenticación y la misma huella de consumo que /v1/chat/completions.

De consumo a costo

Cada request queda registrado, y lo ves en el panel

Cada llamada a la API queda registrada con su modelo, tokens y costo efectivo (calculado con la tarifa de tu organización — el mismo valor que llega en cost_usd). No hay consumo invisible.

Dónde verlo, si tu código corre en un servidor de Dev Studio:

  • Ficha del servidor → pestaña «Uso de IA»: el consumo tipificado por la huella (origen → función → acción → modelo).
  • Ficha del servidor → pestaña «Costos»: los totales del servidor, IA incluida.
  • Inversión en servidores: la vista consolidada de toda tu organización, servidor por servidor.
Los tres cuadran entre sí: la huella no cambia el cobro, solo lo explica.
Ver el manual de Huella de IA ← Otros manuales