Integraciones

NexoID: los datos oficiales del Perú, con su procedencia

API multi-tenant que resuelve RUC, tipo de cambio y ubigeo desde las fuentes oficiales peruanas —y la identidad, bajo contrato—, con el origen, la fecha y la licencia de cada dato dentro de la respuesta.

NexoID: los datos oficiales del Perú, con su procedencia

Problema

Registrar un cliente en el Perú empieza por un RUC o un DNI. Y ahí empieza el problema: el dato existe —SUNAT publica su padrón como dato abierto— pero cada sistema que lo necesita acaba resolviéndolo a su manera. Un scraper contra la consulta web que se rompe cuando cambia el HTML. Una API de la que nadie sabe de dónde saca los datos ni de cuándo son. O el caso más común: alguien teclea el nombre de la empresa a mano.

Lo descubrí resolviendo lo mismo tres veces, en tres sistemas distintos. El dolor no era «falta una API de RUC»: era que ninguna respondía de dónde venía el dato ni de qué fecha era, y eso es exactamente lo que pregunta un auditor.

Contexto

Cuando un RUC entra en una ficha de cliente deja de ser una consulta y pasa a ser un registro. Si mañana alguien revisa por qué esa factura salió a nombre de esa empresa, la respuesta no puede ser «lo trajo una API». Tiene que ser «salió del padrón de SUNAT del 27 de septiembre, con esta licencia».

Con el DNI el problema cambia de naturaleza: ya no es un registro público, es la identidad de una persona. Quien la consulta por cuenta de otro es encargado de su tratamiento, con todo lo que eso exige. Tratar los dos datos igual es lo cómodo. NexoID nació para no hacerlo.

Arquitectura

API-first y headless. No hay panel para el cliente final: lo que se entrega es un contrato OpenAPI 3.1, un token y una respuesta con meta. Los tokens de prueba (nxid_test_) no consumen cuota.

PiezaQué hace
Laravel 13 + Octane sobre FrankenPHPEl runtime: el framework vive en memoria entre peticiones
PostgreSQL 16El padrón completo, los tenants, la auditoría y la facturación
PgBouncerEn modo transaction para el tráfico de la API. La ingesta y las migraciones van por conexión directa, porque el ETL sostiene un advisory lock de sesión que PgBouncer perdería al repartir transacciones
Redis ×2Contadores, rate limit y caché, en dos instancias con políticas de persistencia distintas (ver Decisiones clave)
Ingesta nocturnaEl padrón se descarga cada madrugada; cada ficha guarda la fecha del archivo del que salió

Cada respuesta lleva su propia trazabilidad:

"meta": {
  "origen": "padron_reducido_sunat",
  "fecha_dato": "2026-09-27",
  "atribucion": "Padrón Reducido de Contribuyentes — SUNAT"
}

No es un campo decorativo: es lo que permite usar el dato en un documento fiscal.

Qué resuelve

Cuatro APIs públicas sobre un solo token, y una quinta bajo contrato:

  • RUC. La ficha del contribuyente —estado, condición, ubigeo y actividad económica— desde el padrón de SUNAT, y la búsqueda por razón social. 18.4 millones de contribuyentes, con el 82.6 % de las fichas resueltas hasta el nombre del distrito.
  • Tipo de cambio. El dólar y el euro oficiales del BCRP —con SUNAT de respaldo—: el de hoy, el de una fecha, el de un rango o el de un mes. No descuenta cuota.
  • Ubigeo. Departamento, provincia y distrito a partir de un código INEI.
  • Vigilancia de RUC. Un webhook firmado avisa cuando un RUC vigilado cambia de estado, de condición o de nombre. El cliente no tiene que preguntar: se le avisa.
  • Identidad (DNI), solo por contrato. Exige el scope dni.read, una finalidad declarada, una base legal y un anexo de tratamiento firmado. No se activa desde la web.

A eso se suman las dos cosas que vuelven integrable una API: lotes de hasta 100 documentos por petición e Idempotency-Key, para que una conexión cortada se repita sin consultar —ni cobrar— dos veces.

