Qué es esta API
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:
Los procesos del servidor cargan ese archivo solos; en tus scripts basta con leer las variables de entorno.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-... - 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
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.
Descubrir modelos
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_name | Nombre legible del modelo, para mostrar en tu interfaz. |
context_limit | Tamaño máximo de contexto, en tokens. |
input_per_1m_usd | Precio efectivo por millón de tokens de entrada, ya con la tarifa de tu organización. |
output_per_1m_usd | Precio efectivo por millón de tokens de salida. |
/v1/models al arrancar
(o cachearlo unos minutos) y elegir según context_limit y precio.Chat completions
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"]
}
}
}]
}
Modelo Automático
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.
cost_usd) siempre corresponde al modelo real elegido,
y así queda registrado en tu consumo.Errores y límites
| Código | Qué pasó | Qué hacer |
|---|---|---|
401 | Clave inválida, revocada o rotada. | No reintentes: consigue una clave válida (Ajustes, o el ai.env actualizado del servidor). |
403 | El modelo pedido no está permitido para tu usuario. | Consulta /v1/models y usa uno del catálogo, o pide que te habiliten el modelo. |
429 | Cuota 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. |
Huella de consumo
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-Origin | Canal o superficie por donde entra el trabajo. | web-chat, api, worker, whatsapp, panel-admin |
X-SIAC-Feature | Función de negocio que consume. | asesor-repuestos, clasificador-tickets |
X-SIAC-Action | Etapa 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-etiquetaen 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-chatvs.whatsapp). - La función es una palabra de negocio, que el dueño reconocería sin leer código
(
asesor-repuestos, nohandler-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.
Otros endpoints
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.
/v1/chat/completions.De consumo a costo
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.