Saltar al contenido

lección 3

Día 2 — GDPR: diseñar proceso de supresión

Sara te explica el artículo 17. Inventarías PII en 6 tablas, diseñas anonimización irreversible con SHA-256 + salt efímero y construyes el GDPRAnonymizer.

45 min

### Miércoles, 9:00 AM — La reunión con Sara

Sara Vidal te espera en la sala Ada Lovelace con un café cortado y una carpeta azul con el logo de la AEPD. Es puntual al segundo — llegas a las 9:01 y ya tiene el portátil abierto con un cronograma proyectado en la pantalla. "Gracias por venir puntual. Voy directa al grano porque el reloj corre y cada día que pasa sin resolver esto es un día más cerca de tener que notificar incumplimiento."

1Sara Vidal (sala Ada Lovelace, 9:02 AM):
2"Artículo 17 del RGPD. Derecho de supresión. El cliente
3acc_00028473, Jenaro Portero Llorente, solicitó el borrado
4de todos sus datos personales el 7 de septiembre de 2024.
5
6El plazo legal es de 30 días naturales. Hoy es 18 de septiembre.
7Llevamos 11 días consumidos. Me quedan 19 días naturales,
8que son 13 laborables.
9
10Si no tenemos el proceso implementado, ejecutado y AUDITADO
11para el 7 de octubre, tengo la obligación legal de notificar
12incumplimiento a la AEPD.
13
14El artículo 83.5 fija el tope en 20 millones de euros o el 4%
15de la facturación anual global, lo que sea MAYOR. El 4% de
16NeoBank son 3,8 millones, así que en nuestro caso el techo
17son los 20 millones. Esa regla existe para que a las empresas
18pequeñas no les salga barato.
19
20Nadie espera la multa máxima por una petición. Pero el
21expediente sí llega, y con él la inspección de todo lo demás.
22
23No es negociable. No es aplazable. No es 'ya lo hacemos la
24semana que viene'. Es ESTO o es una multa."

Sara explica el deadline regulatorio — sin margen de error

Sara te pasa un documento de 3 páginas con los requisitos legales exactos. Los lees con atención porque cada palabra tiene implicaciones técnicas:

  • Anonimización IRREVERSIBLE — no pseudonimización. No debe ser posible re-identificar al cliente bajo ninguna circunstancia, ni con acceso a la base de datos completa.
  • Aplica a TODOS los sistemas: base operacional (PostgreSQL), data lake (S3 raw/silver/gold), warehouse (Redshift), backups, logs y cualquier sistema que contenga PII.
  • Los datos transaccionales pueden CONSERVARSE si se elimina toda PII — NeoBank necesita el histórico para reporting regulatorio al BdE, pero sin identificar al individuo.
  • Debe quedar un registro de auditoría INMUTABLE que demuestre qué se borró, cuándo, por quién y con qué método.
  • El proceso debe ser REPETIBLE y AUTOMATIZADO — este no será el último cliente que lo pida. Llevamos 14 peticiones en dos meses y medio, unas 6 al mes, y la cifra sube cada trimestre.

### Inventario de PII — dónde vive Jenaro

Vuelves a tu escritorio y empiezas el inventario. Es el paso más importante y el que más tiempo consume. Consultas el catálogo de datos (un Confluence desactualizado pero mejor que nada), revisas los schemas de Redshift con information_schema, y exploras los paths de S3 con aws s3 ls. Después de 2 horas de investigación, tienes el mapa completo:

