--- id: CASE-2026-05-30-sucursal-tag-en-leads-digitales fecha: 2026-05-30 categoria: custom_field, cascada_n8n location_ids: ["GbKkBpCmKu2QmloKFHy3 (Marca)", "HvDw9Eg3rjrwkbQJXqfi (Marina Nacional)", "R34lUVVpltnB8Z1RqnEB (Satélite)", "KEZ7dAhgwzK4uZfMvZuj (Puebla)", "nRSeOhlhQ3vyirTKYhPi (Villas del Sol)", "nF1uEaYB3mCK5em9bPn2 (Eugenia)", "rQYjjwsGnjEGagskOxix (Tulyehualco)", "XkduzafvwsrWcEFg6Qlj (Cd. Carmen)"] run_ids: ["pilot-sucursal-tag-20260530013645", "batch-sucursal-tag-20260530013808", "fix-miza-canal-fuente-20260530015547"] snapshots: [] status: resuelto memorias: ["[[createdby_only_in_individual_get]]", "[[super_script_fix_branch_user_origin]]", "[[n8n_realtime_replication]]", "[[ghl_tags_api]]", "[[custom_fields_picklist_alignment]]", "[[feedback_dry_run_protocol]]"] playbooks: [] --- ## TRIGGERS - `tag sucursal y facebook-ads juntos`, `conflicto facebook-ads sucursal`, `falsa atribución digital` - `createdBy.source INTEGRATION channel OAUTH`, `origen real facebook ads vs creado por sucursal` - `quitar tag sucursal lead digital`, `audit_origin_for_contact_list.py`, `fix_remove_sucursal_tag_digital.py` - `n8n no re-añade tag sucursal`, `tag se queda quitado tras 60s` - `Miza 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](../../db.py#L300)). 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 1. Los 87 son leads de **origen digital** (FB Ads/formulario → integración). El tag `facebook-ads` y `LEAD DIGITAL` son **correctos**. La hipótesis de "fake digital" quedó **refutada**. 2. La etiqueta que sobra y genera el conflicto es **`sucursal`** (en la taxonomía MP `sucursal` y `facebook-ads` son tags de ORIGEN mutuamente excluyentes — ver `fix_branch_user_origin.py`). 3. 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]]. 1. **Quitar tag `sucursal`** de 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. 2. **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 `source` nativo ni la caché**: usar `createdBy.source` en vivo del contacto de **sucursal** (no de Marca). - **`facebook-ads` era CORRECTO**: la instrucción inicial era quitar facebook-ads; los datos dijeron quitar `sucursal`. 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 `sucursal`** a contactos INTEGRATION (piloto lo probó). Quitar un tag no crea opps → cero riesgo de duplicados de opp. - `DELETE /contacts/{id}/tags` con body `{"tags":[...]}` (el path `/tags/{name}` da 404) — ya manejado por `remove_contact_tag` ([[ghl_tags_api]]). ## REUTILIZABLE ```bash # 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 # 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 --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 `sucursal` histó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]]