OpenSolvex Docs
CortexStacks (IaC)

Referencia de la API

Endpoints REST de stacks IaC - autenticación, scopes, cuerpos de petición y respuesta, y códigos de error.

Todo lo que hace el portal de stacks está disponible por REST bajo /v1/iac. Esta página es la referencia completa — pensada también para agentes de IA que crean y despliegan stacks por ti (esta misma página existe como Markdown limpio agregando .md a la URL).

export CORTEX_API_URL="https://cortex.opensolvex.co" # http://localhost:3006 en desarrollo

Autenticación

Dos mecanismos, el mismo contrato:

  • API key (machine-to-machine, CI, agentes): header x-api-key: osx_sk_xxx. Si la key pertenece a un tenant, todas las operaciones ocurren en ese tenant y el campo tenantId del body/query se ignora. Las keys de plataforma deben enviar tenantId explícito: en el body de los POST/DELETE y como query param (?tenantId=org_x) en los GET; sin él, la operación responde 400.
  • Token de portal (humanos): header Authorization: Bearer <token> emitido por la consola de OpenSolvex; el tenant se toma de tu organización.

Toda operación queda auditada con su actor (api-key:<id> o user:<id>). Cuando una key de plataforma opera en nombre de un tenant, el actor queda decorado como api-key:<id> on-behalf-of:<tenantId> y los eventos de dominio iac.stack.* llevan onBehalfOf: true — así se puede auditar qué hizo cada key de plataforma y sobre qué tenants.

Scopes

ScopePermite
iac:readListar y leer stacks, changesets y eventos
iac:manageValidar templates, crear stacks, proponer changesets, detectar drift, exportar
iac:deployEjecutar changesets y eliminar stacks

Un scope insuficiente responde 403. iac:deploy equivale a administrar el tenant completo — ver Seguridad.

Endpoints

MétodoRutaScope
POST/v1/iac/templates/validateiac:manage
GET/v1/iac/stacksiac:read
POST/v1/iac/stacksiac:manage
GET/v1/iac/stacks/:idiac:read
GET/v1/iac/stacks/:id/eventsiac:read
GET/v1/iac/stacks/:id/change-setsiac:read
POST/v1/iac/stacks/:id/change-setsiac:manage
POST/v1/iac/stacks/:id/change-sets/:csId/executeiac:deploy
POST/v1/iac/stacks/:id/detect-driftiac:manage
DELETE/v1/iac/stacks/:idiac:deploy
GET/v1/iac/exportiac:manage

Validar un template

POST /v1/iac/templates/validate — dry-run puro, no muta nada.

Request
{ "template": "<YAML como string>" }

200 si es válido (con los recursos detectados y el orden de despliegue); 422 si no:

Respuesta 422
{
  "message": "Template inválido",
  "errors": [
    { "path": "resources.Modelo.properties.accountId", "message": "referencia a un nombre inexistente" }
  ]
}

Crear un stack

POST /v1/iac/stacks — crea el stack en REVIEW_IN_PROGRESS con su changeset inicial. No materializa recursos: eso ocurre al ejecutar el changeset.

Request
{
  "name": "mi-primer-stack",
  "template": "<YAML como string>",
  "parameters": { "OpenaiApiKey": "sk-..." }
}
  • name: kebab-case (^[a-z0-9][a-z0-9-]*$), único por tenant (409 si ya existe).
  • parameters: valores para los parámetros del template. Los noEcho nunca vuelven en ninguna respuesta.

Responde { "stack": { ... }, "changeSet": { ... } } con los ids de ambos.

Leer stacks

  • GET /v1/iac/stacks — lista con nombre, estado y metadatos.
  • GET /v1/iac/stacks/:id — detalle: status, statusReason, templateSource (el YAML vigente), parameters (redactados), outputs y resources[] (nombre lógico → tipo, id físico, estado, drift).
  • GET /v1/iac/stacks/:id/events — traza de despliegue, más recientes primero: recurso, estado, motivo, actor, timestamp.

Proponer un changeset

POST /v1/iac/stacks/:id/change-sets

Request
{
  "template": "<YAML como string>",
  "parameters": { "OpenaiApiKey": "sk-..." }
}

Valida el template, calcula el diff contra lo aplicado y responde el changeset en pending. El pendiente anterior (si lo había) queda superseded. 409 si el stack está en progreso.