Decisiones clave

  • El dato se clasifica por su origen, y la clase manda. Clase A es dato público de fuente oficial: el padrón, el BCRP. Clase B es identidad de terceros que llega de un proveedor externo. Clase C, identidad de una fuente oficial contratada. NexoID es titular de su copia de la Clase A y solo encargado de tratamiento de la B y la C: no las retiene ni las usa para nada más.
  • Las dos instancias de Redis son un requisito de cumplimiento, no una optimización. La persistencia se configura por instancia, así que la del caché de Clase B corre sin disco (appendonly no, save ""). Una identidad de terceros no puede acabar escrita en disco ni por accidente. Consolidarlas ahorraría un contenedor y rompería la garantía.
  • El cumplimiento lo verifica el build. Persistir en PostgreSQL un DNI de Clase B o C lo rechaza una política de persistencia, y un test de arquitectura rompe el build si alguien lo intenta. Una regla legal que solo vive en un documento se olvida en el siguiente refactor.
  • El régimen lo decide el contenido, no la ruta. Un RUC que empieza en 10, 15 o 17 es una persona natural: GET /ruc/10… devuelve lo mismo que GET /dni/… y recibe el mismo trato. Sin el scope de identidad responde 403, la respuesta va private, no-store y la auditoría guarda el documento hasheado.
  • Sin caché de borde delante. Cada consulta llega al servidor y queda registrada antes de responder. Un CDN haría la API más rápida y la auditoría incompleta; gana la auditoría.
  • El error es parte del contrato. Código estable, mensaje en español y una señal de si se arregla esperando. Un 429 cuota_agotada es información; un 500 genérico es una llamada a soporte.

Lo que NexoID no hace es parte del diseño, no una lista de pendientes: no busca personas por nombre, no exporta personas naturales, no vende datos ni arma bases, y no puntúa ni perfila a nadie. En un dominio donde el dato es sensible, el alcance se define también por lo que se deja fuera.

Cómo se construye

Treinta y siete sprints, cada uno cerrado con un ADR que deja escrita la decisión y lo que costó. 1,648 casos de prueba en Pest 4, PHPStan en nivel 8 y CI en GitHub Actions. Va por la versión 1.22.

No es disciplina por estética. En un servicio que trata identidad de terceros, la pregunta «¿por qué está hecho así?» la puede hacer un auditor, y la respuesta tiene que estar escrita antes de que llegue.

Qué queda por delante

  • La revisión legal. La política de privacidad, los términos, el anexo de tratamiento y el documento de seguridad ya están redactados y publicados. Falta la revisión de un abogado, que es lo que destraba el alta pública autoservicio: hoy el acceso se pide por WhatsApp.
  • Los SDKs de PHP y JavaScript y el servidor MCP se entregan a pedido mientras se publican. El contrato OpenAPI ya es el que manda; los clientes tipados vienen detrás.
  • Lo que viene: verificar que un nombre corresponde a un documento sin revelar el dato, y validar comprobantes electrónicos uno a uno o en lote.

Aprendizajes

El producto nació de la peor forma posible de descubrir una necesidad: resolviéndola tres veces. Lo que no vi hasta la tercera fue que el valor no estaba en consultar el RUC —eso lo hace cualquiera— sino en poder demostrar de dónde salió.

La segunda lección fue sobre el DNI. Tratar un RUC 10 como «otro RUC» parece razonable hasta que te das cuenta de que su nombre es el nombre de alguien. Decidir el régimen por el contenido del dato, y no por el endpoint que lo pidió, es lo que cierra la puerta trasera: pedir por RUC lo que no se puede pedir por DNI.

Resultado

17 centros podológicos y una clínica dental consultan con NexoID en producción desde septiembre de 2026, validando los datos de sus pacientes y clientes contra el padrón oficial. Los dos sistemas son míos —Podoplus y Diseña Estudio Dental— y fueron los que originaron el producto: el primer cliente de NexoID fue el problema que lo creó.

Proyectos relacionados

IntegracionesEn beta de SUNAT

NexoCPE: facturación electrónica SUNAT como servicio

Servicio multi-tenant que emite comprobantes electrónicos ante SUNAT por API —facturas, boletas, notas y guías—, con cada emisor firmando con su propio certificado y el punto de venta sin esperar nunca a SUNAT.

Núcleo emisor completo y aceptado por SUNAT en beta; piloto en curso con Podoplus

PHP 8.3Laravel 12PostgreSQL 16Redis 7Horizon
Ver caso
AutomatizaciónEn desarrollo

NexoConecta: eventos de negocio convertidos en conversaciones de WhatsApp

Capa multi-tenant que recibe «ocurrió esto» de un sistema cliente y decide qué mensaje de WhatsApp corresponde, cuándo enviarlo y cuánto cuesta. El sistema de origen nunca habla con Meta.

Dominio y panel de operación construidos sobre un simulador de Meta; el onboarding contra Meta real es lo que falta

PHP 8.4Laravel 13Octane + FrankenPHPPostgreSQL 16Redis 7
Ver caso