1-- INVENTARIO DE PII PARA SOLICITUD gdpr_001
2-- Cliente: acc_00028473 (Jenaro Portero Llorente)
3-- Fecha inventario: 2024-09-18
4-- Realizado por: Data Engineer
5
6-- ═══════════════════════════════════════════════════════════════
7-- TABLA 1: clientes (tabla maestra de clientes)
8-- ═══════════════════════════════════════════════════════════════
9-- PII encontrada: nombre, email, teléfono, direccion, fecha_nacimiento, dni_hash
10-- Registros afectados: 1
11-- Acción: ANONIMIZAR (mantener registro con campos hasheados)
12SELECT account_id, nombre, email, telefono, direccion, fecha_nacimiento
13FROM clientes WHERE account_id = 'acc_00028473';
14
15-- ═══════════════════════════════════════════════════════════════
16-- TABLA 2: transactions (histórico de movimientos)
17-- ═══════════════════════════════════════════════════════════════
18-- PII encontrada: account_id como FK (permite re-identificación)
19-- Registros afectados: 4.122 transacciones en 14 meses
20-- Acción: ANONIMIZAR account_id (mantener amounts para reporting BdE)
21SELECT count(*) as total_txns,
22 min(timestamp) as primera, max(timestamp) as última,
23 sum(amount) as volumen_total
24FROM transactions WHERE account_id = 'acc_00028473';
25-- Resultado: 4122 txns, 2023-01-05 a 2024-02-20, 187.432,56 EUR
26
27-- ═══════════════════════════════════════════════════════════════
28-- TABLA 3: kyc_documents (Know Your Customer)
29-- ═══════════════════════════════════════════════════════════════
30-- PII encontrada: scan_dni (imagen), selfie_url, direccion_verificada
31-- Registros afectados: 3 (alta original + 2 actualizaciones)
32-- Acción: BORRAR FÍSICAMENTE (sin valor analítico, alto riesgo)
33SELECT document_type, created_at, verified_at
34FROM kyc_documents WHERE account_id = 'acc_00028473';
35
36-- ═══════════════════════════════════════════════════════════════
37-- TABLA 4: communications (emails y SMS enviados)
38-- ═══════════════════════════════════════════════════════════════
39-- PII encontrada: email_destino, telefono_destino, contenido del mensaje
40-- Registros afectados: 847 comunicaciones
41-- Acción: NULLIFICAR contenido, ANONIMIZAR destinatario
42SELECT comm_type, count(*) as total
43FROM communications WHERE account_id = 'acc_00028473'
44GROUP BY comm_type;
45-- email: 523, sms: 289, push: 35
46
47-- ═══════════════════════════════════════════════════════════════
48-- TABLA 5: support_tickets (atención al cliente)
49-- ═══════════════════════════════════════════════════════════════
50-- PII encontrada: nombre en texto libre, email, contenido conversaciones
51-- Registros afectados: 12 tickets (4 cerrados, 8 resueltos)
52-- Acción: NULLIFICAR contenido de texto libre, ANONIMIZAR contacto
53SELECT ticket_id, status, created_at, subject
54FROM support_tickets WHERE account_id = 'acc_00028473';
55
56-- ═══════════════════════════════════════════════════════════════
57-- TABLA 6: fraud_alerts (alertas del modelo ML)
58-- ═══════════════════════════════════════════════════════════════
59-- PII encontrada: account_id, transaction_details (puede contener merchant names)
60-- Registros afectados: 0 (esta cuenta no tiene alertas en el fichero)
61-- Acción: Ninguna necesaria
62SELECT alert_id, alert_type, false_positive, timestamp
63FROM fraud_alerts WHERE account_id = 'acc_00028473';
64-- Resultado: 0 filas
65
66-- ═══════════════════════════════════════════════════════════════
67-- S3 PATHS AFECTADOS
68-- ═══════════════════════════════════════════════════════════════
69-- s3://neobank-lake/raw/clients/2023/01/acc_00028473.json
70-- s3://neobank-lake/silver/transactions/account_id=acc_00028473/ (14 particiones mensuales)
71-- s3://neobank-lake/gold/client_360/acc_00028473.parquet
72
73-- ═══════════════════════════════════════════════════════════════
74-- RESUMEN
75-- ═══════════════════════════════════════════════════════════════
76-- Total tablas afectadas: 5 (fraud_alerts no tiene registros de este cliente)
77-- Total registros: 4.985
78-- Total paths S3: 16 archivos en 3 prefijos
79-- Riesgo: ALTO (contiene KYC con documento de identidad escaneado)
80-- Prioridad: P1 (deadline regulatorio)

Inventario completo de PII — el mapa de lo que hay que anonimizar

El inventario de PII es el paso MÁS IMPORTANTE y el que más tiempo consume. Si te dejas un sistema sin anonimizar, no solo incumples la ley — puedes generar una brecha de datos. En empresas grandes existe "data lineage" automatizado (herramientas como Collibra o Alation). En NeoBank todavía no — por eso tardas 2 horas revisando manualmente. Esta experiencia es exactamente por la que Alberto quiere que propongas un catálogo la semana que viene.

### Estrategia de anonimización — SHA-256 con salt efímero

