La señal de churn más confiable que tiene un equipo de CS es que el uso del producto se desplome, y la forma más común en que esa señal se pierde es que nadie está mirando la semana correcta. Para cuando un QBR saca a la superficie la caída, la cuenta lleva dos meses en silencio. Este workflow cierra ese gap con el mecanismo más pequeño posible: un flow semanal de n8n que lee el conteo de usuarios activos de cada cuenta desde Amplitude, compara la semana pasada con la anterior, y envía un mensaje directo de Slack al CSM dueño cuando la caída cruza un umbral que el lead de CS Ops controla por cuenta. Hace una sola cosa —sacar a la superficie una caída de uso semana-contra-semana mientras sigue siendo el problema de esta semana— y lo hace sin un dashboard que nadie abre.
El bundle del artifact vive en apps/web/public/artifacts/usage-drop-alert-n8n/. El export de n8n es usage-drop-alert-n8n.json y la guía de credenciales, schema y verificación es _README.md. Ambos son lectura obligatoria antes de activar el schedule, porque el bundle viene con credenciales de placeholder y dos tablas de Postgres que tienen que existir antes de la primera corrida.
Cuándo usarlo
Úsalo cuando eres un lead de CS Ops corriendo un book de cuentas que ha crecido más allá de revisar a ojo un dashboard de uso —en algún punto por encima de 50 cuentas por CSM, donde nadie puede mantener el portfolio completo en la cabeza. Necesitas Amplitude (o una herramienta de product analytics a la que el nodo HTTP pueda re-apuntarse) rastreando una señal de usuarios activos por cuenta, un workspace de Slack, y un lugar donde guardar umbrales por cuenta. El flow es la elección correcta cuando la organización de CS ya confía en el uso del producto como indicador líder pero no tiene un mecanismo que empuje la señal a un humano antes del próximo QBR.
Encaja especialmente bien como la capa inicial barata debajo de un modelo de salud más pesado. Si ya estás corriendo el customer health score compuesto en n8n, este flow es el complemento de reacción rápida: el score compuesto se recalcula cada noche y te dice dónde está parada una cuenta, mientras que esta alerta dispara semanalmente y te dice qué acaba de moverse. Muchos equipos levantan la alerta primero porque es una tarde de trabajo y gana confianza en días, y después gradúan al compuesto una vez que los CSMs están actuando sobre los pings.
Cuándo NO usarlo
Sáltalo si un CSM puede leer el portfolio entero a mano. Por debajo de unas 30 cuentas por CSM, un humano escaneando el dashboard de uso el lunes por la mañana atrapa las mismas caídas con más contexto, y el costo de falsos positivos de un umbral automatizado no vale la pena. El flow gana su lugar por volumen, no por astucia.
Sáltalo si tu tagging a nivel de cuenta en Amplitude no es confiable. El flow entero descansa en que gp:account_id (o tu propiedad de usuario equivalente) esté seteado consistentemente en cada evento. Si las cuentas están tagueadas de forma inconsistente —algunos eventos cargan la propiedad, otros no— el conteo de activos semanales no significa nada y la alerta dispara sobre un artefacto de tagging, no sobre un cambio de comportamiento. Arregla la taxonomía primero; una alerta sobre datos sucios es peor que ninguna alerta, porque carga la autoridad de un número.
Sáltalo si la caída que te importa es a nivel de asiento o de feature en vez de a nivel de cuenta. Este flow vigila una señal —activos únicos semanales por cuenta— y una caída del 40% en activos totales puede esconder una cuenta sana que simplemente perdió un power user durante una semana de feriado. Si tu riesgo de churn vive en el abandono de un feature específico o en un solo champion nombrado quedándose en silencio, necesitas un cohort por-feature o por-usuario, que es un flow distinto (más pesado). Y sáltalo si el equipo no tiene un playbook de qué hacer cuando una alerta de caída dispara; una notificación sin acción siguiente definida entrena a la gente a descartarla.
Setup
El setup está documentado de punta a punta en apps/web/public/artifacts/usage-drop-alert-n8n/_README.md. La versión corta: importa el JSON en n8n bajo Settings → Import From File, crea las tres credenciales de placeholder (Postgres, Amplitude Basic auth, Slack bot token), crea las dos tablas de Postgres a partir del DDL en el README (accounts_in_scope y usage_alert_history), siembra una cuenta canary, y corre la secuencia de verificación de ocho pasos antes de activar el schedule. Desde una instalación limpia de n8n, presupuesta 45 a 90 minutos —la mayor parte gastada confirmando que la query de segmentación de Amplitude coincide con tu taxonomía de eventos y que la app de Slack puede mandar DM a usuarios en tu workspace.
La tabla accounts_in_scope es donde vive la política por cuenta, y acertar con ella es la diferencia entre una alerta útil y un bot muteado. Cada fila carga drop_threshold_pct (el porcentaje de caída que dispara una alerta) y min_baseline_events (el piso de usuarios activos por debajo del cual la cuenta es demasiado pequeña para juzgar). Las cuentas enterprise a menudo corren un umbral más estricto —una caída del 25% en una cuenta de 200 asientos vale la pena mirar— mientras que las cuentas self-serve toleran más ruido y corren al 50%. Mantener estos como columnas de tabla en vez de constantes hard-coded significa que re-afinar es un solo UPDATE, no un redeploy.
Qué hace el flow en realidad
El cron dispara el lunes a las 09:00 en America/New_York (la expresión es 0 13 * * 1 —13:00 UTC— así que confirma que el timezone del workflow esté seteado). El lunes por la mañana es deliberado: la semana previa está completamente cerrada, así que no hay comparación de semana-parcial que leería cada lunes como una caída. Pull Accounts In Scope lee hasta 500 cuentas activas que tienen un Slack id de CSM seteado; las cuentas sin dueño se filtran en SQL porque no hay a quién notificar. Batch Accounts (20/group) las divide en grupos para que las llamadas paralelas a Amplitude se mantengan bajo el cap de concurrencia de la Dashboard REST API, con una espera de un segundo entre batches.
Amplitude — Weekly Actives (14d) llama al endpoint /api/2/events/segmentation con i=7 (buckets semanales) sobre una ventana de 14 días, segmentado sobre la propiedad gp:account_id de la cuenta. Eso devuelve dos puntos semanales: la semana pasada y la anterior. Compute WoW Drop es la única lógica real en el flow y toma dos decisiones. Primero, el guard de ruido: si el conteo de activos de la semana previa está por debajo de min_baseline_events, la cuenta se marca skipped_low_baseline y nunca alerta —un cambio de cuatro activos a dos es una caída del 50% y puro ruido. Segundo, el umbral: computa (week_before - last_week) / week_before como porcentaje y, solo si eso alcanza o excede el drop_threshold_pct de la cuenta, marca la fila alert con una razón legible como “los activos semanales cayeron 47% (de 120 a 64) vs la semana previa”.
Crosses Threshold? rutea las filas de alerta hacia adelante; todo lo demás va directo al throttle. Lookup Recent Alert después checa usage_alert_history por cualquier alerta sobre esta cuenta en los últimos 14 días, y Outside Cooldown? suprime la repetición si existe una. Este es el segundo guard contra la fatiga: una caída sostenida de otra forma le haría ping al CSM cada lunes hasta que el uso se recupere, lo que lo entrena a ignorar el bot. Con el cooldown, una caída real hace ping una vez, y el CSM se hace dueño del seguimiento desde ahí.
Las filas que sobreviven golpean Slack — DM Owning CSM, que postea un mensaje Block Kit directamente al Slack user id del CSM con el nombre de la cuenta, el segmento, los conteos de activos antes/después, el porcentaje de caída, y el umbral que disparó. Persist Alert (idempotent per week) escribe la alerta en usage_alert_history con una cláusula ON CONFLICT encadenada en (account_id, date_trunc('week', alerted_at)), así que una corrida reintentada actualiza la fila existente en vez de mandar DM al CSM dos veces, y estampa last_alerted_at en la cuenta para la lectura de cooldown del camino rápido.
Realidad de costos
Este flow es casi gratis de correr. No hay llamada a LLM —la comparación es aritmética en un nodo Code, así que el único costo es la cuota de API y el tiempo de ejecución de n8n. Por cuenta por semana el flow hace una lectura de segmentación de Amplitude, como máximo una escritura de Slack, y dos o tres queries de Postgres. La Dashboard REST API de Amplitude no factura por llamada en planes pagos; la restricción es su límite bajo de concurrencia, que es exactamente por qué el batch size es 20 con un throttle de un segundo. Para 500 cuentas la corrida entera se completa en aproximadamente tres a seis minutos en el ejecutor pequeño de n8n Cloud, dominada por las lecturas serializadas de Amplitude. El chat.postMessage de Slack está rate-limited a aproximadamente un mensaje por segundo por contexto de canal, cómodamente bajo lo que un volumen de alerta semanal necesita.
El costo real es humano, y es el costo que estás tratando de reducir: un lead de CS Ops gasta tal vez una hora al trimestre re-afinando umbrales conforme los segmentos cambian, contra la alternativa de que los CSMs cada uno gaste 20 a 30 minutos a la semana revisando dashboards a ojo (o, más a menudo, no haciéndolo y enterándose en el QBR). En un equipo de 10 CSMs eso son aproximadamente 40 a 50 horas al trimestre de escaneo manual reemplazadas por una hora de mantenimiento de umbrales —y el escaneo estaba atrapando caídas un mes tarde de todos modos.
Cómo se ve el éxito
Vigila tres números en el primer trimestre. Primero, la tasa de acción sobre las alertas —la proporción de DMs que resultan en un touch logueado del CSM (un email, una llamada agendada, una nota) dentro de cinco días hábiles. Encuesta o instrumenta esto; apunta por encima del 60% para el fin del primer mes. Una tasa de acción por debajo del 40% significa que el umbral está demasiado flojo y el bot está gritando lobo —sube drop_threshold_pct para los segmentos ruidosos. Segundo, el lead time a la intervención —para cuentas que después hicieron churn o se contrajeron, mide cuántos días la alerta de caída de uso precedió al primer outreach del CSM versus el baseline histórico de “se enteró en el QBR”. El punto entero es mover ese número de aproximadamente 60 días a menos de 14. Tercero, la tasa de supresión —la proporción de cuentas que cruzaron el umbral pero fueron retenidas por el cooldown. Un número saludable es bajo y estable; una tasa de supresión en aumento significa que un cohort está en declive sostenido y la alerta semanal ya no es la herramienta correcta —esas cuentas necesitan el customer health score compuesto y un save play, no otro ping.
Versus las alternativas
La alternativa por default es el alerting propio de Amplitude —sus monitores de Anomaly y Threshold pueden vigilar un chart y disparar a Slack o email. Si necesitas exactamente una alerta global (“los activos semanales totales cayeron”), usa el monitor nativo de Amplitude; es menos trabajo que levantar n8n. La razón por la que este flow existe es el ruteo por cuenta: los monitores de Amplitude alertan sobre un chart, no sobre un mapeo cuenta-a-CSM, así que un monitor a nivel de portfolio no puede decirle al CSM dueño que su cuenta cayó. Para sacar ruteo por-cuenta, por-dueño solo de Amplitude terminas construyendo un monitor por cuenta, lo que no escala más allá de un puñado. Este flow mantiene el mapa cuenta-a-CSM y los umbrales por cuenta en una tabla que tú posees y rutea acordemente.
Una segunda alternativa son las alertas de uso integradas de tu CSP —Gainsight, Catalyst, ChurnZero, Vitally, Planhat y Totango todos vienen con alguna forma de trigger de caída de uso. Si ya corres un CSP y canalizas el uso del producto hacia él, usa el trigger nativo —los datos ya están ahí y el ruteo al CSM ya está cableado. Este flow es para el equipo que tiene product analytics en Amplitude pero no ha centralizado el uso en un CSP todavía, o cuyo CSP retrasa los datos de uso de Amplitude por un ciclo de sync. Es el puente que entrega el indicador líder antes de que el rollup del CSP se ponga al día.
Una tercera alternativa es un script DIY en un cron —un job de Python golpeando la API de Amplitude y la API de Slack. Es más rápido escribir la primera versión que cablear el flow de n8n, pero carga el peso de rotación de credenciales en código, no tiene semántica de reintentos out of the box, y es invisible para el lead de CS Ops que no es ingeniero. La versión de n8n cambia flexibilidad cruda por una UI de credenciales, reintentos integrados, y un flow visual que un no-ingeniero puede leer y re-afinar. Elige DIY si CS Ops tiene un ingeniero permanente; elige el flow de n8n si la persona afinando umbrales es la misma persona leyendo las alertas.
A vigilar
Un artefacto de tagging se lee como un desplome de uso. Si la instrumentación del producto cambia —un evento se renombra, la propiedad account_id deja de setearse en una superficie— cada cuenta en esa superficie muestra una caída a cero y el bot manda DM a todos los CSMs a la vez. Guarda: antes de activar, consulta el conteo distinto de cuentas con gp:account_id no-nulo de las últimas dos semanas y confirma que es estable; y trata un pico de volumen de alerta en la misma semana a través de muchas cuentas como un incidente de instrumentación, no una ola de churn —la tabla usage_alert_history hace ese pico visible de un vistazo.
Las cuentas pequeñas generan caídas fantasma. Una cuenta con cuatro activos semanales cayendo a dos es una caída del 50% y no significa nada. Guarda: el piso min_baseline_events en accounts_in_scope marca cualquier cuenta por debajo del umbral de la semana previa como skipped_low_baseline y nunca alerta sobre ella. Setea el piso por segmento —self-serve puede correr un piso de 5, enterprise rara vez necesita uno.
Las caídas sostenidas spamean al CSM. Sin supresión, una cuenta que cae y se queda abajo dispararía cada lunes hasta que se recupere. Guarda: Lookup Recent Alert más el cooldown de 14 días en Outside Cooldown? asegura una alerta por evento de caída; el CSM se hace dueño del seguimiento después del primer ping, y una cuenta aún en declive sale a la superficie en el customer health score compuesto, no en una alerta repetida.
Los reintentos mandan doble DM. Una falla de nodo a mitad de batch que dispara un reintento de n8n podría mandar el DM de Slack dos veces. Guarda: usage_alert_history tiene un índice único en (account_id, date_trunc('week', alerted_at)) y Persist Alert usa ON CONFLICT ... DO UPDATE, así que el segundo intento actualiza la fila semanal existente en vez de insertar una nueva —y como la escritura de Slack precede al persist, la lectura de cooldown en el reintento lo atrapa.
El DM llega y no pasa nada. Una alerta sin paso siguiente definido es ruido con un timestamp. Guarda: este es un guard de proceso, no de código —empareja el rollout con un playbook de una línea (“DM de caída de uso → checa la cuenta en tu CSP → loguea un touch dentro de cinco días hábiles”) y rastrea la tasa de acción de arriba. Si la tasa de acción es baja, el arreglo es el playbook o el umbral, no más alertas.
Stack
n8n —orquestación, el schedule semanal, reintentos, gestión de credenciales, y un flow visual que un lead de CS Ops puede re-afinar sin un ingeniero
Amplitude —la fuente de uso del producto; activos únicos semanales por cuenta vía el endpoint events/segmentation de la Dashboard REST
Slack —el canal de entrega; un DM Block Kit al user id del CSM dueño (re-apuntable a un canal compartido)
Postgres —accounts_in_scope para umbrales por cuenta y ruteo de CSM, usage_alert_history para el cooldown y la clave de idempotencia
# Usage-drop alert for CSMs — n8n flow
## What this flow does
This flow runs every Monday at 09:00 in `America/New_York` and checks every active account for a week-over-week drop in product usage. For each account it pulls two weekly buckets of unique active users from Amplitude (last week and the week before), computes the percentage drop, and compares it against a per-account threshold stored in Postgres. Accounts whose drop crosses the threshold — and that are not inside a 14-day cooldown from a prior alert — trigger a Slack direct message to the owning CSM naming the account, the before/after active-user counts, and the percentage drop. Every alert is logged to a history table so a sustained dip pings the CSM once, not every week.
The flow is deliberately small: one external read (Amplitude), one decision (threshold), one suppression check (cooldown), one notification (Slack), one write (history). It is the leading-indicator companion to a full composite health score, not a replacement for one.
## Import
In n8n: open **Settings → Import From File → select `usage-drop-alert-n8n.json`**. After import, open the workflow and confirm the timezone in **Workflow Settings** is `America/New_York` (it ships set, but reconfirm — the schedule trigger and the cron's `13:00 UTC` expression both assume it). Activate the workflow only after credentials are wired and the verification run below has passed.
## Credentials
Two placeholder credentials are referenced by name in the export. Create each in n8n under **Credentials → New** and map the matching `PLACEHOLDER_*_CRED_ID` reference on first open. (Postgres is the third — it backs the state tables and is also referenced by name.)
### `PLACEHOLDER_POSTGRES_CRED_ID` — Postgres — usage-alert-state
Used by three nodes: `Pull Accounts In Scope`, `Lookup Recent Alert`, and `Persist Alert (idempotent per week)`. Point this at a Postgres database you control. Required tables:
```sql
CREATE TABLE accounts_in_scope (
account_id text PRIMARY KEY,
account_name text NOT NULL,
amplitude_project_id text,
segment text,
active boolean NOT NULL DEFAULT true,
drop_threshold_pct int NOT NULL DEFAULT 40, -- per-account % drop that triggers an alert
min_baseline_events int NOT NULL DEFAULT 10, -- floor below which the account is too small to judge
csm_slack_user_id text, -- Slack user id of the owning CSM (e.g. U0123ABCD)
last_alerted_at timestamptz
);
CREATE TABLE usage_alert_history (
account_id text NOT NULL,
alerted_at timestamptz NOT NULL DEFAULT now(),
week_before int,
last_week int,
drop_pct int,
threshold int,
reason text
);
-- Idempotence key: one row per account per week, so retries do not double-log or double-DM.
CREATE UNIQUE INDEX usage_alert_history_week_uniq
ON usage_alert_history (account_id, date_trunc('week', alerted_at));
```
### `PLACEHOLDER_AMPLITUDE_CRED_ID` — Amplitude — API key:secret (Basic)
Amplitude's Dashboard REST API uses HTTP Basic auth where the username is the project **API Key** and the password is the project **Secret Key**. Find both in Amplitude under **Settings → Projects → [your project] → General**. In n8n create a **Basic Auth** credential: username = API Key, password = Secret Key. The flow calls the `/api/2/events/segmentation` endpoint, which needs no extra scope beyond a valid key pair. Note the endpoint returns event-segmentation series; the node's query segments on a `gp:account_id` user property — rename that to whatever account identifier your Amplitude taxonomy uses, and replace the `_active` event with your own activity event if you do not track a synthetic `_active` event.
### `PLACEHOLDER_SLACK_CRED_ID` — Slack — bot token
In your Slack workspace under **api.slack.com/apps**, create an app with a bot user and the scopes `chat:write` and `im:write` (the latter is required to open a DM channel with a user). Install the app to the workspace and copy the bot token (`xoxb-...`). Store it as a header credential with header name `Authorization` and prefix value `Bearer `. Because the flow DMs the CSM by Slack user id, each CSM must have **"Allow users in your workspace to send you direct messages"** enabled and the app must not be blocked. If your org restricts app DMs, point the `channel` field at a shared channel such as `#cs-usage-alerts` and tag the CSM in the message text instead.
## First-run verification
Run the flow manually before activating the schedule. This sequence proves each branch without spamming CSMs.
1. **Seed one canary account.** Insert a single row into `accounts_in_scope` with a real `account_id` that exists in Amplitude, your own Slack user id in `csm_slack_user_id`, `drop_threshold_pct = 1` (so any drop fires), and `min_baseline_events = 1`.
2. **Run `Pull Accounts In Scope` in isolation.** Confirm the canary row comes back. If empty, check `active = true` and that `csm_slack_user_id` is non-null (the `WHERE` clause filters out null Slack ids).
3. **Run `Amplitude — Weekly Actives (14d)`.** Confirm a non-empty `data.series` array with at least two weekly values. A 400 usually means the `gp:account_id` property name or the event name does not match your taxonomy; a 401 means the Basic auth key/secret pair is wrong.
4. **Run `Compute WoW Drop`.** Confirm `week_before`, `last_week`, `drop_pct`, and `status` are populated. Temporarily hand-edit the canary's Amplitude data (or pin a fixture) so `last_week` is well below `week_before` and confirm `status` becomes `alert`. Then set `min_baseline_events` above `week_before` and confirm `status` becomes `skipped_low_baseline` — that proves the noise guard works.
5. **Check the cooldown path.** With `usage_alert_history` empty, `Outside Cooldown?` should route to the Slack node. Manually insert a row into `usage_alert_history` for the canary dated yesterday, re-run, and confirm `Outside Cooldown?` now routes to the throttle (suppressed). Delete the test row afterward.
6. **Fire one real DM.** With the cooldown clear, let the flow run end-to-end on the canary. Confirm you receive the Slack DM with the account name, the before/after counts, and the drop percentage, and that one row landed in `usage_alert_history`.
7. **Re-run the same day.** Confirm no second DM arrives and `usage_alert_history` still has exactly one row for the week (the `ON CONFLICT` clause is doing its job).
8. **Restore real thresholds.** Set `drop_threshold_pct` and `min_baseline_events` back to production values (40 and 10 are sensible defaults) before activating the schedule.
If any step fails, fix it before activating. A weekly cron that DMs CSMs about phantom drops will train them to mute the bot inside a month — the noise guard and the cooldown exist specifically to keep that from happening.