{"openapi":"3.1.0","info":{"title":"NexoID","version":"1.0.0","summary":"RUC y DNI peruanos desde fuentes oficiales, con procedencia auditable.","description":"Resuelve RUC y DNI peruanos contra el padrón del RUC de SUNAT, con la procedencia de cada\ndato como campo de la respuesta y no como nota al pie.\n\n## Tres cosas que conviene leer antes de integrar\n\n🔒 **Un RUC que empieza en `10`, `15` o `17` es una persona natural**, y su nombre es un\ndato de identidad: exige `dni.read` y finalidad declarada aunque la ruta sea `/ruc`. Es la\nprimera sorpresa de toda migración.\n\n🔒 **`dni.fallback` es un scope aparte**, el único que cuesta dinero. Sin él, los DNI que\nel padrón local no tiene devuelven 404 sin ningún error — parece «no existe» y es «no\npuedo preguntarlo».\n\n**El caché va en tu sistema, no aquí.** La respuesta de identidad lleva `no-store` a\npropósito: guardarla es tu decisión y tu responsabilidad, y así el dato no se acumula en un\ntercero."},"servers":[{"url":"https://nexoid.victorllerena.com/id/v1"}],"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","description":"El token de la API, con prefijo `nxid_live_` o `nxid_test_`. 🔒 Nunca en el navegador: expondría cuota, identidad y auditoría del tenant."}}},"paths":{"/dni/batch":{"post":{"operationId":"dni.batch","summary":"Hasta 100 DNIs de una vez","description":"🔒 **Nunca dispara fallback**, aunque el token tenga `dni.fallback`: la respuesta del batch se guarda 24 h para la idempotencia, y un dato de Clase B no puede acabar en almacenamiento persistente (§15.3, R1).\n\nScopes: `batch`, `dni.read`.","security":[{"bearer":["batch","dni.read"]}],"parameters":[],"x-scopes":["batch","dni.read"],"x-consume-cuota":true}},"/dni/{dni}":{"get":{"operationId":"dni.consultar","summary":"Identidad de una persona natural por DNI","description":"🔒 Exige `dni.read` y finalidad declarada (R6). Responde siempre `private, no-store`. Si el padrón no lo tiene y el token lleva `dni.fallback`, sale al proveedor externo; sin ese scope devuelve 404 aunque el fallback esté contratado. El 404 lleva `meta.motivo` y `meta.degradado`: «no está» y «no pude preguntar» son cosas distintas.\n\nScopes: `dni.read`.","security":[{"bearer":["dni.read"]}],"parameters":[{"name":"dni","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9]{8}$"}}],"x-scopes":["dni.read"],"x-consume-cuota":true}},"/health":{"get":{"operationId":"health","summary":"Estado del servicio y frescura del padrón","description":"Público: sin token. `dependencias.padron` dice si las vistas SIRVEN filas, que no es lo mismo que si el ETL corrió: el padrón puede quedarse vacío con `sync_run` en `ok`. `estado` pasa a `degraded` si el padrón supera 36 h.","security":[],"parameters":[],"x-scopes":[],"x-consume-cuota":false}},"/me":{"get":{"operationId":"me","summary":"Qué es este token y qué puede hacer","description":"Devuelve tres listas de scopes: los del token, los del plan y los **efectivos** (su intersección, §13.3). Un 403 de scope tiene dos causas posibles y esto dice cuál es la tuya. No consume cuota.","security":[{"bearer":[]}],"parameters":[],"x-scopes":[],"x-consume-cuota":false}},"/openapi.json":{"get":{"operationId":"openapi","summary":"Esta misma especificación","description":"Público y generado desde la tabla de rutas en cada petición, así que describe **este** despliegue y no la última vez que alguien corrió un comando.","security":[],"parameters":[],"x-scopes":[],"x-consume-cuota":false}},"/ruc/batch":{"post":{"operationId":"ruc.batch","summary":"Hasta 100 RUCs de una vez","description":"Con `Idempotency-Key`: repetir la misma clave devuelve la respuesta guardada sin volver a cobrar. Cada documento se audita por separado.\n\nScopes: `batch`, `ruc.read`.","security":[{"bearer":["batch","ruc.read"]}],"parameters":[],"x-scopes":["batch","ruc.read"],"x-consume-cuota":true}},"/ruc/buscar":{"get":{"operationId":"ruc.buscar","summary":"Búsqueda por razón social","description":"🔒 **Solo tipo `20`.** No existe búsqueda por nombre de persona: sería una vía de resolución de identidad sin documento (R4).\n\nScopes: `ruc.search`.","security":[{"bearer":["ruc.search"]}],"parameters":[],"x-scopes":["ruc.search"],"x-consume-cuota":true}},"/ruc/{ruc}":{"get":{"operationId":"ruc.consultar","summary":"Ficha de contribuyente por RUC","description":"🔒 Un RUC que empieza en `10`, `15` o `17` es una PERSONA NATURAL: su `nombre` es un dato de identidad, así que exige `dni.read` —no basta `ruc.read`—, finalidad declarada, y responde `private, no-store` (§3.4). `direccion` llega en el 13.2% de los casos y es un techo; `ubigeo` y sus nombres, en el 82.5%. Los campos del padrón extendido traen su propia fecha en `meta.fecha_dato_extendido`, que puede ser de hace un mes.\n\nScopes: `ruc.read`.","security":[{"bearer":["ruc.read"]}],"parameters":[{"name":"ruc","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9]{11}$"}}],"x-scopes":["ruc.read"],"x-consume-cuota":true}},"/tipo-cambio":{"get":{"operationId":"tc.hoy","summary":"Tipo de cambio de hoy","description":"Compra y venta del BCRP. Si hoy no hay publicación —fin de semana o feriado— devuelve el último día hábil y lo dice en `meta`.\n\nScopes: `tc.read`.","security":[{"bearer":["tc.read"]}],"parameters":[],"x-scopes":["tc.read"],"x-consume-cuota":true}},"/tipo-cambio/rango":{"get":{"operationId":"tc.rango","summary":"Serie histórica de tipo de cambio","description":"Entre dos fechas. El rango está acotado para que no sea una exportación del histórico completo.\n\nScopes: `tc.read`.","security":[{"bearer":["tc.read"]}],"parameters":[],"x-scopes":["tc.read"],"x-consume-cuota":true}},"/tipo-cambio/{fecha}":{"get":{"operationId":"tc.fecha","summary":"Tipo de cambio de una fecha","description":"Formato `YYYY-MM-DD`. Una fecha sin publicación devuelve 404.\n\nScopes: `tc.read`.","security":[{"bearer":["tc.read"]}],"parameters":[{"name":"fecha","in":"path","required":true,"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}}],"x-scopes":["tc.read"],"x-consume-cuota":true}},"/ubigeo/{codigo}":{"get":{"operationId":"ubigeo.consultar","summary":"Departamento, provincia y distrito de un código INEI","description":"Catálogo de 1,900 divisiones territoriales. Sin scope: no resuelve ningún documento.","security":[{"bearer":[]}],"parameters":[{"name":"codigo","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9]{6}$"}}],"x-scopes":[],"x-consume-cuota":false}},"/uso":{"get":{"operationId":"uso","summary":"Consumo del periodo, desglosado","description":"Solo del tenant del token. `cuota.consumidas` sale del contador en vivo —es el que decide en cada petición— y `consultas.*` del consolidado, que se vuelca cada 60 s: pueden diferir en un minuto. No incluye el hit rate: es un KPI interno y necesita las consultas que no encontraron nada.","security":[{"bearer":[]}],"parameters":[],"x-scopes":[],"x-consume-cuota":false}},"/watch/eventos":{"get":{"operationId":"watch.eventos","summary":"Historial de cambios detectados","description":"Los eventos que el diff del padrón generó para los RUC vigilados.\n\nScopes: `watch.manage`.","security":[{"bearer":["watch.manage"]}],"parameters":[],"x-scopes":["watch.manage"],"x-consume-cuota":false}},"/watch/listas":{"get":{"operationId":"watch.listas","summary":"Listar watchlists","description":"Con el número de RUCs vigilados en cada una.\n\nScopes: `watch.manage`.","security":[{"bearer":["watch.manage"]}],"parameters":[],"x-scopes":["watch.manage"],"x-consume-cuota":false},"post":{"operationId":"watch.listas.crear","summary":"Crear una watchlist","description":"Sujeta al tope del plan. Un plan sin Watch lo dice explícitamente.\n\nScopes: `watch.manage`.","security":[{"bearer":["watch.manage"]}],"parameters":[],"x-scopes":["watch.manage"],"x-consume-cuota":false}},"/watch/listas/{lista}/rucs":{"post":{"operationId":"watch.rucs.agregar","summary":"Agregar RUCs a una watchlist","description":"🔒 **Solo tipo `20`.** El payload del webhook lleva el nombre, y para un RUC `10` eso sería identidad saliendo a una URL ajena. El tope de RUCs es del TENANT sumando todas sus listas, y un lote que lo rebase se rechaza entero.\n\nScopes: `watch.manage`.","security":[{"bearer":["watch.manage"]}],"parameters":[{"name":"lista","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9]+$"}}],"x-scopes":["watch.manage"],"x-consume-cuota":false},"delete":{"operationId":"watch.rucs.quitar","summary":"Quitar RUCs de una watchlist","description":"Deja de vigilarlos desde el diff siguiente.\n\nScopes: `watch.manage`.","security":[{"bearer":["watch.manage"]}],"parameters":[{"name":"lista","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9]+$"}}],"x-scopes":["watch.manage"],"x-consume-cuota":false}},"/watch/webhooks":{"get":{"operationId":"watch.webhooks","summary":"Listar endpoints de webhook","description":"El secreto de firma no se devuelve nunca tras crearlo.\n\nScopes: `watch.manage`.","security":[{"bearer":["watch.manage"]}],"parameters":[],"x-scopes":["watch.manage"],"x-consume-cuota":false},"post":{"operationId":"watch.webhooks.crear","summary":"Registrar un endpoint de webhook","description":"Devuelve el secreto de firma **una sola vez**. Las entregas van firmadas con HMAC-SHA256 sobre timestamp y cuerpo (§12.4).\n\nScopes: `watch.manage`.","security":[{"bearer":["watch.manage"]}],"parameters":[],"x-scopes":["watch.manage"],"x-consume-cuota":false}},"/watch/webhooks/{endpoint}/probar":{"post":{"operationId":"watch.webhooks.probar","summary":"Enviar una entrega de prueba","description":"Firmada y marcada como prueba. No cuenta para el deshabilitado automático.\n\nScopes: `watch.manage`.","security":[{"bearer":["watch.manage"]}],"parameters":[{"name":"endpoint","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9]+$"}}],"x-scopes":["watch.manage"],"x-consume-cuota":false}}}}