NexoID

Empezar a consultar

Cuatro formas de hacer la primera consulta. Las cuatro hacen lo mismo: piden una ficha de RUC, distinguen «no existe» de «no se pudo preguntar» y leen la procedencia del dato.

🔒 El token no va al navegador. Es la primera sorpresa de toda migración: quien hoy consulta con el token en el front —una variable VITE_… o escrito en una plantilla— está publicando una credencial. Con la de NexoID expondría su cuota, su identidad y su auditoría, y cualquiera podría gastar por él.

La integración correcta es un proxy en tu propio backend: tu página llama a tu servidor, y tu servidor llama aquí con el token. Si ese proxy lo consume una página sin sesión, ponle límite por IP.

Antes de la primera llamada

URL base https://nexoid.victorllerena.com/id/v1
Autenticación Authorization: Bearer nxid_live_…, siempre desde tu servidor
Scope del ejemplo ruc.read
Versión 1.29.1 — los cambios aditivos no la suben (§15.7)

🔒 Un RUC que empieza en 10, 15 o 17 es una persona natural, y su nombre es identidad: esa consulta exige dni.read y finalidad declarada aunque la ruta sea /ruc. Con solo ruc.read recibes un 403, y no es un error de configuración: es la regla.

curl

consulta.sh

# El token sale del entorno: escrito en el guion acaba en el historial y en el repositorio.
curl -s -H "Authorization: Bearer $NEXOID_TOKEN" \
     -H "Accept: application/json" \
     "https://nexoid.victorllerena.com/id/v1/ruc/20131312955" | jq '.data.nombre, .meta.fecha_dato'

PHP

NexoId.php

<?php
// Laravel. 🔒 En el SERVIDOR: el token no baja al navegador.
use Illuminate\Support\Facades\Http;

$respuesta = Http::withToken(config('services.nexoid.token'))
    ->acceptJson()
    ->timeout(5)
    ->get('https://nexoid.victorllerena.com/id/v1/ruc/20131312955');

if ($respuesta->status() === 404) {
    // No está en el padrón. `meta.degradado` distingue «no existe» de
    // «no se pudo preguntar»: no son lo mismo y no se tratan igual.
    $degradado = (bool) $respuesta->json('meta.degradado');

    return $degradado ? null : 'no existe';
}

if ($respuesta->failed()) {
    // El `codigo` es estable; el `mensaje` puede cambiar. Programa contra el código.
    report(new RuntimeException((string) $respuesta->json('error.codigo')));
}

$nombre = $respuesta->json('data.nombre');

JavaScript / TypeScript

nexoid.ts

// Node, Next.js o cualquier backend. 🔒 Nunca desde el navegador:
// el token expondría tu cuota, tu identidad y tu auditoría.
const respuesta = await fetch('https://nexoid.victorllerena.com/id/v1/ruc/20131312955', {
  headers: {
    Authorization: `Bearer ${process.env.NEXOID_TOKEN}`,
    Accept: 'application/json',
  },
});

if (respuesta.status === 404) {
  const { meta } = await respuesta.json();
  // `degradado: true` = no se pudo preguntar. No lo guardes como «no existe».
  return meta.degradado ? null : 'no existe';
}

if (!respuesta.ok) {
  const { error } = await respuesta.json();
  throw new Error(error.codigo); // el código es estable; el mensaje, no
}

const { data, meta } = await respuesta.json();
console.log(data.nombre, meta.fecha_dato, meta.origen);

Python

nexoid.py

import os
import requests  # 🔒 En el servidor. El token no viaja al cliente.

respuesta = requests.get(
    "https://nexoid.victorllerena.com/id/v1/ruc/20131312955",
    headers={"Authorization": f"Bearer {os.environ['NEXOID_TOKEN']}", "Accept": "application/json"},
    timeout=5,
)

if respuesta.status_code == 404:
    meta = respuesta.json()["meta"]
    # degradado=True: no se pudo preguntar. Distinto de «no existe».
    nombre = None if meta.get("degradado") else "no existe"
elif not respuesta.ok:
    # El código es estable; el mensaje puede cambiar.
    raise RuntimeError(respuesta.json()["error"]["codigo"])
else:
    cuerpo = respuesta.json()
    nombre = cuerpo["data"]["nombre"]

Qué hacer con cada respuesta

EstadoQué significaQué hacer
200 La ficha, con meta.origen y meta.fecha_dato Guarda también la procedencia: es lo que te deja explicar un dato meses después
404 meta.degradado dice si se pudo preguntar Con degradado: true, no lo guardes como «no existe»
403 Scope, finalidad o plan Mira /me: dice cuál de los tres te falta
429 Ritmo, cuota o presupuesto Reintenta pasado Retry-After; si es cuota, no se arregla esperando: vuelve en X-Cuota-Reinicio, o antes con una bolsa. Cada respuesta trae X-Cuota-Restante para verlo venir

El catálogo completo de errores, con qué hacer ante cada uno, está en la documentación. Y el contrato entero, en OpenAPI 3.1: genera tu cliente de ahí en vez de escribir los tipos a mano.