Con el inventario completo, diseñas la estrategia. La clave es distinguir entre 3 acciones diferentes según el tipo de dato:

  • BORRAR físicamente: datos que no tienen valor analítico sin la persona (KYC, selfies, DNI escaneado). Se eliminan de la base y de S3.
  • ANONIMIZAR con hash: datos que queremos conservar para analytics pero sin identificar a la persona (transacciones, alertas). El account_id se reemplaza por un hash irreversible.
  • NULLIFICAR: campos de texto libre donde la PII está embebida en texto natural (tickets de soporte, contenido de emails). Se reemplazan por NULL.

La decisión técnica crucial: usar SHA-256 con un salt aleatorio que se DESTRUYE después de la ejecución. ¿Por qué? Sin salt, alguien con acceso a la base podría hashear todos los account_ids posibles (son secuenciales: acc_00000001 a acc_02100000) y hacer una tabla rainbow para re-identificar. Con salt efímero destruido, es matemáticamente imposible revertir el hash — ni siquiera con fuerza bruta.

### Hashear el identificador no impide reidentificar a la persona

Hay una pregunta que Sara te va a hacer y conviene tenerla pensada: si borras el nombre pero dejas las 4.122 transacciones, ¿has anonimizado de verdad? La respuesta honesta es: no del todo. Un historial de transacciones es una huella dactilar. Si alguien sabe que Jenaro compró en una gasolinera de Las Palmas el martes, cenó en un restaurante concreto el jueves y pagó el gimnasio el día 1, con tres o cuatro datos así puede señalar su fila entre dos millones. Está estudiado y publicado: con cuatro puntos de tiempo y lugar se identifica a la gran mayoría de las personas en un fichero de pagos con tarjeta, aunque no haya ni nombres ni números de cuenta.

El hash del account_id protege contra el ataque fácil — alguien con acceso a la base que busca «Jenaro» — y eso ya es mucho. Contra el ataque informado, no. Qué se hace en la práctica:

  • Agregar antes de conservar. El BdE necesita volúmenes por producto y por mes, no el detalle de cada compra. Si el reporte regulatorio se construye sobre datos agregados, el detalle se borra.
  • Recortar los campos que más identifican. El importe exacto y el minuto exacto son los que más distinguen. Redondear el importe y truncar la hora al día reduce la capacidad de señalar a una persona, y casi nunca estropea el reporte.
  • Separar quién puede ver qué. El histórico anonimizado vive en un esquema al que solo accede el equipo de reporting, con registro de accesos.
  • Escribirlo. Si conservas datos tras una petición de supresión, el motivo legal — aquí, la obligación de reporte al supervisor — va documentado en el informe de cumplimiento. El artículo 17.3 permite conservar por obligación legal, pero hay que decir cuál.

«Anonimizado» no es una propiedad del campo, es una propiedad del conjunto. Puedes tener una tabla sin un solo nombre y seguir teniendo datos personales.

### Los backups y los logs: la parte que nadie quiere mirar

Los backups y los logs son la parte que hunde los procesos reales. Un backup de hace tres meses tiene los datos de Jenaro dentro y no se puede editar: si lo tocas, deja de ser un backup válido.

  • Los backups no se editan: se caducan. Se documenta la política de retención (por ejemplo, 90 días) y se registra que a partir de esa fecha los datos habrán desaparecido por rotación. Mientras tanto, quedan bloqueados: nadie restaura un backup sin pasar por Sara.
  • Se guarda una lista de supresiones pendientes. Si algún día hay que restaurar un backup anterior a la petición, lo primero que se ejecuta después de restaurar es la anonimización de todos los que la pidieron. Sin esa lista, restaurar es resucitar los datos.
  • Los logs se tratan como cualquier otra tabla. Si en un log hay un account_id, es PII. Lo normal es fijar una retención corta (30-90 días) y documentarla, en vez de intentar reescribirlos.

Y una cosa más: las cadenas que busques en la verificación las sacas del registro, copiadas y pegadas. No de tu cabeza ni del ticket. Sara ha visto auditorías firmadas buscando un teléfono que el cliente había cambiado hace dos años. Una verificación con datos equivocados da cero filas y parece que todo está bien — es la forma más fácil de certificar un incumplimiento sin saberlo.

