En México, todo producto financiero — desde una cuenta de nómina hasta un crédito personal — requiere verificar la identidad del cliente. La regulación de la CNBV, CONDUSEF y las disposiciones antilavado exigen validar que la persona es quien dice ser. Esta guía explica cómo implementar un flujo de KYC completo con API, sin capturas manuales.
¿Qué documentos se requieren para KYC en México?
| Tipo de cliente | Documentos requeridos | Regulación aplicable |
|---|---|---|
| Persona física (bajo riesgo) | INE vigente | CNBV Disposiciones Antilavado Art. 12 |
| Persona física (medio riesgo) | INE + CURP + comprobante domicilio | CNBV Disposiciones Antilavado Art. 13 |
| Persona moral | INE del rep. legal + CSF + acta constitutiva | CNBV Disposiciones Antilavado Art. 14 |
| Plataforma gig (conductor, repartidor) | INE + comprobante domicilio | STPS / IMSS verificación opcional |
Arquitectura del flujo de KYC con Ocilar
# Flujo típico de onboarding KYC en México # 1. Usuario sube foto de INE (frente y reverso) # 2. API extrae datos automáticamente # 3. Se cruzan datos con CURP (opcional) # 4. Se verifica vigencia del INE # 5. Se registra el resultado con timestamp para auditoría
Paso 1 — Extraer datos de la INE
Python
from ocilar import OcilarClient
from datetime import datetime
client = OcilarClient(api_key="sk-your_key")
def extraer_ine(frente_path: str, reverso_path: str) -> dict:
"""Extrae todos los datos de una INE."""
resultado = client.extract_ine(
front=frente_path,
back=reverso_path
)
# Validar vigencia
anio_actual = datetime.now().year
ine_vigente = int(resultado.vigencia) >= anio_actual
return {
"nombre": resultado.nombre,
"curp": resultado.curp,
"clave_elector": resultado.clave_elector,
"fecha_nacimiento": resultado.fecha_nacimiento,
"sexo": resultado.sexo,
"estado": resultado.estado,
"municipio": resultado.municipio,
"domicilio": resultado.domicilio,
"seccion": resultado.seccion,
"vigencia": resultado.vigencia,
"vigente": ine_vigente,
"anio_registro": resultado.anio_registro,
}
# Uso
datos_ine = extraer_ine("ine_frente.jpg", "ine_reverso.jpg")
print(datos_ine) Node.js
import { OcilarClient } from '@ocilar/sdk'
import { createReadStream } from 'fs'
const client = new OcilarClient({ apiKey: 'sk-your_key' })
async function extraerIne(frentePath, reversPath) {
const result = await client.extractIne({
front: createReadStream(frentePath),
back: createReadStream(reversPath)
})
const anioActual = new Date().getFullYear()
const vigente = parseInt(result.vigencia) >= anioActual
return {
nombre: result.nombre,
curp: result.curp,
claveElector: result.claveElector,
fechaNacimiento: result.fechaNacimiento,
sexo: result.sexo,
estado: result.estado,
domicilio: result.domicilio,
vigencia: result.vigencia,
vigente
}
} Paso 2 — Validar la CURP
La CURP extraída de la INE puede validarse cruzándola con el documento de CURP emitido por RENAPO:
from ocilar import OcilarClient
client = OcilarClient(api_key="sk-your_key")
def validar_curp_con_documento(curp_extraida: str, curp_pdf_path: str) -> bool:
"""
Cruza la CURP de la INE con el documento de CURP oficial de RENAPO.
Retorna True si coinciden.
"""
resultado_curp = client.extract_curp(file_path=curp_pdf_path)
coinciden = curp_extraida.upper() == resultado_curp.curp.upper()
if not coinciden:
print(f"Discrepancia: INE dice {curp_extraida}, CURP doc dice {resultado_curp.curp}")
return coinciden Paso 3 — Flujo completo de onboarding
Persona física (nivel bajo de riesgo)
from ocilar import OcilarClient
from datetime import datetime
import uuid
client = OcilarClient(api_key="sk-your_key")
def onboarding_persona_fisica(
ine_frente: str,
ine_reverso: str,
curp_pdf: str = None
) -> dict:
"""
Flujo completo de KYC para persona física.
Retorna datos verificados listos para guardar en tu base de datos.
"""
# 1. Extraer INE
ine = client.extract_ine(front=ine_frente, back=ine_reverso)
# 2. Validar vigencia
if int(ine.vigencia) < datetime.now().year:
return {
"aprobado": False,
"razon_rechazo": f"INE vencida en {ine.vigencia}",
"timestamp": datetime.now().isoformat()
}
# 3. Validar CURP si se proporcionó
curp_validada = True
if curp_pdf:
curp_doc = client.extract_curp(file_path=curp_pdf)
curp_validada = ine.curp.upper() == curp_doc.curp.upper()
if not curp_validada:
return {
"aprobado": False,
"razon_rechazo": "La CURP del documento no coincide con la INE",
"timestamp": datetime.now().isoformat()
}
# 4. Retornar datos verificados
return {
"aprobado": True,
"id_verificacion": str(uuid.uuid4()),
"timestamp": datetime.now().isoformat(),
"datos": {
"nombre_completo": ine.nombre,
"curp": ine.curp,
"clave_elector": ine.clave_elector,
"fecha_nacimiento": ine.fecha_nacimiento,
"sexo": ine.sexo,
"estado_registro": ine.estado,
"municipio_registro": ine.municipio,
"domicilio": ine.domicilio,
"ine_vigencia": ine.vigencia,
}
}
# Uso en tu endpoint de onboarding
resultado = onboarding_persona_fisica(
ine_frente="uploads/ine_frente.jpg",
ine_reverso="uploads/ine_reverso.jpg",
curp_pdf="uploads/curp.pdf" # opcional
)
if resultado["aprobado"]:
print("Cliente verificado:", resultado["datos"]["nombre_completo"])
else:
print("Rechazo:", resultado["razon_rechazo"]) Persona moral (con rep. legal)
def onboarding_persona_moral(
ine_rep_frente: str,
ine_rep_reverso: str,
csf_path: str
) -> dict:
"""KYC para persona moral: extrae INE del representante legal + CSF de la empresa."""
rep_legal = client.extract_ine(front=ine_rep_frente, back=ine_rep_reverso)
# Validar vigencia del representante
if int(rep_legal.vigencia) < datetime.now().year:
return {"aprobado": False, "razon_rechazo": "INE del representante vencida"}
empresa = client.extract_csf(file_path=csf_path)
return {
"aprobado": True,
"id_verificacion": str(uuid.uuid4()),
"timestamp": datetime.now().isoformat(),
"representante_legal": {
"nombre": rep_legal.nombre,
"curp": rep_legal.curp,
"ine_vigencia": rep_legal.vigencia,
},
"empresa": {
"rfc": empresa.rfc,
"razon_social": empresa.nombre,
"regimen_fiscal": empresa.regimen_fiscal,
"domicilio_fiscal": empresa.domicilio_fiscal,
"codigo_postal": empresa.codigo_postal,
"actividades": empresa.actividades,
}
} Integración con FastAPI (ejemplo backend)
from fastapi import FastAPI, UploadFile, File
from ocilar import OcilarClient
import tempfile
import os
app = FastAPI()
client = OcilarClient(api_key="sk-your_key")
@app.post("/kyc/individual")
async def kyc_individual(
ine_frente: UploadFile = File(...),
ine_reverso: UploadFile = File(...),
):
# Guardar archivos temporales
with tempfile.NamedTemporaryFile(delete=False, suffix=".jpg") as f_frente:
f_frente.write(await ine_frente.read())
frente_path = f_frente.name
with tempfile.NamedTemporaryFile(delete=False, suffix=".jpg") as f_reverso:
f_reverso.write(await ine_reverso.read())
reverso_path = f_reverso.name
try:
resultado = client.extract_ine(front=frente_path, back=reverso_path)
return {
"nombre": resultado.nombre,
"curp": resultado.curp,
"vigente": int(resultado.vigencia) >= 2026,
"vigencia": resultado.vigencia,
}
finally:
os.unlink(frente_path)
os.unlink(reverso_path) Casos de uso por industria
Fintech y neobancos
La regulación CNBV exige identificación de clientes para apertura de cuentas. Con Ocilar, el flujo completo — subida de INE, extracción, validación — tarda menos de 3 segundos, contra 2–5 minutos de revisión manual.
Plataformas de crédito (BNPL, préstamos)
Para scoring crediticio necesitas nombre, fecha de nacimiento, domicilio y CURP. Extrae todos estos campos automáticamente del INE en el primer paso del formulario de solicitud.
Plataformas gig economy
Verificar la identidad de conductores, repartidores y contratistas independientes antes de activarlos. Combina extracción de INE con verificación de semanas cotizadas en IMSS para un perfil más completo.
HR y nómina
Onboarding de empleados sin captura manual. Los datos de INE se alimentan directamente a IMSS, INFONAVIT y el sistema de nómina.
Arrendamiento y bienes raíces
Verificación de inquilinos o compradores automáticamente. El domicilio en la INE sirve como referencia de dirección registrada.
Precios
Extraer datos de una INE (frente + reverso) cuesta $0.05–$0.10 por documento. El plan gratuito incluye 100 documentos al mes.
Preguntas frecuentes
¿La API valida la autenticidad del INE contra el RENAPO?
Ocilar extrae los datos del documento físico por OCR. La validación de autenticidad contra el padrón del RENAPO es un servicio separado. Contáctanos si necesitas esa integración.
¿Los datos se almacenan después del procesamiento?
No. Las imágenes se eliminan dentro de los 60 segundos posteriores al procesamiento. Consulta nuestra política de privacidad.
¿Cumple con la Ley Federal de Protección de Datos Personales?
Ocilar procesa los datos como encargado (procesador). El responsable del tratamiento es tu empresa. Consulta con tu área legal para asegurar que tu aviso de privacidad cubre el procesamiento de imágenes de identificación por terceros.
¿Funciona con fotos tomadas con celular?
Sí. Las fotos móviles funcionan bien cuando la INE ocupa la mayor parte del encuadre, está bien iluminada y no tiene reflejos ni sombras sobre el texto.