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… | Paquete | Guí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.
| Lenguaje | Paquete | Guía | Instalación |
|---|---|---|---|
| Python 3.11+ | spicyapi (código fuente) | SDK de Python | pip install spicyapi |
| Go 1.23+ | github.com/spicyapi-ai/spicy-go (código fuente) | SDK de Go | go get github.com/spicyapi-ai/spicy-go |
| PHP 8.1+ | spicyapi/spicyapi (código fuente) | SDK de PHP | composer require spicyapi/spicyapi |
| Java 17+ | ai.spicyapi:spicyapi-java (código fuente) | SDK de Java | Desde 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 createpide primero una cotización, muestra el importe y preguntaContinue? [y/N]. Solo--yesse 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
createTaskoretryTask, 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, aPOST /api/v1/jobs/quotey envía la tarea con suquoteIdy suexpectedCost: así, si el precio ha cambiado, el envío falla con40901en 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.
| Documento | Para qué sirve |
|---|---|
/agent.md | Política de integración breve y el flujo predeterminado más seguro |
/llms.txt | Mapa público del producto y de la documentación |
/llms-full.txt | Corpus ampliado para búsquedas cuando el índice corto no basta |
/api/agent | Manifiesto JSON estructurado para herramientas y clientes autónomos |
openapi.yaml | Formas 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 modeloimport { 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 dineroLa 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-v1Guarda 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.
// 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: 200ydata.state: "failed"; - respuestas duplicadas de
createTaskque devuelven el mismotaskId; - 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
quoteIdyexpectedCost. - Cada solicitud lógica tiene una sola
Idempotency-Keyguardada, que se envía siempre con la misma clave de API. - El
taskIdse 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 elcodede 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
taskIdy 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.
SDK de Java
El SDK oficial de Java para SpicyAPI (ai.spicyapi spicyapi-java) — instálalo, cotiza y lanza tu primera generación, sube un archivo local, verifica las llamadas de retorno y recorre de forma exhaustiva una taxonomía de errores sellada.
Herramienta de línea de comandos (CLI)
Usa el comando spicyapi para explorar modelos, consultar precios, generar imágenes y videos y descargar los resultados desde una terminal, sin escribir código.

