> ## Documentation Index
> Fetch the complete documentation index at: https://docs.origoid.com/llms.txt
> Use this file to discover all available pages before exploring further.

# SDK Node / TypeScript

> `@origoid/sdk` — cliente oficial para Node.js 18+ y proyectos TypeScript.

El SDK de Node trae definiciones TypeScript completas, reintentos
automáticos y un método tipado por cada endpoint de OrigoID. Funciona
en cualquier runtime Node 18+ y en Deno.

## Instalación

<CodeGroup>
  ```bash npm theme={null}
  npm install @origoid/sdk
  ```

  ```bash yarn theme={null}
  yarn add @origoid/sdk
  ```

  ```bash pnpm theme={null}
  pnpm add @origoid/sdk
  ```

  ```bash bun theme={null}
  bun add @origoid/sdk
  ```
</CodeGroup>

Paquete en npm: [`@origoid/sdk`](https://www.npmjs.com/package/@origoid/sdk).
Código en [github.com/origoid/sdk-node](https://github.com/origoid/sdk-node)
(público, para auditar).

## Inicializa el cliente

```typescript theme={null}
import { OrigoidApiClient } from "@origoid/sdk";

const client = new OrigoidApiClient({
  apiKey: process.env.ORIGOID_API_KEY!,
});
```

Ese es todo el setup — no hay nada más que configurar. Nunca
hardcodees la API key. Cárgala desde `process.env`, un secrets manager
(Vault, 1Password, Doppler, etc.), o la capa de config de tu
plataforma.

## Tu primera llamada

El ejemplo usa `PELJ900101HDFRRN09`, un **CURP sintético de los
ejemplos del OpenAPI** — no es CURP de persona real. Reemplázalo con
el CURP que necesites validar.

```typescript theme={null}
import { OrigoidApiClient } from "@origoid/sdk";

const client = new OrigoidApiClient({ apiKey: process.env.ORIGOID_API_KEY! });

const envelope = await client.renapo.validateCurp({
  curp: "PELJ900101HDFRRN09", // sintético — reemplazar con input real
});

if (envelope.status === "OK" && envelope.type === "SUCCESS") {
  // envelope.data trae el registro RENAPO.
  console.log("Titular del CURP:", envelope.data);
} else {
  // Otro tipo de resultado — revisa el catálogo de respuestas del endpoint para tu lógica.
  console.log(envelope.type, "—", envelope.message);
}
```

Cada método devuelve la misma shape `Envelope`: `{ status, type,
message, data, transactionId, processedAt, billable, errors? }`. Ver
[Envelope de respuesta](/es/envelope) para el contrato completo.

## Métodos por recurso

El cliente agrupa operaciones bajo una propiedad por dominio
regulatorio.

### `client.authentication`

```typescript theme={null}
await client.authentication.issueToken({ expireAfter: 1800 });
```

### `client.renapo`

```typescript theme={null}
await client.renapo.validateCurp({ curp: "PELJ900101HDFRRN09" });
await client.renapo.lookupCurp({
  givenNames: "JUAN",
  firstSurname: "PEREZ",
  secondSurname: "LOPEZ",
  dateOfBirth: "1990-01-01",
  gender: "H",
  birthStateCode: "DF",
});
```

### `client.sat`

```typescript theme={null}
await client.sat.validateRfc({ rfc: "PEZJ811011KI1" });
await client.sat.extractCsf({ rfc: "PEZJ811011KI1", cif: "17060597619" });
await client.sat.validateCfdi({
  uuid: "7C8BD4EA-AE86-4CB5-88B8-C6E61E988A8B",
  rfcEmisor: "PEZJ811011KI1",
  rfcReceptor: "EMP170623KI3",
  total: "999999.99",
});
```

### `client.imss`

```typescript theme={null}
await client.imss.lookupNss({ curp: "PELJ900101HDFRRN09" });
await client.imss.getEmploymentStatus({
  curp: "PELJ900101HDFRRN09",
  nss: "92038109713",
});
```

### `client.ine`

```typescript theme={null}
await client.ine.validateVoterList({ cic: "123456789", citizenIdentifier: "987654321" });
await client.ine.extractVoterIdData({ front: "<jpg base64>", back: "<jpg base64>" });
await client.ine.extractQrData({ back: "<jpg base64 del reverso>" });
```

### `client.compliance`

```typescript theme={null}
await client.compliance.searchSat69({ rfc: "PEZJ811011KI1" });
await client.compliance.searchSat69B({ rfc: "PEZJ811011KI1" });
await client.compliance.searchOfac({ name: "John Doe", minSimilarityScore: 85 });
await client.compliance.searchPeps({
  givenNames: "JUAN",
  firstSurname: "PEREZ",
  secondSurname: "LOPEZ",
});
```

Nota: el método para la lista SAT 69-B es `searchSat69B` (con B
mayúscula). Los demás métodos de compliance siguen el patrón regular
camelCase.

### `client.biometrics`

```typescript theme={null}
await client.biometrics.matchFaces({
  face: "<selfie base64>",
  front: "<frente id base64>",
  threshold: 80,
  documentType: "INE",
});
await client.biometrics.checkLiveness({ selfie: "<selfie base64>" });
```

### `client.email`

```typescript theme={null}
await client.email.validateEmail({ email: "user@example.com" });
```

### `client.proofOfAddress`

```typescript theme={null}
await client.proofOfAddress.extractProofOfAddress({ file: "<pdf o jpg base64>" });
```

## Manejo de errores

El SDK distingue entre **errores de negocio** (vienen dentro del
envelope) y **errores de transporte** (lanzados como excepciones
tipadas).

### Errores de negocio — léelos del envelope

Para cualquier respuesta HTTP 200, incluyendo `INVALID_REQUEST`, el
SDK devuelve un objeto `Envelope` normal. Revisa `status` y `type`
antes de usar `data`:

```typescript theme={null}
const env = await client.renapo.validateCurp({ curp: "BAD" });

if (env.status === "ERROR") {
  // INVALID_REQUEST, SERVICE_UNAVAILABLE, …
  console.error(env.type, env.message);
} else {
  // status === "OK" — puede ser un NOT_FOUND de negocio
  switch (env.type) {
    case "SUCCESS":            /* env.data trae el registro */ break;
    case "CURP_NOT_FOUND":     /* sin match */ break;
    // ver /es/errors y /es/catalogs para el catálogo completo
  }
}
```

### Errores de transporte — try/catch

Para `401`, `429` y fallas de transporte irrecuperables el SDK lanza
errores tipados:

```typescript theme={null}
import {
  OrigoidApiClient,
  OrigoidApiError,
  OrigoidApiTimeoutError,
} from "@origoid/sdk";

try {
  const env = await client.renapo.validateCurp({ curp: "PELJ900101HDFRRN09" });
  // … usa env
} catch (err) {
  if (err instanceof OrigoidApiTimeoutError) {
    // request no completó dentro del timeout configurado
  } else if (err instanceof OrigoidApiError) {
    // err.statusCode — 401, 429, etc.
    // err.body — envelope que devolvió el servidor
  } else {
    throw err; // inesperado
  }
}
```

## Configuración por llamada (avanzado)

Cada método acepta un segundo argumento opcional:

```typescript theme={null}
await client.ine.validateVoterList(
  { cic: "123456789", citizenIdentifier: "987654321" },
  {
    timeoutInSeconds: 120,   // default 60
    maxRetries: 3,           // default 2
    abortSignal: ac.signal,  // AbortController estándar
    headers: { "x-trace-id": "..." },
  },
);
```

<Info>
  **Lee esto antes de tunear timeouts o retries.** El SDK sólo
  reintenta errores `5xx` y fallas de red, nunca respuestas de negocio
  exitosas — entonces los retries **no** crean llamadas duplicadas
  facturables cuando el API respondió correctamente. **Sí** crean
  llamadas extra cuando el request realmente falló: un request que
  hace timeout tres veces puede consumir tres créditos si la llamada
  eventualmente tuvo éxito en un intento posterior.

  * **Los defaults (`timeoutInSeconds: 60`, `maxRetries: 2`) son
    correctos para casi cualquier workload.** Cambia sólo con razón
    específica.
  * Combinar timeout largo con `maxRetries` alto (ej. `120s` × `5`)
    significa que un único request fallando puede ocupar un thread
    cliente hasta 10 minutos — malo para tu throughput y tu
    infraestructura.
  * Sobrescribe per-call sólo en endpoints con cold starts lentos
    conocidos (algunas llamadas de compliance y de lista INE).
</Info>

## TypeScript

Cada tipo de request y response se exporta bajo el namespace
`OrigoidApi`:

```typescript theme={null}
import { OrigoidApi } from "@origoid/sdk";

function handleResult(env: OrigoidApi.Envelope) {
  if (env.status === "OK" && env.type === "SUCCESS" && env.data) {
    // env.data está tipado
  }
}
```

## CommonJS

El paquete trae ambos entry points ESM y CJS. En proyectos CommonJS:

```javascript theme={null}
const { OrigoidApiClient } = require("@origoid/sdk");
const client = new OrigoidApiClient({ apiKey: process.env.ORIGOID_API_KEY });
```

## Uso desde navegador

**No llames a OrigoID directo desde un navegador.** Las API keys son
credenciales de larga vida con acceso facturable a tu cuenta; en el
momento que una key llega al bundle del navegador, a la consola, o a
local storage, queda efectivamente pública y en riesgo de abuso — del
mismo modo que nunca pondrías la secret key de un procesador de
tarjetas en código del lado cliente.

El patrón correcto es **backend-for-frontend (BFF)**: tu navegador
habla con tu servidor, tu servidor guarda la API key y llama a OrigoID
desde un ambiente confiable (Node, Python, Go).

Si tu caso de uso genuinamente requiere llamadas directas desde
navegador (widget de partner, formulario embebido, etc.) podemos
habilitar CORS para tus orígenes específicos. La key sólo permanece
segura si la escopeas con cuidado y la complementas con restricciones
de referrer/origen — te ayudamos a diseñar ese flujo. Contáctanos y
trabajamos la arquitectura contigo.
