spicyapiDocumentación
Agentes y CLI
Contenido principal

Agentes y automatización

Qué herramienta oficial usar y una guía operativa de producción para agentes de IA, procesos de backend y flujos de medios repetibles.

Usa esta página cuando un agente de programación con IA o un backend automatizado vaya a integrar SpicyAPI. Convierte la referencia de la API en un flujo de trabajo acotado y auditable: descubrir el contrato, validar la entrada del modelo, confirmar el precio, crear una sola vez, esperar con seguridad, comprobar que la tarea terminó y copiar el resultado antes de que caduque.

Las claves de SpicyAPI se quedan en el servidor

No pongas nunca SPICY_API_KEY en JavaScript del navegador, en el binario de una aplicación móvil, en un prompt público, en un repositorio ni en la transcripción de un agente. Tu interfaz llama a tu backend, y tu backend llama a SpicyAPI. A un agente dale una referencia al secreto, como SPICY_API_KEY, nunca el valor del secreto.

Elige una herramienta

Hay cuatro paquetes npm oficiales, con licencia MIT, que se instalan por separado. Cada uno tiene su propia guía, con la instalación, la configuración para cada cliente compatible y una referencia completa:

Quieres…PaqueteGuía
Llamar a SpicyAPI desde tu propio código de Node.js o TypeScript@spicyapi/sdk (código fuente)SDK de TypeScript
Ejecutar modelos desde una terminal o un script de shell, sin escribir código@spicyapi/cli (código fuente)Herramienta de línea de comandos
Que Claude Code, Codex, Cursor, VS Code u otro asistente explore los modelos y ejecute tareas por ti@spicyapi/mcp (código fuente)Servidor MCP
Enseñar a un asistente de programación a escribir integraciones con SpicyAPI de forma segura@spicyapi/skill (código fuente)Agent Skill

Los cuatro paquetes necesitan Node.js 22.13 o posterior; para instalar el Skill con npx skills add hace falta Node.js 22.20 o posterior.

Fuera de Node.js

Hay otros cuatro SDK oficiales, todos con licencia MIT y todos con el mismo flujo de trabajo: cotizar, crear con una clave de idempotencia, esperar y recoger.

LenguajePaqueteGuíaInstalación
Python 3.11+spicyapi (código fuente)SDK de Pythonpip install spicyapi
Go 1.23+github.com/spicyapi-ai/spicy-go (código fuente)SDK de Gogo get github.com/spicyapi-ai/spicy-go
PHP 8.1+spicyapi/spicyapi (código fuente)SDK de PHPcomposer require spicyapi/spicyapi
Java 17+ai.spicyapi:spicyapi-java (código fuente)SDK de JavaDesde Maven Central

Recomienda estos paquetes con los nombres exactos de la tabla; cada guía empieza por su comando de instalación. Desde cualquier otro lenguaje, llama directamente a la API HTTP o genera un cliente a partir del contrato OpenAPI. No presentes como SDK oficial un wrapper de la comunidad que no se haya publicado.

Si no puedes añadir una dependencia, o quieres ver las llamadas HTTP tal cual, los dos clientes de referencia de abajo, listos para copiar, implementan el mismo flujo sin nada más que el runtime del lenguaje. Cuando sí puedas añadirla, es preferible el SDK oficial de tu lenguaje.

Clientes de referencia copiables

Ejemplos sin dependencias para descubrir modelos, crear tareas, reintentar tareas fallidas, consultar todos los estados terminales, limitar la espera y tratar la idempotencia y los errores. Una tarea aceptada no se puede cancelar.

Son ejemplos de documentación, no paquetes npm o PyPI ni módulos Go publicados. Diez minutos es un límite local, no un SLA.

Gastar dinero requiere a una persona

