🔗

CULTIVA IA — Gemini Webhook Handler

Servidor Express.js de produccion para recibir notificaciones asincronas de la API de Google Gemini en el pipeline de contenido masivo de CULTIVA IA.

Node.js 20 Produccion Standard Webhooks HMAC-SHA256 Gemini API
📦 Eventos manejados
9
tipos de eventos Gemini
batch · video · interaction
🔒 Seguridad
100%
verificacion por firma
Anti-replay · Rotacion de secretos
Latencia objetivo
<30s
respuesta requerida
Gemini reintenta 24h con backoff
Flujo de procesamiento de un webhook
Gemini API
POST Event
3 headers firmados
Paso 1
Verificar firma
HMAC-SHA256 + anti-replay
Paso 2
Parsear JSON
raw body → event
Paso 3
Idempotencia
webhook-id registrado
Paso 4
Despachar handler
por event.type
Paso 5
200 OK
{ received: true }
Catalogo de eventos manejados
Evento API Estado resultado Accion CULTIVA IA
batch.succeeded Batch API succeeded Actualiza DB + Notifica cliente con output count
batch.failed Batch API failed Registra error + Alerta al cliente con mensaje
batch.cancelled Batch API cancelled Marca trabajo cancelado en DB
batch.expired Batch API expired Notifica cliente: trabajo expirado (>24h)
video.generated Veo API video_ready Guarda URI del video + Notifica cliente
interaction.completed Interactions API interaction_done Almacena resultado del agente
interaction.requires_action Interactions API requires_action Encola function-call para worker
interaction.failed Interactions API interaction_failed Log de error estructurado
interaction.cancelled Interactions API interaction_cancelled Marca cancelacion en DB
Verificacion de firma (Standard Webhooks)
JavaScript verifyGeminiSignature()
// 1. Validar cabeceras requeridas
if (!webhookId || !webhookSignature.includes(','))
  return { ok: false, reason: 'missing_headers' };

// 2. Anti-replay: ventana de ±5 min
const diff = currentTime - parseInt(webhookTimestamp);
if (diff > 300 || diff < -300)
  return { ok: false, reason: 'timestamp_out_of_range' };

// 3. Construir contenido firmado
const signedContent =
  `${webhookId}.${webhookTimestamp}.${payloadStr}`;

// 4. Decodificar secreto (quitar prefijo whsec_)
const secretBytes = Buffer.from(
  secret.slice(6), 'base64'
);

// 5. Comparacion timing-safe (anti timing-attack)
crypto.timingSafeEqual(sigBuf, expectedBuf);
Simulacion de log en tiempo real
Console output CULTIVA-IA webhook server :3001
2026-06-16T09:14:03Z INFO webhook.received {"eventType":"batch.succeeded"}
2026-06-16T09:14:03Z INFO [JOB] batch_4f8a → succeeded {"outputCount":247}
2026-06-16T09:14:03Z INFO [NOTIFY] → cliente_42: Batch completado (247 outputs)
2026-06-16T09:17:51Z INFO webhook.received {"eventType":"video.generated"}
2026-06-16T09:17:51Z INFO video.generated vid_veo_8c2d ready {"uri":"gs://veo/..."}
2026-06-16T09:22:10Z WARN webhook.rejected {"reason":"timestamp_out_of_range"}
2026-06-16T09:28:44Z WARN interaction.requires_action {"action":"function_call"}
Configuracion de entorno
GEMINI_WEBHOOK_SECRET whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxx Secreto de firma del webhook (base64, prefijo whsec_). Generado al crear el endpoint via WebhookService API.
GEMINI_API_KEY AIzaSy_xxxxxxxxxxxxxxxxxxxx Clave de API de Google Gemini para llamadas salientes (Batch, Veo, Interactions).
PORT 3001 Puerto del servidor Express. Por defecto 3001 si no se define.
Endpoints del servidor
📥 Webhook principal
POST /webhooks/gemini
Recibe y verifica eventos de Gemini. Usa express.raw() para preservar el body sin parsear para la firma.
💚 Health check
GET /health
Estado del servidor: jobs, notifications, uptime. Apto para load balancers y k8s liveness probes.
📋 Lista de trabajos
GET /jobs
Array de todos los trabajos procesados con su estado actual. En produccion, sustituir el Map en memoria por una query a base de datos.
Desarrollo local con Hookdeck
Gemini API requiere una URL publica para enviar webhooks. En desarrollo, usa Hookdeck CLI para crear un tunel sin necesidad de cuenta:
Terminal dev setup
# Instalar dependencias
npm install express

# Arrancar el servidor en una terminal
GEMINI_WEBHOOK_SECRET=whsec_test node resultado.js

# En otra terminal: tunel publico a localhost:3001
npx hookdeck-cli listen 3001 gemini --path /webhooks/gemini

# Output del tunel:
⣾ Listening on: https://abc123.hookdeck.io/webhooks/gemini
# Usar esta URL en la configuracion del webhook de Gemini API