ComunidadFelizCatalogo
Deduplicator9000

Deduplicator9000

Servicio webhook que detecta negocios duplicados en HubSpot por direccion: normaliza la direccion con IA y geocoding, busca coincidencias entre objetos y publica un informe como nota.

Endpoints

GET/api/healthhealth
GET/api/metrics/menumetrics menu
GET/api/metricsmetrics

Capabilities

apicron

Variables de entorno

HUBSPOT_API_KEYToken de la app privada de HubSpot (Bearer) con scopes de CRM para deals, custom objects y notas
HUBSPOT_APP_SECRETClient secret de la app de HubSpot; valida la firma v3 del webhook entrante
GEOCODING_API_KEYAPI key de Google Geocoding API (excepcion de stack documentada en docs.md)

Documentacion

# Deduplicator9000

Servicio interno que detecta negocios duplicados en HubSpot por similitud de direccion. Cuando una persona cambia la direccion de un Deal o de un custom object, el servicio normaliza la direccion con IA (Gemini) y Google Geocoding, la guarda en `direccion_normalizada`, busca coincidencias entre 4 objetos del CRM y publica un informe de posibles duplicados como nota en el record.

## Como funciona

1. HubSpot envia un webhook `object.propertyChange` a `POST /api/webhook/hubspot`.
2. El servicio valida la firma v3 de HubSpot (HMAC) y filtra el evento (objeto valido + propiedad de direccion).
3. En background: corrige la direccion con Gemini -> geocoding -> guarda `direccion_normalizada` en el record.
4. Busca coincidencias de `direccion_normalizada` en Negocios, Comunidades, Comunidades con otro software y Postulando a comunidades.
5. Genera un "Certificado de Duplicidades" con Gemini y lo postea como nota asociada al record.

Loop-protection: si el ultimo cambio de la propiedad lo hizo la propia app, o el `contador_deduplicator` supera 10, el evento se descarta.

## Objetos de HubSpot

| Objeto                        | type_id    | Propiedad                         |
| ----------------------------- | ---------- | --------------------------------- |
| Negocios (Deals)              | 0-3        | calle_numero                      |
| Comunidades                   | 2-26552810 | calle\_\_\_numero (solo se busca) |
| Comunidades con otro software | 2-38809341 | calle\_\_\_numero                 |
| Postulando a comunidades      | 2-38812538 | calle\_\_\_numero                 |

## Endpoints

| Metodo | Ruta                            | Auth                | Descripcion                       |
| ------ | ------------------------------- | ------------------- | --------------------------------- |
| POST   | /api/webhook/hubspot            | Firma v3 de HubSpot | Receptor del webhook              |
| GET    | /api/health                     | Publico             | Health check                      |
| GET    | /api/metrics, /api/metrics/menu | x-api-key           | Metricas para el Hub              |
| GET    | /api/cron/backfill-direccion    | x-api-key           | Backfill de direccion_normalizada |

## Variables de entorno

| Variable           | Descripcion                                                        |
| ------------------ | ------------------------------------------------------------------ |
| HUBSPOT_API_KEY    | Token de la app privada de HubSpot (Bearer)                        |
| HUBSPOT_APP_SECRET | Client secret de la app de HubSpot; valida la firma v3 del webhook |
| GEOCODING_API_KEY  | API key de Google Geocoding API                                    |

Gemini usa la service account del Hub (`HUB_GOOGLE_SA_CREDENTIALS`), inyectada por el Hub; no requiere API key propia.

## Excepcion de stack: Google Geocoding API

El stack aprobado no incluye APIs externas de Google salvo casos puntuales. Este servicio usa **Google Geocoding API** (`maps.googleapis.com`) para normalizar y geolocalizar direcciones (mayormente chilenas), funcion central del producto. Requiere `GEOCODING_API_KEY` y la aprobacion del owner del Hub antes de deployar.

## Backfill

El `backfill.py` del proyecto original se migro a un cron idempotente (`/api/cron/backfill-direccion`, protegido con `x-api-key`): recorre los custom objects con direccion pero sin `direccion_normalizada`, normaliza en lotes (Gemini + geocoding) y actualiza HubSpot. Correrlo manualmente: `GET /api/cron/backfill-direccion` con el header `x-api-key`.

## Notas de la migracion

- Origen: servicio Flask en Cloud Run (`--allow-unauthenticated`). El webhook no validaba el origen; ahora valida la firma v3 de HubSpot.
- Secrets: se dejo de usar Google Secret Manager; las variables las inyecta el Hub.
- El procesamiento pesado corre en background (el server de Railway es persistente), equivalente al thread del servicio original.