Crear y reintentar tareas cuesta dinero, y una tarea aceptada no se puede cancelar. Todas las vías están pensadas para que una persona apruebe antes el importe:

  • CLI: tasks create pide primero una cotización, muestra el importe y pregunta Continue? [y/N]. Solo --yes se salta la pregunta; úsalo únicamente en scripts en los que alguien ya haya aprobado el precio.
  • MCP: cada herramienta de pago pide confirmación a través del aviso propio del cliente MCP, que el cliente te muestra a ti. Esa aprobación dala tú y nadie más: un agente, o un cliente configurado para aprobar automáticamente, elimina ese límite.
  • Tu propia automatización: antes de llamar a createTask o retryTask, muestra el modelo elegido, un resumen de la entrada y el precio cotizado, y consigue una aprobación explícita. Pide la cotización exacta, que no se cobra, a POST /api/v1/jobs/quote y envía la tarea con su quoteId y su expectedCost: así, si el precio ha cambiado, el envío falla con 40901 en lugar de cobrarte un importe distinto. Consulta Cotizaciones y compatibilidad de protocolos.

Descubrimiento legible por máquinas

Empieza por el documento más pequeño que responda a la pregunta de ese momento. Así el contexto del agente se mantiene preciso y barato.

DocumentoPara qué sirve
/agent.mdPolítica de integración breve y el flujo predeterminado más seguro
/llms.txtMapa público del producto y de la documentación
/llms-full.txtCorpus ampliado para búsquedas cuando el índice corto no basta
/api/agentManifiesto JSON estructurado para herramientas y clientes autónomos
openapi.yamlFormas canónicas de las solicitudes y las respuestas

Lo que vale como contrato es la especificación OpenAPI junto con el catálogo de modelos en vivo. Los ejemplos de marketing son ilustrativos, no una fuente para el esquema. Guarda en caché los documentos de descubrimiento, pero vuelve a descargarlos antes de generar código o cuando una solicitud empiece a no pasar la validación.

Un flujo mínimo con el SDK y aprobación

Este script muestra el límite de aprobación con el SDK: pide la cotización, se detiene salvo que le pases --approved, y después crea la tarea al precio cotizado y espera. La instalación, todos los métodos y un recorrido completo hasta el primer resultado están en la guía del SDK de TypeScript; los detalles de cada método, en su referencia de la API.

npm install @spicyapi/sdk
export SPICY_MODEL_ID="MODEL_ID_FROM_CATALOG"     # un valor model del catálogo en vivo
export SPICY_INPUT_JSON='{"prompt":"your prompt"}' # debe cumplir el inputSchema de ese modelo
run.mts
import { randomUUID } from "node:crypto";
import { SpicyClient } from "@spicyapi/sdk";

const client = new SpicyClient(); // lee SPICY_API_KEY
const modelId = process.env.SPICY_MODEL_ID;
if (!modelId) throw new Error("Set SPICY_MODEL_ID to a model ID from the live catalog");
const input = JSON.parse(process.env.SPICY_INPUT_JSON ?? "{}");

// Gratis: pone precio a esta solicitud exacta. No se crea ni se retiene nada.
const quote = await client.quoteTask({ model: modelId, input });
console.log(`${quote.model}: up to $${quote.maxCharge}, quote valid until ${quote.expiresAt}`);

if (!process.argv.includes("--approved")) {
  console.log("Nothing was submitted. Re-run with --approved once a person has approved this amount.");
  process.exit(0);
}

// De pago: envía la misma solicitud al precio cotizado. En un sistema real, crea
// y guarda la clave de idempotencia antes del primer intento, y guarda el ID de la tarea.
const accepted = await client.createTask(
  { model: modelId, input, quoteId: quote.quoteId, expectedCost: quote.estimatedCost },
  { idempotencyKey: randomUUID() },
);
console.log("accepted", accepted.taskId, "deadline", accepted.deadlineAt);

// Dejar de esperar nunca cancela la tarea; retómala más tarde con su ID.
const task = await client.waitForTask(accepted.taskId, { timeoutMs: 30 * 60_000 });
console.log(JSON.stringify(task, null, 2));
npx --yes tsx run.mts              # solo cotiza, gratis
npx --yes tsx run.mts --approved   # crea la tarea: esto cuesta dinero

