mx-identifiers: Validate and Generate Mexican RFC, CURP, CLABE and NSS
Zero-dependency TypeScript npm library that validates and generates Mexican RFC, CURP, CLABE and NSS, with check digits verified against public vectors. Node and browser.
- Role
- Design & Development
- Year
- 2026
- Published
mx-identifiers is a TypeScript npm library that validates and generates the four identifiers a Mexican invoicing, payroll or onboarding form ends up needing: RFC (tax ID), CURP (population registry ID), CLABE (18-digit bank account number) and NSS (social security number), with check digits verified against public vectors. One package instead of four, zero dependencies, the same code in Node and in the browser, MIT licensed.
npm install mx-identifiers
Why it exists
npm has a package for CLABE, another for CURP and almost nothing covering all four, let alone one that also generates valid test data. This is the module behind the Mexican ID validator and the test data generator on this site, extracted as-is. It is also the arithmetic the Mexican tax MCP server runs on. If you want the maths rather than the package, the algorithms are worked through here.
Usage
import { validateRfc, validateClabe, isCurp, generatePerson } from 'mx-identifiers';
validateRfc('GODE561231GR8');
// { valid: true, normalized: 'GODE561231GR8', kind: 'fisica',
// parts: { iniciales: 'GODE', fecha: '561231', homoclave: 'GR', digito: '8' },
// birthDate: '1956-12-31', expectedCheckDigit: '8', errors: [] }
validateClabe('002010077777777771').bankName; // 'Banamex'
isCurp('not-a-curp'); // false, never throws
generatePerson(); // fictional person: RFC and CURP derive from the same name and birth date
Validators never throw. They return an object with valid, the identifier’s pieces (parts), the check digit the algorithm expected (expectedCheckDigit) and an errors array of stable, language-neutral codes (empty, length, shape, digits, date, state, inconvenient, checksum) that you map to your own copy. When all you want is a boolean there are isRfc, isCurp, isClabe and isNss. Field names such as iniciales or homoclave are the Spanish terms the identifiers are made of; the API itself is in English.
What is in the box
- Validation:
validateRfc(13-character individuals and 12-character companies),validateCurp(18 characters, resolves state and sex),validateClabe(18 digits, resolves the bank) andvalidateNss(11 digits, Luhn). Input is normalized first: uppercased, with spaces, hyphens, dots and underscores stripped. - Test data generation:
generatePerson,generateCompany,buildRfcFisica,buildRfcMoral,buildCurp,buildClabeandbuildNss. They apply the public naming rules: strip accents, map Ñ to X, skip particles (de,la,del…), use the second given name when the first is José or María, and replace the second letter with X when the initials spell a word RENAPO forbids. - Catalogs:
CURP_STATES,CLABE_BANKS,INCONVENIENT_WORDSand the CFDI 4.0 invoice catalogs (CFDI_REGIMEN,CFDI_USO,CFDI_FORMA_PAGO…), handy for driving a<select>. The bank list follows Banxico’s listing of 6 October 2026; the CFDI ones were checked against the SAT’scatCFDIspreadsheet. - Two things a hand-rolled implementation tends to get wrong: the SAT’s generic RFC
XAXX010101000must be allowlisted explicitly or you reject what every consumer invoice in Mexico carries, and the CLABE check digit keeps the last digit of each product (63 counts as 3), not the digit sum as Luhn would (which would give 9). - Tested and published: 173 tests that run against the built package (both the ESM and CommonJS entry points), with vectors from the SAT, python-stdnum and Banxico; CI on Node 18, 20, 22 and 24; published on npm with verifiable provenance.
What it does not do
- Valid does not mean it exists. It checks structure, date and check digit; it does not query the SAT, RENAPO, IMSS or Banxico. To find out whether an invoice is still live, see the MCP server.
- Generated data is fictional. It passes any format and check-digit validation but belongs to no real person or record. The RFC homoclave is random, so results are self-consistent, not a prediction of what the SAT would issue. Use it for development databases and fixtures, never to fill official filings.
- Not reproducible yet. Generation uses
Math.random(); seeded generation is on the roadmap. - The bank catalog is not Banxico’s full list: a code that is not in it is reported as unknown (
bankName: null) rather than given an invented name, and it does not invalidate the CLABE.
Tech stack
- TypeScript, strict, built with tsup to ESM and CommonJS with type declarations
- Node.js ≥ 18 and browsers, no Node APIs
- Zero runtime dependencies
- node:test for the suite
- GitHub Actions for CI and publishing
Repository
View on GitHub → · Package on npm →
Goal
So nobody has to rewrite, again and with a bug in the CLABE, the algorithms behind four public identifiers. One library, with the SAT, Banxico and RENAPO vectors as proof, equally good for validating a form and for seeding a development database.