Uno de nuestros asesores se pondrá en contacto contigo en breve.
Tres llamadas. Del PDF al veredicto, sin revisión manual.
Sube el PDF descargado de CFE o la foto del recibo impreso. Regresan los campos estructurados y el análisis de alteración.
Número de servicio, titular e importe se validan contra el registro oficial. Respuesta inmediata con job_id.
Veredicto, datos oficiales del servicio, comparación campo a campo y código de rechazo si aplica.
Casi cualquier comprobante falso es impecable a la vista. El filtro no es la apariencia del documento: es cuántos datos tienen que coincidir al mismo tiempo contra el registro de CFE, y en qué periodo.
No basta con copiar el número de servicio y el nombre de un recibo real. El cotejo exige además el importe exacto del recibo vigente o del penúltimo. Quien arma un comprobante puede acertar dos datos; los tres, contra el registro oficial y en el periodo correcto, no.
La respuesta no es un “no válido” opaco. codigo_rechazo señala si lo que no cuadró fue el número de servicio, el titular o el importe. Cada causa corresponde a un fraude distinto y te deja ramificar el flujo en vez de mandar todo a revisión manual.
Además de los campos estructurados, recuperamos el comprobante oficial desde CFE en PDF. Tu expediente guarda el documento de la fuente —no el archivo que subió tu cliente, ni una reconstrucción de sus datos—, que es lo que sostiene una revisión posterior.
Como el importe se valida contra el último o el penúltimo recibo, un comprobante viejo no puede coincidir aunque sus datos sean reales. La vigencia deja de ser una promesa del documento: es una consecuencia de haber pasado el cotejo.
Todas las peticiones son JSON sobre HTTPS y se autentican con un bearer token. Estos son los payloads reales del flujo, de principio a fin.
Un multipart/form-data con el archivo. Si el PDF es el nativo del portal de CFE, la extracción se hace sobre la capa de texto y la confianza roza el 100%; si es una fotografía, se aplica OCR con corrección de perspectiva.
El bloque analisis_documento es el que delata al recibo hecho con plantilla: capas de edición, texto superpuesto y qué software generó el archivo.
curl -X POST https://api.verificamex.com/v1/cfe/ocr \
-H "Authorization: Bearer $VMX_API_KEY" \
-F "[email protected]"
# → 200 OK
{
"estado": "procesado",
"confianza": 0.98,
"formato_detectado": "pdf_nativo",
"datos": {
"numero_servicio": "50918273645",
"nombre_titular": "BENITO PABLO JUAREZ GARCIA",
"direccion": "AV. HIDALGO 118, CENTRO, 68000 OAXACA DE JUAREZ, OAX.",
"tarifa": "1C",
"periodo_facturado": "MAY-JUN 2026",
"fecha_limite_pago": "2026-07-14",
"importe": "104.00"
},
"analisis_documento": {
"alterado": false,
"capas_edicion": 0,
"texto_superpuesto": false,
"productor_pdf": "CFE Portal"
}
}
Pasas los tres campos que CFE usa para localizar el servicio y tu webhook_url. La API responde en milisegundos con un job_id: no dejamos tu petición HTTP abierta esperando a un tercero.
El importe no es un capricho: CFE lo valida contra el último o penúltimo recibo, así que si coincide, el comprobante es reciente por construcción.
use Illuminate\Support\Facades\Http;
$respuesta = Http::withToken(config('services.verificamex.key'))
->post('https://api.verificamex.com/v1/cfe/validaciones', [
'numero_servicio' => '50918273645',
'nombre_titular' => 'BENITO PABLO JUAREZ GARCIA',
'importe' => '104',
'webhook_url' => route('webhooks.cfe'),
]);
$jobId = $respuesta->json('job_id');
// "23133c38d62c" — guárdalo para conciliar el webhook
const respuesta = await fetch("https://api.verificamex.com/v1/cfe/validaciones", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.VMX_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
numero_servicio: "50918273645",
nombre_titular: "BENITO PABLO JUAREZ GARCIA",
importe: "104",
webhook_url: "https://tu-app.mx/webhooks/cfe",
}),
});
const { job_id } = await respuesta.json();
// "23133c38d62c" — guárdalo para conciliar el webhook
import os, requests
respuesta = requests.post(
"https://api.verificamex.com/v1/cfe/validaciones",
headers={"Authorization": f"Bearer {os.environ['VMX_API_KEY']}"},
json={
"numero_servicio": "50918273645",
"nombre_titular": "BENITO PABLO JUAREZ GARCIA",
"importe": "104",
"webhook_url": "https://tu-app.mx/webhooks/cfe",
},
)
job_id = respuesta.json()["job_id"]
# "23133c38d62c" — guárdalo para conciliar el webhook
Cuando CFE responde, hacemos POST a tu webhook_url con el resultado completo. verificado es el booleano que puedes cablear directo a tu motor de decisión; el resto es la evidencia para tu expediente.
Cuando el cotejo procede, datos_oficiales trae el registro tal como lo devuelve CFE y comparacion el contraste campo a campo contra lo que declaró tu cliente.
{
"job_id": "23133c38d62c",
"estado": "completado",
"resultado": {
"estado": "VALIDADO",
"verificado": true,
"mensaje": "El servicio fue localizado en CFE y los datos declarados coinciden con el registro oficial.",
"codigo_rechazo": null,
"datos_oficiales": {
"numero_servicio": "50918273645",
"nombre_titular": "BENITO PABLO JUAREZ GARCIA",
"direccion": "AV. HIDALGO 118, CENTRO, 68000 OAXACA DE JUAREZ, OAX.",
"tarifa": "1C",
"estatus_servicio": "ACTIVO",
"periodo_facturado": "MAY-JUN 2026",
"fecha_limite_pago": "2026-07-14",
"total_a_pagar": "104.00"
},
"comparacion": {
"numero_servicio": { "coincide": true, "score": 100 },
"nombre_titular": { "coincide": true, "score": 100 },
"importe": { "coincide": true, "score": 100 }
},
"periodo_vigente": {
"vigente": true,
"periodo": "MAY-JUN 2026",
"antiguedad_dias": 23
},
"alertas": [],
"pdf_filename": "comprobante_50918273645.pdf",
"pdf_base64": "JVBERi0xLjQKJeLjz9MKMyAwIG9iago8PC9UeXBl..."
}
}
El objeto resultado está diseñado para que tu motor de decisión no tenga que interpretar texto libre.
| Campo | Tipo | Para qué sirve |
|---|---|---|
| verificado | boolean | El único campo que necesitas para aprobar o frenar automáticamente. true sólo si el servicio existe, está activo y los tres datos declarados coinciden. |
| estado | string | Veredicto legible: VALIDADO, DATOS_NO_COINCIDEN, SERVICIO_NO_ENCONTRADO o ERROR_FUENTE. |
| codigo_rechazo | string · null | Causa exacta del rechazo, para ramificar tu flujo. null cuando verificado es true. |
| datos_oficiales | object · null | El registro tal como lo devuelve CFE: titular, dirección del suministro, tarifa, estatus, periodo e importe. Es la evidencia que guardas en el expediente. |
| comparacion | object | Contraste campo a campo entre lo declarado y lo oficial, con score de similitud. Útil para tolerar diferencias de acentos o abreviaturas en el nombre. |
| periodo_vigente | object · null | Periodo facturado y antiguedad_dias. Te deja aplicar tu propia política de vigencia (por ejemplo, rechazar comprobantes de más de 90 días). |
| alertas | array | Señales no bloqueantes que ameritan revisión humana aunque el cotejo haya pasado. |
| pdf_base64 | string · null | El comprobante oficial recuperado desde CFE, no el que subió tu cliente. Es el documento que conviene archivar. |
Cada código corresponde a una forma concreta de comprobante falso. Ramifica tu onboarding según cuál llegue.
| codigo_rechazo | Qué significa en la práctica |
|---|---|
| IMPORTE_NO_COINCIDE | El total declarado no corresponde al último ni al penúltimo recibo del servicio. Casi siempre es un comprobante viejo reciclado o un importe editado. |
| SERVICIO_NO_ENCONTRADO | El número de servicio no existe en CFE. Comprobante inventado con plantilla o RPU mal transcrito. |
| TITULAR_NO_COINCIDE | El servicio existe, pero está a nombre de otra persona. Es el comprobante prestado: el domicilio es real, la relación con tu cliente no. |
| SERVICIO_INACTIVO | El suministro está dado de baja. El domicilio existió, pero hoy nadie vive ahí bajo ese contrato. |
| DOCUMENTO_ALTERADO | El análisis del archivo detectó capas de edición o texto superpuesto sobre el original. Recibo real, retocado. |
| OCR_ILEGIBLE | La imagen no permite extraer los campos mínimos. No es fraude: pide a tu cliente una foto mejor y reintenta. |
{
"job_id": "23133c38d62c",
"estado": "completado",
"resultado": {
"estado": "DATOS_NO_COINCIDEN",
"verificado": false,
"mensaje": "CFE rechazó el registro del servicio: el total a pagar no coincide con nuestro registro, favor de validar la información con su último o penúltimo recibo.",
"codigo_rechazo": "IMPORTE_NO_COINCIDE",
"datos_oficiales": null,
"comparacion": {},
"alertas": [
"El importe declarado no corresponde al último ni al penúltimo recibo."
],
"periodo_vigente": null,
"pdf_base64": null
}
}
Un recibo de luz se edita con una plantilla en minutos: se cambia el nombre, la dirección o la fecha y pasa cualquier revisión visual. Si tu proceso lo acepta de vista, tu expediente tiene un documento que no prueba nada. Así se ve el momento en que uno de esos comprobantes se cae.
El veredicto sale del cotejo ante CFE, no de si el documento "se ve bien".
Como CFE coteja el importe contra el último o penúltimo recibo, un comprobante viejo se cae solo.
Confirma que el domicilio declarado está a nombre de quien dice ser, o detecta el comprobante prestado.
Todo lo que necesitas para dejar de aceptar comprobantes de domicilio de vista.
El comprobante de domicilio se pide en casi toda alta. Validarlo cambia lo que ese documento prueba.
En préstamos y financieras, un domicilio falso vuelve incobrable la cartera: la dirección de cobranza nunca existió. En arrendamiento y proptech, el comprobante prestado o editado es la base del perfil falso del inquilino. En cualquier onboarding con expediente KYC, la validación ante CFE convierte un papel decorativo en evidencia real del domicilio.
El comprobante validado rinde más junto a la identidad del titular: valida su INE ante la Lista Nominal, su CURP ante RENAPO y extrae los datos de la credencial con OCR de INE en el mismo flujo. Una sola integración, expediente completo.
El comprobante de domicilio validado, junto con la identidad del titular, en un solo flujo.