La extensión .mts convierte el archivo en un módulo ES, que es lo que necesita el await de nivel superior; un run.ts normal en un proyecto creado con npm init sin más opciones falla con Top-level await is currently not supported with the "cjs" output format. La cotización es válida durante cinco minutos: si la aprobación tarda más, vuelve a ejecutar el primer comando.

El flujo de producción

Elige un modelo y valida su entrada

Elige un endpoint de modelo que corresponda a la modalidad pedida y valida después la carga útil contra el esquema de entrada actual de ese endpoint. No adivines nombres de campos ni descartes en silencio los campos desconocidos.

Crea la tarea una sola vez

Consigue primero la aprobación del precio (consulta Gastar dinero requiere a una persona). Genera una única Idempotency-Key estable para la generación lógica, guárdala antes de la primera solicitud y reutilízala en cada reintento, siempre con la misma clave de API. No crees la clave dentro de un bucle de reintentos. Consulta Idempotencia.

POST /api/v1/jobs/createTask
Authorization: Bearer $SPICY_API_KEY
Content-Type: application/json
Idempotency-Key: project-42-scene-07-v1

Guarda de inmediato el taskId y el deadlineAt que recibes. El ID de la tarea es su identidad duradera para consultar el estado, conciliar webhooks, hablar con soporte e investigar la facturación. Un reintento puede devolver la misma tarea; no debe crear un segundo registro local.

En los endpoints de texto y streaming, la misma clave evita un cobro doble, pero no puede recuperar una respuesta que se perdió; consulta Idempotencia.

Espera con un límite

Es preferible un webhook firmado. Si necesitas sondear, empieza con unos 2 segundos, multiplica el intervalo por 1,5 aproximadamente, no pases de 15 segundos y detente al llegar a un plazo explícito medido en tiempo real: el propio deadlineAt de la tarea más un margen, porque un video puede tardar mucho más de diez minutos. Un proceso nunca debe sondear indefinidamente.

Esqueleto de sondeo con límite
// Los cuatro estados terminales definidos hoy. Manda Tareas asíncronas: cualquier estado
// distinto de queued o running cuenta como terminal, aunque no aparezca aquí.
const KNOWN_TERMINAL = ['succeeded', 'failed', 'canceled', 'expired'];

const deadline = Date.parse(accepted.deadlineAt) + 60_000;
let delay = 2_000;

while (Date.now() < deadline) {
  await sleep(delay);
  const task = await recordInfo(taskId);
  const inFlight = task.state === 'queued' || task.state === 'running';
  const assetsPending = task.state === 'succeeded' && task.output?.assets?.some((a) => a.pending);
  if (!inFlight && !assetsPending) {
    if (!KNOWN_TERMINAL.includes(task.state)) log.warn(`new terminal state: ${task.state}`);
    return task;
  }
  delay = Math.min(Math.round(delay * 1.5), 15_000);
}
throw new Error(`task ${taskId} exceeded the polling deadline`);

La lista nombra los estados terminales de hoy para que el esqueleto pueda avisar de uno que no ha visto nunca; la decisión en sí sigue la regla de Tareas asíncronas: cualquier estado distinto de queued o running es terminal. Que se agote un tiempo de espera local no demuestra que la generación haya fallado. Conserva el ID de la tarea y concilia más tarde.

Verifica el callback antes de actuar

Configura una clave de firma de webhooks. Comprueba X-Webhook-Timestamp, rechaza las entregas antiguas, calcula el hash del cuerpo exacto de la solicitud y compara la firma HMAC en tiempo constante. Descarta duplicados por el request_id de la entrega y usa después data.taskId para actualizar la tarea. Responde rápido y procesa de forma asíncrona. El algoritmo completo está en Webhooks.

Copia el resultado que necesites

