> ## 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.

# Node / TypeScript SDK

> `@origoid/sdk` — official client for Node.js 18+ and TypeScript projects.

The Node SDK ships with full TypeScript definitions, automatic retries,
and one strongly-typed method per OrigoID endpoint. It works in any
Node 18+ runtime and in Deno.

## Install

<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>

Package on npm: [`@origoid/sdk`](https://www.npmjs.com/package/@origoid/sdk).
Source on [github.com/origoid/sdk-node](https://github.com/origoid/sdk-node)
(public, for auditing).

## Initialize the client

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

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

That's the whole setup — there is nothing else to configure. Never
hardcode the API key in source. Load it from `process.env`, a secrets
manager (Vault, 1Password, Doppler, etc.), or your platform's config
layer.

## Your first call

The example below uses `PELJ900101HDFRRN09`, a **synthetic CURP from
the OpenAPI examples** — not a real person's CURP. Replace it with the
CURP you need to validate.

```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", // synthetic — replace with real input
});

if (envelope.status === "OK" && envelope.type === "SUCCESS") {
  // envelope.data holds the RENAPO record.
  console.log("CURP holder:", envelope.data);
} else {
  // Other result type — check the endpoint's response catalog to drive your logic.
  console.log(envelope.type, "—", envelope.message);
}
```

Every method returns the same `Envelope` shape: `{ status, type,
message, data, transactionId, processedAt, billable, errors? }`. See
[Response envelope](/en/envelope) for the full contract.

## Methods by resource

The client groups operations under one property per regulatory domain.

### `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: "<base64 jpg>", back: "<base64 jpg>" });
await client.ine.extractQrData({ back: "<base64 jpg of credential back>" });
```

### `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",
});
```

Note: the method for the SAT 69-B list is `searchSat69B` (capital B).
The other compliance methods follow the regular camelCase pattern.

### `client.biometrics`

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

### `client.email`

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

### `client.proofOfAddress`

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

## Error handling

The SDK distinguishes between **business errors** (returned inside the
envelope) and **transport errors** (thrown as typed exceptions).

### Business errors — read from the envelope

For any HTTP 200, including `INVALID_REQUEST`, the SDK returns a
normal `Envelope` object. Inspect `status` and `type` before using
`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" — could still be a business-level NOT_FOUND.
  switch (env.type) {
    case "SUCCESS":            /* env.data holds the record */ break;
    case "CURP_NOT_FOUND":     /* no match */ break;
    // see /en/errors and /en/catalogs for the full catalog
  }
}
```

### Transport errors — caught with try/catch

For `401`, `429`, and unrecoverable transport failures the SDK throws
typed errors:

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

try {
  const env = await client.renapo.validateCurp({ curp: "PELJ900101HDFRRN09" });
  // … use env
} catch (err) {
  if (err instanceof OrigoidApiTimeoutError) {
    // request did not complete within the configured timeout
  } else if (err instanceof OrigoidApiError) {
    // err.statusCode — 401, 429, etc.
    // err.body — the envelope returned by the server
  } else {
    throw err; // unexpected
  }
}
```

## Per-call configuration (advanced)

Every method accepts an optional second argument:

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

<Info>
  **Read this before tuning timeouts or retries.** The SDK only sends a
  retry for `5xx` and network failures, never for successful business
  responses — so retries do **not** create duplicate billable calls when
  the API responded correctly. They **do** create extra calls when the
  request actually failed: a request that times out three times can
  consume three credits if the request eventually succeeded on a later
  attempt.

  * **Defaults (`timeoutInSeconds: 60`, `maxRetries: 2`) are right for
    almost every workload.** Change them only with a specific reason.
  * Combining a long timeout with high `maxRetries` (e.g. `120s` × `5`)
    means a single failing request can occupy a client thread for up to
    10 minutes — bad for your own throughput and infrastructure.
  * Set per-call overrides only on endpoints with known slow cold
    starts (some compliance and INE-list calls).
</Info>

## TypeScript

Every request and response type is exported under the `OrigoidApi`
namespace:

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

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

## CommonJS

The package ships both ESM and CJS entry points. In a CommonJS
project:

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

## Browser usage

**Do not call OrigoID directly from a browser.** API keys are
long-lived credentials that grant billable access to your account; the
moment a key reaches a browser bundle, browser console, or local
storage, it is effectively public and at risk of abuse — the same way
you would never put a credit-card processor's secret key in
client-side code.

The correct pattern is **backend-for-frontend (BFF)**: your browser
talks to your server, your server holds the API key and calls OrigoID
from a trusted environment (Node, Python, Go).

If your use case truly requires browser-direct calls (partner widget,
embedded form, etc.) we can enable CORS for your specific origins. The
key stays out of the browser only if you scope it carefully and pair
it with referrer/origin restrictions — we will help you design that
flow. Reach out and we will work through the architecture with you.
