Files
MP-Manager/docs/casos/2026-05-30-sucursal-tag-en-leads-digitales.md
T
2026-05-30 14:31:19 -06:00

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
GbKkBpCmKu2QmloKFHy3 (Marca)
HvDw9Eg3rjrwkbQJXqfi (Marina Nacional)
R34lUVVpltnB8Z1RqnEB (Satélite)
KEZ7dAhgwzK4uZfMvZuj (Puebla)
nRSeOhlhQ3vyirTKYhPi (Villas del Sol)
nF1uEaYB3mCK5em9bPn2 (Eugenia)
rQYjjwsGnjEGagskOxix (Tulyehualco)
XkduzafvwsrWcEFg6Qlj (Cd. Carmen)
pilot-sucursal-tag-20260530013645
batch-sucursal-tag-20260530013808
fix-miza-canal-fuente-20260530015547
resuelto
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

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). 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

# 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