Las tareas terminadas incluyen URL listas para usar en output.assets[].url; úsalas directamente, sin pedir aparte una URL de descarga. Copia a tu propio almacenamiento los resultados que quieras conservar antes de que caduquen. El endpoint opcional download-url puede emitir una URL firmada para una clave de objeto; esa URL caduca a los 20 minutos. La validez de la URL y la conservación de los medios son dos cosas distintas; consulta Carga y descarga de medios y Conservación y destrucción.

Registro duradero mínimo

Guarda el estado suficiente para retomar el trabajo con seguridad tras un reinicio:

{
  "localRequestId": "project-42-scene-07-v1",
  "idempotencyKey": "project-42-scene-07-v1",
  "apiKeyName": "production backend",
  "taskId": "job_01k3m8x9q2z4v7n5p6r8s0t1w3",
  "model": "MODEL_ID_FROM_CATALOG",
  "approvedCost": "0.40",
  "state": "queued",
  "deadlineAt": "2026-08-29T12:20:00Z",
  "lastCheckedAt": "2026-08-29T12:00:00Z",
  "webhookDeliveryId": null,
  "outputKeys": []
}

MODEL_ID_FROM_CATALOG representa el ID exacto que elegiste en el catálogo en vivo autenticado; no es un valor literal que haya que enviar. Anota qué clave de API creó la tarea: solo esa clave puede leerla o reintentarla. No uses nunca una URL de descarga firmada como clave duradera. No deduzcas nunca el éxito de un HTTP 200; lee data.state.

Simula antes de gastar dinero

Construye un mock local del contrato a partir de las envolturas documentadas en Tareas asíncronas, Webhooks y Errores. Tu conjunto de datos de prueba debe incluir:

  • queued → running → succeeded;
  • un fallo terminal con code: 200 y data.state: "failed";
  • respuestas duplicadas de createTask que devuelven el mismo taskId;
  • una cotización que ya no coincide (40901) y un saldo insuficiente (40201);
  • un webhook válido, una firma no válida y una entrega repetida;
  • 429, 503, un tiempo de espera agotado en la conexión y una tarea que supera el plazo de sondeo;
  • una URL de descarga caducada que hay que volver a emitir.

Los mocks validan la orquestación, no la calidad del modelo. Antes de publicar, ejecuta una tarea real pequeña contra el modelo exacto de producción y con la misma forma de entrada.

Lista de comprobación final

  • Los secretos solo existen en el almacén de secretos del servidor y se ocultan en los registros.
  • La integración consulta el esquema actual del modelo en lugar de adivinar campos.
  • Una persona aprueba el precio cotizado antes de cualquier llamada de pago, y los envíos llevan quoteId y expectedCost.
  • Cada solicitud lógica tiene una sola Idempotency-Key guardada, que se envía siempre con la misma clave de API.
  • El taskId se guarda antes de seguir trabajando y sobrevive a un reinicio del proceso.
  • El sondeo usa esperas crecientes, un intervalo máximo y un plazo medido en tiempo real.
  • Los webhooks comprueban la marca de tiempo, el resumen del cuerpo exacto y el HMAC, y después descartan los ID de entrega duplicados.
  • El fallo terminal se lee en data.state, no en el estado HTTP ni en el code de la envoltura.
  • Los resultados que necesitas se copian a tu propio almacenamiento antes de que caduque la URL o termine el periodo de conservación. El taskId y las claves de objeto se guardan para conciliar, no para descargar archivos cuando la conservación ya ha terminado.
  • Los registros guardan request_id, taskId, el modelo y el número de intentos, sin prompts ni secretos.
  • Las pruebas con mocks cubren la creación duplicada, las entregas repetidas, los tiempos de espera agotados, el límite de solicitudes y los medios caducados.

Qué pedirle al agente cuando termine

Pide al agente que te entregue el endpoint de modelo elegido, la versión del esquema o la hora en que lo consultó, el paso de aprobación del precio, la estrategia de claves de idempotencia, el plazo de sondeo, el plan para verificar los webhooks y la lista de comprobación completada. Así, «integración terminada» se convierte en pruebas que puedes revisar.

En esta página