MétodoRutaScopeQué hace
GET/api/v1/ai/servicesai:readServicios de imagen disponibles
GET/api/v1/ai/paramsai:readParámetros de uno o varios servicios
POST/api/v1/ai/generateai:generateGenera una imagen
GET/api/v1/ai/jobs/{id}ai:readEstado de un trabajo de imagen
GET/api/v1/ai/video/servicesai:readServicios de vídeo
GET/api/v1/ai/video/paramsai:readParámetros de un servicio de vídeo
GET/api/v1/ai/video/capabilitiesai:readCapacidades de un servicio de vídeo
POST/api/v1/ai/video/generateai:generateGenera un vídeo
GET/api/v1/ai/video/jobs/{id}ai:readEstado de un trabajo de vídeo
GET/api/v1/stock/servicesstock:searchBancos de imágenes disponibles
GET/api/v1/stock/searchstock:searchBusca imágenes (paginado)
POST/api/v1/stock/downloadstock:downloadDescarga una imagen del banco
GET/api/v1/external/portalsexternal:readPortales externos disponibles
GET/api/v1/external/portals/{slug}external:readDetalle de un portal externo
POST/api/v1/external/portals/{slug}/refreshportal:adminFuerza la actualización de su caché
GET/api/v1/external/mediaexternal:readMedios de un portal externo
GET/api/v1/external/articlesexternal:readArtículos de uno o varios portales
GET/api/v1/external/articles/{id}external:readUn artículo (id numérico)

Los scopes ai:*, stock:* y external:read se conceden a la API key (Autenticación). external:read es nuevo en v1 y no lo tiene ninguna key existente: un administrador lo concede por CLI con php bin/console limbo:grant-scope <api-key-id> external:read. Los textos de error de Atenea nunca llegan al cliente: recibes siempre el error normalizado de Limbo.

Imágenes con IA

curl
curl https://limbo.lefebvre.es/api/v1/ai/services -H "Authorization: Bearer $JWT"

curl "https://limbo.lefebvre.es/api/v1/ai/params?services=flux-pro,dalle3&form=true" -H "Authorization: Bearer $JWT"

curl -X POST https://limbo.lefebvre.es/api/v1/ai/generate \
  -H "Authorization: Bearer $JWT" -H "Content-Type: application/json" \
  -H "Idempotency-Key: 3f1c9a52-8d07-4c1e-9b0a-52d0f7a1e001" \
  -d '{"service": "flux-pro", "prompt": "Oficina luminosa con plantas, estilo editorial", "aspect_ratio": "16:9"}'

# Éxito (recortado)
{ "success": true, "data": { "status": "completed", "images": [ "https://..." ] } }

# La espera se agotó: 202
{ "success": true, "data": { "job_id": "job_123", "status": "processing" } }

curl https://limbo.lefebvre.es/api/v1/ai/jobs/job_123 -H "Authorization: Bearer $JWT"
  • params acepta services (slugs separados por coma) y form (true|false|1|0).
  • service es un slug (minúsculas, dígitos, ., _ y -) y prompt admite hasta 4000 caracteres. El resto de campos son las opciones del modelo (escalares o listas de escalares, máximo 40 campos, nombres que empiezan por minúscula; letras, dígitos y _): las valida Atenea, y si las rechaza es un 422 upstream_rejected.
  • generate es síncrono. Limbo espera internamente al resultado (hasta 30 consultas, la primera tras 5 s) y lo devuelve en data. Si la espera se agota responde 202 con job_id y status: processing: consulta entonces jobs/{id} hasta que termine. async y query no existen en v1 y dan 422.
  • Un job_id solo es visible para el portal que lo creó (si no, 404). Los trabajos creados con la API antigua no se ven en v1.
  • Usa Idempotency-Key en generate: un reintento por timeout de red no vuelve a generar (ni a cobrar) la imagen.

Vídeo con IA

curl
curl "https://limbo.lefebvre.es/api/v1/ai/video/params?service=kling" -H "Authorization: Bearer $JWT"
curl "https://limbo.lefebvre.es/api/v1/ai/video/capabilities?service=kling" -H "Authorization: Bearer $JWT"

curl -X POST https://limbo.lefebvre.es/api/v1/ai/video/generate \
  -H "Authorization: Bearer $JWT" -H "Content-Type: application/json" \
  -d '{"service": "kling", "prompt": "Panorámica lenta sobre una oficina al amanecer", "image": "data:image/jpeg;base64,/9j/4AAQ..."}'
  • params y capabilities exigen service; si falta o no es un slug, 422.
  • Los medios de entrada van como data URI en image, image2, last_frame, video o reference_images (de 1 a 3). Qué combinaciones admite cada servicio sale de capabilities.
  • Misma mecánica que en imagen: Limbo espera (hasta 6 consultas de 5 s) y, si se agota, responde 202 con job_id para consultar video/jobs/{id}. async y query dan 422.

Bancos de imágenes

curl
curl https://limbo.lefebvre.es/api/v1/stock/services -H "Authorization: Bearer $JWT"

curl "https://limbo.lefebvre.es/api/v1/stock/search?service=pexels&query=oficina+moderna&orientation=horizontal&page=1&per_page=20" \
  -H "Authorization: Bearer $JWT"

curl -X POST https://limbo.lefebvre.es/api/v1/stock/download \
  -H "Authorization: Bearer $JWT" -H "Content-Type: application/json" \
  -H "Idempotency-Key: 9a7d1c40-52e3-4b6f-8a10-0c5e3d7b2f11" \
  -d '{"service": "pexels", "image_id": "1234567", "size": "large", "filename": "oficina-moderna"}'
  • search es un GET con service y query (de 5 a 200 caracteres) obligatorios. Filtros opcionales: orientation (horizontal|vertical|square), image_type (photo|illustration|vector), category, color, size, width_from, width_to, height_from y height_to. Pagina con page y per_page (máximo 100); limit da 422.
  • download exige image_id (el campo id es un 422) y admite size (original|large|medium|small) y filename. Es idempotente con Idempotency-Key.

Contenido externo (WordPress)

curl
curl https://limbo.lefebvre.es/api/v1/external/portals -H "Authorization: Bearer $JWT"

curl "https://limbo.lefebvre.es/api/v1/external/media?portal=mi-portal&media_type=images&order=DESC&per_page=20" \
  -H "Authorization: Bearer $JWT"

curl "https://limbo.lefebvre.es/api/v1/external/articles?portal=portal-a,portal-b&page=1&per_page=20" \
  -H "Authorization: Bearer $JWT"

curl https://limbo.lefebvre.es/api/v1/external/articles/4521 -H "Authorization: Bearer $JWT"
  • Un portal inactivo o desconocido es siempre 404.
  • media filtra por media_type (images|videos|all), search, order (ASC|DESC), post_type y category.
  • articles exige portal: un slug o una lista de 1 a 10 separados por coma. Con varios portales, page × per_page no puede pasar de 1000. El {id} del artículo son solo dígitos.
  • refresh exige portal:admin (no external:read) y no es idempotente.

Errores propios de esta página

EstadoCausa
202generate no terminó dentro de la espera interna: consulta jobs/{id}
403Falta el scope de la ruta (ai:generate, stock:download, external:read…)
404Trabajo inexistente o de otro portal; portal externo inactivo o desconocido; artículo inexistente
422Parámetro inválido o desconocido (limit, async, query, id en descarga…), o Atenea rechazó la petición (upstream_rejected)
502 / 504Atenea falló o no respondió a tiempo. Reintenta con la misma Idempotency-Key

Formato general de errores en Conceptos; schemas completos en la referencia OpenAPI.