1import hashlib
2import secrets
3import json
4from datetime import datetime, timezone
5from typing import Dict, List, Optional
6from dataclasses import dataclass, field
7
8@dataclass
9class AuditEntry:
10 """Entrada inmutable del log de auditoría."""
11 timestamp: str
12 table: str
13 action: str # "anonymized" | "deleted" | "nullified"
14 field: Optional[str] = None
15 records_affected: int = 1
16
17class GDPRAnonymizer:
18 """
19 Anonimizador de datos personales según RGPD Artículo 17.
20
21 Diseño:
22 - Salt efímero: se genera al inicio y se destruye al final
23 - Hash irreversible: SHA-256 truncado a 16 chars hexadecimales
24 - Audit trail: cada acción queda registrada con timestamp
25 - Compliance report: documento generado para Legal/DPO
26
27 Uso:
28 anon = GDPRAnonymizer("acc_00028473", "gdpr_001")
29 anon.anonymize_record("clients", record, ["nombre", "email"])
30 anon.delete_records("kyc_documents", 3)
31 anon.destroy_salt()
32 report = anon.generate_compliance_report()
33 """
34
35 def __init__(self, account_id: str, request_id: str):
36 self.account_id = account_id
37 self.request_id = request_id
38 self.salt = secrets.token_hex(32) # 256 bits de entropía
39 self.audit_log: List[AuditEntry] = []
40 self.started_at = datetime.now(timezone.utc)
41 self._salt_destroyed = False
42
43 # Registrar inicio en audit log
44 self.audit_log.append(AuditEntry(
45 timestamp=self.started_at.isoformat(),
46 table="system",
47 action="process_started",
48 field=f"account_id={account_id}",
49 ))
50
51 def hash_value(self, value: str) -> str:
52 """
53 Hash irreversible con salt efímero.
54 SHA-256(salt + ":" + value) truncado a 16 chars hex.
55 """
56 if self._salt_destroyed:
57 raise RuntimeError("Salt destruido -- no se pueden generar más hashes")
58 combined = f"{self.salt}:{value}".encode("utf-8")
59 full_hash = hashlib.sha256(combined).hexdigest()
60 return full_hash[:16]
61
62 def anonymize_record(self, table: str, record: Dict,
63 pii_fields: List[str]) -> Dict:
64 """
65 Anonimiza los campos PII de un registro.
66 Los campos no-PII permanecen intactos.
67 """
68 anonymized = record.copy()
69 for field_name in pii_fields:
70 if field_name in anonymized and anonymized[field_name] is not None:
71 original_value = str(anonymized[field_name])
72 anonymized[field_name] = f"ANON_{self.hash_value(original_value)}"
73 self.audit_log.append(AuditEntry(
74 timestamp=datetime.now(timezone.utc).isoformat(),
75 table=table,
76 action="anonymized",
77 field=field_name,
78 ))
79 return anonymized
80
81 def nullify_fields(self, table: str, fields: List[str], count: int) -> None:
82 """Registra la nullificación de campos de texto libre."""
83 for field_name in fields:
84 self.audit_log.append(AuditEntry(
85 timestamp=datetime.now(timezone.utc).isoformat(),
86 table=table,
87 action="nullified",
88 field=field_name,
89 records_affected=count,
90 ))
91
92 def delete_records(self, table: str, count: int) -> None:
93 """Registra el borrado físico de registros."""
94 self.audit_log.append(AuditEntry(
95 timestamp=datetime.now(timezone.utc).isoformat(),
96 table=table,
97 action="deleted",
98 records_affected=count,
99 ))
100
101 def delete_s3_paths(self, paths: List[str]) -> None:
102 """Registra la eliminación de archivos en S3."""
103 for path in paths:
104 self.audit_log.append(AuditEntry(
105 timestamp=datetime.now(timezone.utc).isoformat(),
106 table="s3",
107 action="deleted",
108 field=path,
109 ))
110
111 def destroy_salt(self) -> None:
112 """
113 Destruye el salt de forma irreversible.
114 Después de esto, es matemáticamente imposible revertir los hashes.
115 """
116 self.salt = None
117 self._salt_destroyed = True
118 self.audit_log.append(AuditEntry(
119 timestamp=datetime.now(timezone.utc).isoformat(),
120 table="system",
121 action="salt_destroyed",
122 ))
123
124 def generate_compliance_report(self) -> Dict:
125 """Genera reporte para Sara/Legal con resumen del proceso."""
126 actions_by_type = {}
127 for entry in self.audit_log:
128 actions_by_type[entry.action] = actions_by_type.get(entry.action, 0) + 1
129
130 return {
131 "request_id": self.request_id,
132 "account_id": self.account_id,
133 "started_at": self.started_at.isoformat(),
134 "completed_at": datetime.now(timezone.utc).isoformat(),
135 "salt_destroyed": self._salt_destroyed,
136 "total_audit_entries": len(self.audit_log),
137 "actions_summary": actions_by_type,
138 "compliant": self._salt_destroyed, # Solo es compliant si el salt fue destruido
139 "audit_trail": [
140 {"ts": e.timestamp, "table": e.table, "action": e.action, "field": e.field}
141 for e in self.audit_log
142 ],
143 }
144
145# === DEMOSTRACIÓN ===
146anonymizer = GDPRAnonymizer("acc_00028473", "gdpr_001")
147
148# Anonimizar registro de cliente
149cliente_original = {
150 "account_id": "acc_00028473",
151 "nombre": "Jenaro Portero Llorente",
152 "email": "jenaro.llorente28473@hotmail.com",
153 "telefono": "+34617004390",
154 "dirección": "Plaza Pablo Sevilla 179, Las Palmas",
155 "fecha_nacimiento": "1985-07-14",
156 "fecha_alta": "2022-06-15",
157 "saldo_actual": 12847.33,
158}
159
160cliente_anonimizado = anonymizer.anonymize_record(
161 "clients", cliente_original,
162 pii_fields=["nombre", "email", "telefono", "dirección", "fecha_nacimiento"]
163)
164
165print("=== ANTES vs DESPUÉS ===")
166print(f"Nombre: {cliente_original['nombre']:40s} -> {cliente_anonimizado['nombre']}")
167print(f"Email: {cliente_original['email']:40s} -> {cliente_anonimizado['email']}")
168print(f"Saldo: {cliente_original['saldo_actual']:40} -> {cliente_anonimizado['saldo_actual']} (NO es PII)")
169
170# Registrar otras acciones
171anonymizer.delete_records("kyc_documents", 3)
172anonymizer.nullify_fields("support_tickets", ["contenido", "nombre_agente"], 12)
173anonymizer.delete_s3_paths([
174 "s3://neobank-lake/raw/clients/2022/06/acc_00028473.json",
175 "s3://neobank-lake/gold/client_360/acc_00028473.parquet",
176])
177
178# Destruir salt -- punto de no retorno
179anonymizer.destroy_salt()
180
181# Generar reporte
182report = anonymizer.generate_compliance_report()
183print(f"\n=== REPORTE DE COMPLIANCE ===")
184print(f"Request: {report['request_id']}")
185print(f"Salt destruido: {report['salt_destroyed']}")
186print(f"Acciones totales: {report['total_audit_entries']}")
187print(f"Compliant: {report['compliant']}")

