Skip to main content
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

npm install @origoid/sdk
Package on npm: @origoid/sdk. Source on github.com/origoid/sdk-node (public, for auditing).

Initialize the client

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.
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 for the full contract.

Methods by resource

The client groups operations under one property per regulatory domain.

client.authentication

await client.authentication.issueToken({ expireAfter: 1800 });

client.renapo

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

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

await client.imss.lookupNss({ curp: "PELJ900101HDFRRN09" });
await client.imss.getEmploymentStatus({
  curp: "PELJ900101HDFRRN09",
  nss: "92038109713",
});

client.ine

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

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

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

client.email

await client.email.validateEmail({ email: "user@example.com" });

client.proofOfAddress

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:
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:
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:
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": "..." },
  },
);
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).

TypeScript

Every request and response type is exported under the OrigoidApi namespace:
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:
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.