6.5 KiB
id, fecha, categoria, location_ids, run_ids, snapshots, status, memorias, playbooks
| id | fecha | categoria | location_ids | run_ids | snapshots | status | memorias | playbooks | |||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| CASE-2026-05-30-sucursal-tag-en-leads-digitales | 2026-05-30 | custom_field, cascada_n8n |
|
|
resuelto |
|
TRIGGERS
tag sucursal y facebook-ads juntos,conflicto facebook-ads sucursal,falsa atribución digitalcreatedBy.source INTEGRATION channel OAUTH,origen real facebook ads vs creado por sucursalquitar tag sucursal lead digital,audit_origin_for_contact_list.py,fix_remove_sucursal_tag_digital.pyn8n no re-añade tag sucursal,tag se queda quitado tras 60sMiza Olguin Puebla,contacto Canal SUCURSAL pero opp FORMULARIO,Fuente PROSPECCIÓN no sobrescribir
SÍNTOMA
Export de 87 contactos de Marca con tags facebook-ads + sucursal simultáneos, CANAL DE ORIGEN=FACEBOOK, Fuente de Prospecto=LEAD DIGITAL. El usuario sospechaba que algunos eran creados a mano por staff de sucursal que pusieron apariencia digital falsa (tag facebook-ads + Fuente "redes sociales/LEAD DIGITAL").
DIAGNÓSTICO
La columna source de SQLite guarda el source NATIVO ("Facebook"/"Formulario"), NO createdBy.source (db.py:300). Y es poco confiable (un contacto de sucursal tenía source=null mientras Marca decía "Formulario"). createdBy.source SOLO viene en GET /contacts/{id} individual (createdby_only_in_individual_get).
Como los contactos del export son de Marca (réplica n8n → su createdBy siempre es INTEGRATION), el origen real está en el contacto de la sucursal. Resolución Marca→Sucursal por CF id_contacto_sucursal (id Marca E6lI9ykWhqpj7Pmi7Qd3) → buscar la location en SQLite (SELECT location_id FROM contacts WHERE id=?) → GET en vivo en esa sucursal.
Script: python scripts/audit_origin_for_contact_list.py --csv generated/reports/origin_audit/_input_ids.csv (read-only). Resultado: 87/87 = INTEGRATION (REAL_DIGITAL), 0 manuales. Verificación cruda: createdBy = {"source":"INTEGRATION","channel":"OAUTH",...}. El detector SÍ distingue manuales (un contacto de control de otra cuenta salió WEB_USER).
Callejón descartado: la nota de agente + Canal=SUCURSAL de Miza NO prueban creación manual; su contacto de sucursal es INTEGRATION (entró digital, un agente la reetiquetó/anotó después).
CAUSA RAÍZ
- Los 87 son leads de origen digital (FB Ads/formulario → integración). El tag
facebook-adsyLEAD DIGITALson correctos. La hipótesis de "fake digital" quedó refutada. - La etiqueta que sobra y genera el conflicto es
sucursal(en la taxonomía MPsucursalyfacebook-adsson tags de ORIGEN mutuamente excluyentes — verfix_branch_user_origin.py). - Caso aislado: Miza Olguin tenía CANAL/Fuente del CONTACTO = SUCURSAL, pero su opp ya estaba en Canal=FORMULARIO / Fuente=PROSPECCIÓN. Su origen real es formulario (no facebook-ads).
ACCIÓN
Dry-run → piloto (1 contacto) → batch, protocolo feedback_dry_run_protocol.
- Quitar tag
sucursalde los 87 en ambos lados (Marca + sucursal),scripts/fix_remove_sucursal_tag_digital.py(mutador, dry-run default, bucle quitar→esperar 60s→verificar para vencer la carrera n8n):- Piloto:
--only iR4fS0f2fOGB75vtVZMP --apply --run-id pilot-sucursal-tag-20260530013645→ Ronda 2 = 0 con tag (n8n NO lo re-añade). - Batch:
--apply --run-id batch-sucursal-tag-20260530013808→ 172 lados limpiados, Ronda 2 = 0.
- Piloto:
- Miza (inline,
run_id fix-miza-canal-fuente-20260530015547): contacto Canal SUCURSAL→FORMULARIO, Fuente SUCURSAL→LEAD DIGITAL en Marca + Puebla. La opp NO se tocó (ya correcta: FORMULARIO/PROSPECCIÓN). Cero opps creadas/modificadas.
VERIFICACIÓN
Sweep final independiente (174 lados, GET vivo): 0 con tag sucursal, 0 perdieron facebook-ads. Miza verificada: Canal=FORMULARIO, Fuente=LEAD DIGITAL en ambos lados tras 60s. Resultado: cambio 100% efectivo.
EDGE-CASES / TRAMPAS
- No basta el
sourcenativo ni la caché: usarcreatedBy.sourceen vivo del contacto de sucursal (no de Marca). facebook-adsera CORRECTO: la instrucción inicial era quitar facebook-ads; los datos dijeron quitarsucursal. Confirmar dirección con el usuario antes de mutar evitó corromper 87 leads legítimos.- Opp vs contacto pueden divergir: Miza tenía contacto=SUCURSAL pero opp=FORMULARIO. Investigar la opp ANTES de elegir el valor del contacto (puse FORMULARIO, no FACEBOOK, para no inyectar inconsistencia contacto-vs-opp).
- No sobrescribir Fuente de opp = PROSPECCIÓN (valor de negocio).
- n8n NO re-añade el tag
sucursala contactos INTEGRATION (piloto lo probó). Quitar un tag no crea opps → cero riesgo de duplicados de opp. DELETE /contacts/{id}/tagscon body{"tags":[...]}(el path/tags/{name}da 404) — ya manejado porremove_contact_tag(ghl_tags_api).
REUTILIZABLE
# Auditar origen real de una lista de contactos de Marca (read-only)
python scripts/audit_origin_for_contact_list.py --ids "ID1,ID2,..." # o --csv <ruta>
# Quitar tag sucursal de los REAL_DIGITAL (dry-run -> piloto -> batch)
python scripts/fix_remove_sucursal_tag_digital.py # dry-run
python scripts/fix_remove_sucursal_tag_digital.py --only <marca_id> --apply --run-id pilot-...
python scripts/fix_remove_sucursal_tag_digital.py --apply --run-id batch-...
PENDIENTES
- Investigar por qué estos digitales recibieron el tag
sucursalhistóricamente (qué workflow/migración lo puso). No se re-añade hoy, pero conviene cerrar la fuente para que no reaparezca en leads futuros.
ENLACES
- Scripts:
scripts/audit_origin_for_contact_list.py,scripts/fix_remove_sucursal_tag_digital.py - Artefactos:
generated/reports/origin_audit/origin_audit_20260530_012600.csv(+_input_ids.csv) - Memorias: createdby_only_in_individual_get, super_script_fix_branch_user_origin, n8n_realtime_replication, ghl_tags_api