GDPRAnonymizer completo: hash irreversible + audit trail + compliance report

### El runbook de supresión — proceso repetible

Sara insiste en que el proceso sea un runbook documentado — un procedimiento pasó a pasó que cualquier persona del equipo pueda ejecutar cuando llegue la próxima solicitud (y llegarán: con 2.1M clientes, estadísticamente 15/mes).

  1. 01.Recibir solicitud GDPR de Legal con el account_id y request_id asignado
  2. 02.Verificar identidad del solicitante (lo hace Legal, no datos)
  3. 03.Ejecutar query de inventario para localizar TODOS los registros del cliente
  4. 04.Generar salt efímero y crear instancia de GDPRAnonymizer
  5. 05.Anonimizar tabla clientes (nombre, email, teléfono, dirección, DNI)
  6. 06.Anonimizar FK en transactions (mantener amounts y timestamps)
  7. 07.BORRAR registros de kyc_documents (borrado físico, sin valor analítico)
  8. 08.NULLIFICAR contenido de communications y support_tickets
  9. 09.BORRAR paths de S3 específicos del cliente en las TRES zonas: raw, silver (las 14 particiones mensuales del histórico) y gold/client_360. Olvidar silver es el fallo más común: es donde vive el detalle
  10. 10.BLOQUEAR los backups que contienen al cliente y documentar la política de retención (90 días). Un backup no se edita: se caduca. Nadie restaura sin pasar por Sara
  11. 11.AÑADIR el account_id a la lista de supresiones pendientes, para reaplicar la anonimización si algún día hay que restaurar un backup anterior a la petición
  12. 12.REVISAR los logs: si contienen el account_id, es PII. Documentar la retención corta (30-90 días) en vez de intentar reescribirlos
  13. 13.Ejecutar queries de VERIFICACIÓN post-anonimización (con las cadenas copiadas del registro, no de memoria)
  14. 14.Destruir salt (punto de no retorno)
  15. 15.Generar compliance report y enviarlo a Sara con copia a Legal