Respuesta (extracto)
{
  "id": "chs_01H...",
  "status": "pending",
  "diff": {
    "entries": [
      { "logicalId": "AgenteVentas", "type": "Cortex::Agent", "action": "modify", "changedFields": ["systemPrompt"] },
      { "logicalId": "CrmTools", "type": "Cortex::ToolSource", "action": "replace", "replacement": "delete-first", "reason": "kind es inmutable" }
    ],
    "applyOrder": ["..."],
    "removeOrder": ["..."]
  }
}

GET /v1/iac/stacks/:id/change-sets lista el historial completo.

Ejecutar un changeset

POST /v1/iac/stacks/:id/change-sets/:csId/execute — sin body. Responde 202 y despliega en segundo plano. El changeset debe estar pending; 409 si el stack ya está en progreso.

Para saber el resultado, consulta el stack hasta que salga de *_IN_PROGRESS (CREATE_COMPLETE/UPDATE_COMPLETE = éxito; ROLLBACK_COMPLETE/UPDATE_ROLLBACK_COMPLETE = falló y se revirtió; los detalles quedan en /events).

Detectar drift

POST /v1/iac/stacks/:id/detect-drift — compara lo aplicado con lo que existe y responde el estado por recurso (IN_SYNC, DRIFTED, DELETED) con el diff por propiedad.

Eliminar un stack

DELETE /v1/iac/stacks/:id

Request (opcional)
{ "retain": ["SkillCalificar"] }

Responde 202 con { "status": "DELETE_IN_PROGRESS" }. Elimina los recursos en orden inverso; los nombres en retain sobreviven desasociados (400 si un nombre no existe en el stack). El historial del stack se conserva.

Exportar un template

GET /v1/iac/export — genera un template YAML a partir de los recursos existentes del tenant (?resources= para seleccionar). Útil para adoptar como código lo creado a mano; el resultado usa la forma JSON de las intrínsecas, equivalente a los tags.

Códigos de error

CódigoCuándo
400Body inválido (nombre mal formado, retain con nombres desconocidos)
401Sin credenciales o credenciales inválidas
403Scopes insuficientes
404Stack o changeset inexistente (o de otro tenant)
409Nombre de stack duplicado; stack en progreso (mutex); changeset no ejecutable
422Template inválido (con errors[] detallado)

Receta para agentes

El flujo mínimo que un agente debe seguir para dejar un stack funcionando:

# 1. Validar
curl -sX POST "$CORTEX_API_URL/v1/iac/templates/validate" \
  -H "x-api-key: $CORTEX_API_KEY" -H "Content-Type: application/json" \
  -d "$(jq -n --rawfile t template.yaml '{template: $t}')"

# 2. Crear stack (guarda stack.id y changeSet.id de la respuesta)
curl -sX POST "$CORTEX_API_URL/v1/iac/stacks" \
  -H "x-api-key: $CORTEX_API_KEY" -H "Content-Type: application/json" \
  -d "$(jq -n --rawfile t template.yaml \
    '{name: "mi-stack", template: $t, parameters: {OpenaiApiKey: env.OPENAI_API_KEY}}')"

# 3. Ejecutar el changeset
curl -sX POST "$CORTEX_API_URL/v1/iac/stacks/$STACK_ID/change-sets/$CHANGESET_ID/execute" \
  -H "x-api-key: $CORTEX_API_KEY"

# 4. Esperar hasta CREATE_COMPLETE (o ROLLBACK_COMPLETE → revisar /events)
until [ "$(curl -s "$CORTEX_API_URL/v1/iac/stacks/$STACK_ID" \
  -H "x-api-key: $CORTEX_API_KEY" | jq -r .status)" != "CREATE_IN_PROGRESS" ]; do sleep 3; done

Reglas que el agente debe respetar:

  • Los valores sensibles van solo en parameters (contra parámetros noEcho), nunca dentro del YAML.
  • Nunca asumas éxito tras el 202: consulta el estado final y, si hubo rollback, lee /events para diagnosticar antes de re-proponer.
  • Ante 409 por stack en progreso, espera y reintenta — no crees un stack paralelo.
  • Para modificar un stack existente propón un changeset; no elimines y recrees.

On this page