AI Digest semanal
Cada lunes, Iterar genera un resumen de una página por proyecto con los patrones que detectó la IA, el sentiment de la semana, los items que más crecieron y 3 acciones sugeridas. Se manda por mail al founder y queda en /dashboard/projects/[slug]/digests.
Cómo funciona
- Una sola llamada a Gemini Flash por proyecto por semana (~1 cent / digest).
- Idempotente: si ya hay un digest para esa semana y se envió, no se duplica.
- Se omiten proyectos sin actividad en los últimos 7 días (
skipped_reason = no_activity). - Honra
user_profiles.preferred_language(es / en / pt) para el texto generado. - Si Resend falla, el digest queda guardado pero con
delivered_at = null.
Endpoint
POST /api/ai/digest/run — protegido conAuthorization: Bearer $INTERNAL_API_TOKEN. Body opcional:
{}o vacío → corre para todos los proyectos en plan Starter/Pro/Studio.{ "project_id": "..." }→ corre solo para ese proyecto.{ "force": true }→ regenera aunque ya haya digest entregado.{ "include_free": true }→ incluye proyectos del plan Free (mayormente útil en dev).
curl -X POST https://app.iterar.io/api/ai/digest/run \
-H "Authorization: Bearer $INTERNAL_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{}'curl -X POST https://app.iterar.io/api/ai/digest/run \
-H "Authorization: Bearer $INTERNAL_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"project_id":"00000000-0000-0000-0000-000000000000","force":true}'Programar el cron
Elegí la opción que mejor se lleve con tu stack. Recomendamos Mondays 12:00 UTC (≈ 9am ART, 7am MEX, 9am ESP).
Opción 1: Supabase Scheduled Function (pg_cron + pg_net)
Necesitás habilitar las extensiones pg_cron y pg_net desde Database → Extensions. Después seteá el token conalter database postgres set app.internal_api_token = '...'; y corré:
-- Supabase: pg_cron + pg_net (Database → Extensions)
select cron.schedule(
'iterar-weekly-digest',
'0 12 * * 1', -- Mondays 12:00 UTC (≈ 9am ART)
$$
select net.http_post(
url := 'https://app.iterar.io/api/ai/digest/run',
headers := jsonb_build_object(
'Authorization', 'Bearer ' || current_setting('app.internal_api_token'),
'Content-Type', 'application/json'
),
body := '{}'::jsonb
);
$$
);Opción 2: Cloudflare Cron Trigger
Un Worker mínimo:
# wrangler.toml
name = "iterar-digest-cron"
main = "src/worker.ts"
compatibility_date = "2025-05-01"
[triggers]
crons = ["0 12 * * 1"] # Mondays 12:00 UTC
[vars]
ITERAR_URL = "https://app.iterar.io/api/ai/digest/run"
# INTERNAL_API_TOKEN comes from `wrangler secret put INTERNAL_API_TOKEN`// src/worker.ts
export default {
async scheduled(_event: ScheduledEvent, env: Env) {
const res = await fetch(env.ITERAR_URL, {
method: "POST",
headers: {
"Authorization": `Bearer ${env.INTERNAL_API_TOKEN}`,
"Content-Type": "application/json",
},
body: "{}",
});
if (!res.ok) {
console.error("digest run failed", res.status, await res.text());
}
},
};
interface Env {
ITERAR_URL: string;
INTERNAL_API_TOKEN: string;
}Opción 3: Vercel Cron
Si la app está en Vercel y el token vive en una env var del mismo proyecto, podés usar directamente Vercel Cron — pero ojo: tenés que validar el bearer del headerx-vercel-cron manualmente o autorizar el endpoint con un middleware. El esqueleto JSON:
// vercel.json
{
"crons": [
{ "path": "/api/ai/digest/run", "schedule": "0 12 * * 1" }
]
}maxDuration de la función.Generación manual
Desde /dashboard/projects/[slug]/digests el founder puede tocar Generar ahora, que llama a la server action generateDigestNow(slug). Internamente usa la sesión del usuario para verificar ownership y después invoca deliverWeeklyDigest con force: true — no necesita el bearer token porque la acción se ejecuta del lado server con la service role key.