# Especificación y Contrato de API — Integraciones n8n y Claude Esta documentación describe todos los endpoints disponibles en `https://app.4ventures.es/api/integrations/...` para la ingesta de métricas de Meta Ads, detección de fatiga, relevo creativo con Claude, aprobaciones y publicación. --- ## 1. Autenticación y Seguridad Todas las peticiones a la API de integraciones deben incluir la cabecera `X-Api-Key`: - **Para n8n:** Clave asignada en `N8N_API_KEY` - **Para Claude:** Clave asignada en `CLAUDE_API_KEY` ```http X-Api-Key: Content-Type: application/json ``` ### Códigos de estado HTTP globales - `200 OK`: Petición procesada correctamente. - `400 Bad Request`: Parámetros obligatorios faltantes o payload JSON malformado. - `401 Unauthorized`: Cabecera `X-Api-Key` ausente o no válida. - `403 Forbidden`: El cliente (`client_id` / `tenant_id`) no tiene activo el flag `creatives_enabled = true`. - `404 Not Found`: El cliente o recurso solicitado no existe. - `500 Internal Server Error`: Error inesperado en el servidor. --- ## 2. Endpoints para n8n (Ingesta, Fatiga y Publicación) ### 2.1 Obtener Clientes Activos Obtiene la lista de clientes que tienen activado el módulo de creatividades y su cuenta de anuncios de Meta. - **Método:** `GET` - **Ruta:** `/api/integrations/clients?creatives_enabled=true` - **Headers:** `X-Api-Key: ` #### Respuesta de ejemplo (`200 OK`): ```json [ { "id": "8bec977a-6032-4759-aa7f-b5042081f450", "name": "Blue Parrot School", "slug": "bps", "meta_ad_account_id": "act_1052328112282270", "slack_channel_id": null, "drive_folder_id": null, "creatives_enabled": true, "client_id": "8bec977a-6032-4759-aa7f-b5042081f450", "tenant_id": "8bec977a-6032-4759-aa7f-b5042081f450" } ] ``` --- ### 2.2 Ingesta Diaria de Métricas (Bulk Upsert) Inserta o actualiza las métricas por anuncio y fecha. Si el anuncio (`meta_ad_id`) no existía en la base de datos, lo crea automáticamente en estado `publicada`. - **Método:** `POST` - **Ruta:** `/api/integrations/metrics/daily` - **Headers:** `X-Api-Key: `, `Content-Type: application/json` #### Payload de ejemplo (`Request Body`): ```json { "client_id": "8bec977a-6032-4759-aa7f-b5042081f450", "metrics": [ { "meta_ad_id": "1202058493829102", "date": "2026-10-11", "campaign_name": "Matrículas Octubre 2026", "impressions": 4200, "reach": 3800, "clicks": 135, "spend": 54.20, "results": 6, "raw": { "cpm": 12.90, "ctr": 3.21 } } ] } ``` #### Respuesta de ejemplo (`200 OK`): ```json { "ok": true, "count": 1, "rows": [ { "id": "7e417520-6089-4cc8-9e44-f08b4e948b2a", "meta_ad_id": "1202058493829102", "date": "2026-10-11" } ] } ``` --- ### 2.3 Obtener Candidatos de Fatiga Devuelve las creatividades actualmente publicadas con sus métricas dentro de la ventana de análisis (`lookback_days`) y las reglas de fatiga del cliente. - **Método:** `GET` - **Ruta:** `/api/integrations/fatigue/candidates?client_id=8bec977a-6032-4759-aa7f-b5042081f450` - **Headers:** `X-Api-Key: ` #### Respuesta de ejemplo (`200 OK`): ```json { "client_id": "8bec977a-6032-4759-aa7f-b5042081f450", "tenant_id": "8bec977a-6032-4759-aa7f-b5042081f450", "lookback_days": 7, "rules": { "lookback_days": 7, "min_impressions": 2000, "max_frequency": 3.0, "ctr_drop_pct": 30.0, "cpa_rise_pct": 25.0, "signals_required": 2, "auto_request_relief": true }, "candidates": [ { "id": "c05d4143-f19d-421a-b8d9-9b03d6b0889a", "meta_ad_id": "1202058493829102", "format": "estatico", "status": "publicada", "metrics": [ { "date": "2026-10-10", "impressions": 3000, "clicks": 90, "spend": 45.0, "results": 5 } ] } ] } ``` --- ### 2.4 Marcar Creatividad Fatigada Cambia el estado de la creatividad a `fatigada`, guarda el evento de auditoría y, si `auto_request_relief` está activo, crea automáticamente una petición de relevo en `generation_requests`. - **Método:** `POST` - **Ruta:** `/api/integrations/fatigue/mark` - **Headers:** `X-Api-Key: `, `Content-Type: application/json` #### Payload de ejemplo (`Request Body`): ```json { "client_id": "8bec977a-6032-4759-aa7f-b5042081f450", "creative_id": "c05d4143-f19d-421a-b8d9-9b03d6b0889a", "signals": { "ctr_drop_pct": 32.5, "frequency": 3.4 } } ``` #### Respuesta de ejemplo (`200 OK`): ```json { "ok": true, "creative_id": "c05d4143-f19d-421a-b8d9-9b03d6b0889a", "status": "fatigada", "signals": { "ctr_drop_pct": 32.5, "frequency": 3.4 }, "generation_request": { "id": "377cf30e-81e5-4d80-8f4f-f30a8dd2d239", "status": "pendiente", "reason": "fatiga", "brief": { "client_slug": "bps", "variants_requested": 2, "avoid_angles": ["objecion_precio"], "format": "estatico", "aspect_ratio": "4:5", "aspect_ratios": ["4:5", "9:16", "1.91:1"], "required_placements": [ { "aspect_ratio": "4:5", "role": "feed", "size": "1080x1350", "meta_key": "asset_url" }, { "aspect_ratio": "9:16", "role": "stories", "size": "1080x1920", "meta_key": "stories_asset_url", "canva_template_key": "stories_9_16" }, { "aspect_ratio": "1.91:1", "role": "landscape", "size": "1200x628", "meta_key": "landscape_asset_url", "canva_template_key": "landscape_1_91" } ] } } } ``` --- ### 2.5 Listar Creatividades Aprobadas para Publicar Permite a n8n consultar las creatividades aprobadas pendientes de subirse a Meta Ads. - **Método:** `GET` - **Ruta:** `/api/integrations/creatives?status=aprobada&client_id=8bec977a-6032-4759-aa7f-b5042081f450` - **Headers:** `X-Api-Key: ` #### Respuesta de ejemplo (`200 OK`): ```json [ { "id": "1800fa1e-e969-469c-9913-6c973acba346", "headline": "Aprende a tu ritmo", "primary_text": "Clases de 25 minutos adaptadas a tu agenda laboral con nativos.", "cta": "Empezar gratis", "asset_url": "https://storage.4ventures.es/creatives/bps_v1.jpg", "status": "aprobada", "format": "estatico" } ] ``` --- ### 2.6 Notificar Publicación de Anuncio en Meta Tras crear el anuncio en Meta Ads, n8n actualiza la creatividad con su identificador oficial y la pasa a `publicada`. - **Método:** `PATCH` - **Ruta:** `/api/integrations/creatives/:id` - **Headers:** `X-Api-Key: `, `Content-Type: application/json` #### Payload de ejemplo (`Request Body`): ```json { "client_id": "8bec977a-6032-4759-aa7f-b5042081f450", "meta_ad_id": "1202059998123456", "status": "publicada" } ``` #### Respuesta de ejemplo (`200 OK`): ```json { "id": "1800fa1e-e969-469c-9913-6c973acba346", "meta_ad_id": "1202059998123456", "status": "publicada", "updated_at": "2026-10-12T11:47:10.455Z" } ``` --- ## 3. Endpoints para Claude (Generación de Relevo Creativo) ### 3.1 Obtener Contexto de la Creatividad Fatigada Devuelve a Claude todos los datos necesarios para generar el relevo: el anuncio fatigado, sus métricas, las 3 mejores creatividades históricas del cliente y las reglas de fatiga. - **Método:** `GET` - **Ruta:** `/api/integrations/creatives/:id/context?client_id=8bec977a-6032-4759-aa7f-b5042081f450` - **Headers:** `X-Api-Key: ` --- ### 3.2 Crear Variantes Generadas Inserta las variantes generadas por Claude. Nacen en estado `borrador`, con visibilidad `agency` y `created_by = 'claude'`. - **Método:** `POST` - **Ruta:** `/api/integrations/creatives` - **Headers:** `X-Api-Key: `, `Content-Type: application/json` #### Payload de ejemplo: ```json { "client_id": "8bec977a-6032-4759-aa7f-b5042081f450", "generation_request_id": "377cf30e-81e5-4d80-8f4f-f30a8dd2d239", "creatives": [ { "format": "estatico", "angle": "objecion_horarios", "hook": "¿Sin tiempo para aprender inglés?", "primary_text": "Clases de 25 minutos adaptadas a tu agenda.", "headline": "Aprende a tu ritmo", "cta": "Empezar gratis", "asset_url": "https://storage.4ventures.es/creatives/bps_v1.jpg" } ] } ``` --- ### 3.3 Completar Petición de Generación Actualiza el estado de la tarea en la cola de Claude (`completada` o `fallida`), anotando los IDs generados y el coste estimado en tokens/créditos. - **Método:** `PATCH` - **Ruta:** `/api/integrations/generation-requests/:id` - **Headers:** `X-Api-Key: `, `Content-Type: application/json` #### Payload de ejemplo: ```json { "client_id": "8bec977a-6032-4759-aa7f-b5042081f450", "status": "completada", "result": { "creatives": ["1800fa1e-e969-469c-9913-6c973acba346"], "notes": "Variante enfocada en flexibilidad horaria", "skills_used": ["cliente-blue-parrot", "formato-estatico-4-5"] }, "cost_estimate": { "tokens": 8500, "credits": 1 } } ``` --- ## 4. Webhooks Salientes hacia n8n La aplicación emite llamadas HTTP POST hacia la URL configurada en `N8N_WEBHOOK_BASE` (`https://n8n-agencia.4ventures.es/webhook/creative-...`). Cada webhook se envía con la cabecera: ```http X-Signature: ``` ### Eventos emitidos: 1. `creative.approved`: Un usuario cliente o de agencia aprueba la creatividad. 2. `creative.rejected`: Un usuario rechaza la creatividad (requiere comentario obligatorio). 3. `creative.sent_to_client`: La agencia envía la creatividad al cliente para su revisión. --- ## 5. Verificación de Firma HMAC en n8n (Nodo Code) En el flujo de n8n que recibe el webhook (`Webhook Node`), agrega un nodo **Code** en modo `Run Once for Each Item` con el siguiente código JavaScript para verificar la autenticidad antes de procesar la petición: ```javascript const crypto = require('crypto'); // 1. Obtener la firma enviada en la cabecera const signature = $input.item.headers['x-signature']; if (!signature) { throw new Error('Cabecera X-Signature no proporcionada en el Webhook'); } // 2. Secreto compartido (obtenido de las credenciales de n8n o variables de entorno) const sharedSecret = $env['N8N_WEBHOOK_SECRET'] || 'whsec_4v_7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d'; // 3. Obtener el cuerpo recibido (raw string exacto o serializado) const bodyPayload = typeof $input.item.body === 'string' ? $input.item.body : JSON.stringify($input.item.body); // 4. Calcular el HMAC esperado const expectedSignature = crypto .createHmac('sha256', sharedSecret) .update(bodyPayload) .digest('hex'); // 5. Comparación segura const isValid = crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expectedSignature) ); if (!isValid) { throw new Error('Firma HMAC no válida: webhook rechazado'); } return { json: { verified: true, event: $input.item.body.event, data: $input.item.body.data } }; ```