---
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 <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 `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]]