### Verificación post-anonimización

Sara es inflexible en un punto: "Si no puedes DEMOSTRAR que no queda PII, no puedo certificar cumplimiento". Las queries de verificación son tan importantes como la anonimización en sí:

1-- VERIFICACIÓN POST-ANONIMIZACIÓN
2-- Ejecutar DESPUÉS del proceso y ANTES de destruir el salt
3-- Todas las queries deben devolver 0 registros con PII
4
5-- 1. El account_id original NO debe existir en ninguna tabla
6SELECT 'clients' as tabla, count(*) as matches
7 FROM clients WHERE account_id = 'acc_00028473'
8UNION ALL
9SELECT 'transactions', count(*)
10 FROM transactions WHERE account_id = 'acc_00028473'
11UNION ALL
12SELECT 'communications', count(*)
13 FROM communications WHERE account_id = 'acc_00028473'
14UNION ALL
15SELECT 'support_tickets', count(*)
16 FROM support_tickets WHERE account_id = 'acc_00028473'
17UNION ALL
18SELECT 'fraud_alerts', count(*)
19 FROM fraud_alerts WHERE account_id = 'acc_00028473';
20-- ESPERADO: todos 0
21
22-- 2. Buscar PII textual (nombre, email, teléfono)
23SELECT 'clients' as tabla, count(*) as matches
24 FROM clientes WHERE nombre LIKE '%Portero Llorente%'
25 OR email LIKE '%jenaro.llorente28473%'
26 OR telefono LIKE '%617004390%'
27UNION ALL
28SELECT 'support_tickets', count(*)
29 FROM support_tickets WHERE contenido LIKE '%Portero%'
30 OR contenido LIKE '%jenaro.llorente%';
31-- ESPERADO: todos 0
32
33-- 3. KYC debe estar BORRADO (no anonimizado)
34SELECT count(*) as kyc_restante FROM kyc_documents
35WHERE account_id = 'acc_00028473'
36 OR account_id LIKE 'ANON_%2847391%';
37-- ESPERADO: 0 (borrado físico completo)
38
39-- 4. Verificar que datos ANONIMIZADOS siguen disponibles para analytics
40SELECT count(*) as txns_anonimizadas,
41 sum(amount) as volumen_conservado,
42 min(timestamp) as primera_txn,
43 max(timestamp) as ultima_txn
44FROM transactions WHERE account_id LIKE 'ANON_%';
45-- ESPERADO: 4122 transacciones con importes intactos
46
47-- 5. Generar resumen para el compliance report
48SELECT tabla, registros_originales, accion_realizada, verificado
49FROM (VALUES
50 ('clients', 1, 'ANONIMIZADO', TRUE),
51 ('transactions', 4122, 'FK ANONIMIZADA', TRUE),
52 ('kyc_documents', 3, 'BORRADO FÍSICO', TRUE),
53 ('communications', 847, 'NULLIFICADO', TRUE),
54 ('support_tickets', 12, 'NULLIFICADO', TRUE),
55 ('fraud_alerts', 0, 'SIN REGISTROS', TRUE)
56) AS t(tabla, registros_originales, accion_realizada, verificado);

Queries de verificación — demostrar que no queda PII

NUNCA ejecutes una anonimización GDPR sin verificación posterior. Si dejas un solo registro sin anonimizar y hay una auditoría de la AEPD, la multa puede ser del 4% de la facturación anual. La verificación no es opcional — es parte integral del proceso y debe quedar documentada en el compliance report.

En la práctica, las solicitudes GDPR vienen en oleadas. Cuando un banco grande publica que respeta el derecho al olvido, otros clientes lo piden también por principio. Automatizar AHORA te ahorra cientos de horas en los próximos meses. Sara lo sabe — por eso insiste en el runbook.

Regístrate para guardar tu progreso.