{"_id":"@allus-fyi/company-data","_rev":"20-8d81855a8d4bdfda0f63e4bf3709335b","name":"@allus-fyi/company-data","dist-tags":{"latest":"0.0.23"},"versions":{"0.0.3":{"name":"@allus-fyi/company-data","version":"0.0.3","keywords":["allus","allme","company-data","sdk"],"author":{"name":"allme.fyi"},"license":"MIT","_id":"@allus-fyi/company-data@0.0.3","maintainers":[{"name":"allus","email":"sdk@allme.fyi"}],"homepage":"https://github.com/allus-fyi/company-data-typescript#readme","bugs":{"url":"https://github.com/allus-fyi/company-data-typescript/issues"},"dist":{"shasum":"3704a2dc9d2bf52f581124f1b1c33aa90c4efdef","tarball":"https://registry.npmjs.org/@allus-fyi/company-data/-/company-data-0.0.3.tgz","fileCount":43,"integrity":"sha512-0LKaIFcFJRme+9G+rKzEwvk7kWlVMjCEfLecRdF72ehrlGayVRqw+XuiKfBXkZZd+N3wVOfrdVheBlhr5ORcNA==","signatures":[{"sig":"MEUCIQC/tsKiUa3NQvOw9vVbFCsMsFsd5Ia5aJOGH7vhW+SaOAIgErurK53CwsKQhZONOe1+iqX1AukIDC+Y2fUgwFML5vM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":366858},"main":"./dist/cjs/index.js","type":"module","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"83a59a7f40498cd0dde5d5823f446fd46e8cf941","scripts":{"test":"node --test --import tsx test/*.test.ts","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:types && node scripts/fixup-cjs.mjs","clean":"rimraf dist","build:cjs":"tsc -p tsconfig.cjs.json","build:esm":"tsc -p tsconfig.esm.json","build:types":"tsc -p tsconfig.types.json"},"_npmUser":{"name":"allus","email":"sdk@allme.fyi"},"repository":{"url":"git+https://github.com/allus-fyi/company-data-typescript.git","type":"git"},"_npmVersion":"11.9.0","description":"TypeScript/Node SDK for the allus company-data API: typed, plaintext, slug-keyed conclusions with transparent decryption.","directories":{},"_nodeVersion":"25.6.1","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","rimraf":"^5.0.0","typescript":"^5.6.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/company-data_0.0.3_1781790827444_0.7672830425730279","host":"s3://npm-registry-packages-npm-production"}},"0.0.4":{"name":"@allus-fyi/company-data","version":"0.0.4","keywords":["allus","allme","company-data","sdk"],"author":{"name":"allme.fyi"},"license":"MIT","_id":"@allus-fyi/company-data@0.0.4","maintainers":[{"name":"allus","email":"sdk@allme.fyi"}],"homepage":"https://github.com/allus-fyi/company-data-typescript#readme","bugs":{"url":"https://github.com/allus-fyi/company-data-typescript/issues"},"dist":{"shasum":"c6a46407eebfcefcda0a0fe85e81911243839e0c","tarball":"https://registry.npmjs.org/@allus-fyi/company-data/-/company-data-0.0.4.tgz","fileCount":43,"integrity":"sha512-Z55lBK1AsX2aRznbRrWND9eKRT0kxLYrJcJ5YhynBtlPNol/n5mWcq5ntnCo1H3nlkYFKPwMmY0lxWa1iJFRXg==","signatures":[{"sig":"MEUCIQCCDOj3ZNdnV/r28DYSINRsk2cv8fJ0xsYhJAzBVivZzgIgca8O6Ek5dsfjbBUOv39zyEla+T0/05FI/nV3cN9d6gk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@allus-fyi%2fcompany-data@0.0.4","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":366986},"main":"./dist/cjs/index.js","type":"module","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"3bf3890582acb69ddc223b31008a3c060a21033f","scripts":{"test":"node --test --import tsx test/*.test.ts","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:types && node scripts/fixup-cjs.mjs","clean":"rimraf dist","build:cjs":"tsc -p tsconfig.cjs.json","build:esm":"tsc -p tsconfig.esm.json","build:types":"tsc -p tsconfig.types.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:05068bde-d04d-4826-997a-97c135f9afe0"}},"repository":{"url":"git+https://github.com/allus-fyi/company-data-typescript.git","type":"git"},"_npmVersion":"11.17.0","description":"TypeScript/Node SDK for the allus company-data API: typed, plaintext, slug-keyed conclusions with transparent decryption.","directories":{},"_nodeVersion":"20.20.2","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","rimraf":"^5.0.0","typescript":"^5.6.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/company-data_0.0.4_1781793238424_0.5360886862705321","host":"s3://npm-registry-packages-npm-production"}},"0.0.5":{"name":"@allus-fyi/company-data","version":"0.0.5","keywords":["allus","allme","company-data","sdk"],"author":{"name":"allme.fyi"},"license":"MIT","_id":"@allus-fyi/company-data@0.0.5","maintainers":[{"name":"allus","email":"sdk@allme.fyi"}],"homepage":"https://github.com/allus-fyi/company-data-typescript#readme","bugs":{"url":"https://github.com/allus-fyi/company-data-typescript/issues"},"dist":{"shasum":"a459fb7868adc854acb2679bb54a4ca1578167e2","tarball":"https://registry.npmjs.org/@allus-fyi/company-data/-/company-data-0.0.5.tgz","fileCount":43,"integrity":"sha512-EHXGBPxxokr/D1WHruUSc9lpJ8IvLQ3a8DGuEwKE6YDlOQffJjyDpinrrhFXlBtfzL2/yt02x9rS8x8yEwaxRg==","signatures":[{"sig":"MEQCIFEUIpx4UcKIkp2HQJSMq25TKvRjvokA9zimQEi9/ZLsAiBuEA6RQp8KeHZujyr+PTk4HCv+lbxRS1U/2zDoainrug==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@allus-fyi%2fcompany-data@0.0.5","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":418748},"main":"./dist/cjs/index.js","type":"module","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"d7b20a1c2d347235e4bc5a3a202ae70cd0956236","scripts":{"test":"node --test --import tsx test/*.test.ts","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:types && node scripts/fixup-cjs.mjs","clean":"rimraf dist","build:cjs":"tsc -p tsconfig.cjs.json","build:esm":"tsc -p tsconfig.esm.json","build:types":"tsc -p tsconfig.types.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:05068bde-d04d-4826-997a-97c135f9afe0"}},"repository":{"url":"git+https://github.com/allus-fyi/company-data-typescript.git","type":"git"},"_npmVersion":"11.17.0","description":"TypeScript/Node SDK for the allus company-data API: typed, plaintext, slug-keyed conclusions with transparent decryption.","directories":{},"_nodeVersion":"20.20.2","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","rimraf":"^5.0.0","typescript":"^5.6.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/company-data_0.0.5_1782129966033_0.3303109613323296","host":"s3://npm-registry-packages-npm-production"}},"0.0.6":{"name":"@allus-fyi/company-data","version":"0.0.6","keywords":["allus","allme","company-data","sdk"],"author":{"name":"allme.fyi"},"license":"MIT","_id":"@allus-fyi/company-data@0.0.6","maintainers":[{"name":"allus","email":"sdk@allme.fyi"}],"homepage":"https://github.com/allus-fyi/company-data-typescript#readme","bugs":{"url":"https://github.com/allus-fyi/company-data-typescript/issues"},"dist":{"shasum":"d6f03847f8955464d7e214be72de13a4676b4823","tarball":"https://registry.npmjs.org/@allus-fyi/company-data/-/company-data-0.0.6.tgz","fileCount":43,"integrity":"sha512-AO2u2ISf/DV4IXOW8UO5ejfulIILIkBn9czR+qpip322tdWvlk8BAyXTRo6ATvYXBnUMsbaJgx4QTq7CLgp94Q==","signatures":[{"sig":"MEUCIDr1C4qCzmmAbxvhB/LaKeIo/6sTez+Eqz0jZy7NwaOeAiEA2MUbnNRCsBrVfT1UXjT1QFQ7o58Cfz4NNcouWjtsRLg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@allus-fyi%2fcompany-data@0.0.6","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":423055},"main":"./dist/cjs/index.js","type":"module","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"52ee569d4e2c52e5522113b5b7d1ccb8c3338ba7","scripts":{"test":"node --test --import tsx test/*.test.ts","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:types && node scripts/fixup-cjs.mjs","clean":"rimraf dist","build:cjs":"tsc -p tsconfig.cjs.json","build:esm":"tsc -p tsconfig.esm.json","build:types":"tsc -p tsconfig.types.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:05068bde-d04d-4826-997a-97c135f9afe0"}},"repository":{"url":"git+https://github.com/allus-fyi/company-data-typescript.git","type":"git"},"_npmVersion":"11.17.0","description":"TypeScript/Node SDK for the allus company-data API: typed, plaintext, slug-keyed conclusions with transparent decryption.","directories":{},"_nodeVersion":"20.20.2","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","rimraf":"^5.0.0","typescript":"^5.6.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/company-data_0.0.6_1782134429344_0.7475167948849335","host":"s3://npm-registry-packages-npm-production"}},"0.0.7":{"name":"@allus-fyi/company-data","version":"0.0.7","keywords":["allus","allme","company-data","sdk"],"author":{"name":"allme.fyi"},"license":"MIT","_id":"@allus-fyi/company-data@0.0.7","maintainers":[{"name":"allus","email":"sdk@allme.fyi"}],"homepage":"https://github.com/allus-fyi/company-data-typescript#readme","bugs":{"url":"https://github.com/allus-fyi/company-data-typescript/issues"},"dist":{"shasum":"31eb5905de7d46c780bf96e0dc37b1ed4f13e02c","tarball":"https://registry.npmjs.org/@allus-fyi/company-data/-/company-data-0.0.7.tgz","fileCount":46,"integrity":"sha512-mZSbPeV8Ai9eaiOwGJQvR/27NUZ1VCVrRZTNcHTMbrvjNYiWgLSI+69ZjA4Z4dz4HyZhEGgfrAbrbqiq4NZdKQ==","signatures":[{"sig":"MEQCIDuS7HmftBVNMjVk2xgtvDmWr8m8ToIkM6bTUvXIvvUjAiAOx6j4EhhDpdAEvrhqo0dK85k40Ohen6bbQXOV+aCPIg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@allus-fyi%2fcompany-data@0.0.7","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":471590},"main":"./dist/cjs/index.js","type":"module","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"e3a5de4532aeafb0c1a37c050eb39b5d622dc5cc","scripts":{"test":"node --test --import tsx test/*.test.ts","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:types && node scripts/fixup-cjs.mjs","clean":"rimraf dist","build:cjs":"tsc -p tsconfig.cjs.json","build:esm":"tsc -p tsconfig.esm.json","build:types":"tsc -p tsconfig.types.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:05068bde-d04d-4826-997a-97c135f9afe0"}},"repository":{"url":"git+https://github.com/allus-fyi/company-data-typescript.git","type":"git"},"_npmVersion":"11.17.0","description":"TypeScript/Node SDK for the allus company-data API: typed, plaintext, slug-keyed conclusions with transparent decryption.","directories":{},"_nodeVersion":"20.20.2","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","rimraf":"^5.0.0","typescript":"^5.6.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/company-data_0.0.7_1782399728000_0.7774272356725982","host":"s3://npm-registry-packages-npm-production"}},"0.0.8":{"name":"@allus-fyi/company-data","version":"0.0.8","keywords":["allus","allme","company-data","sdk"],"author":{"name":"allme.fyi"},"license":"MIT","_id":"@allus-fyi/company-data@0.0.8","maintainers":[{"name":"allus","email":"sdk@allme.fyi"}],"homepage":"https://github.com/allus-fyi/company-data-typescript#readme","bugs":{"url":"https://github.com/allus-fyi/company-data-typescript/issues"},"dist":{"shasum":"4c580d2ccb7f6af7b4c5ba096c8f202eacbc13fd","tarball":"https://registry.npmjs.org/@allus-fyi/company-data/-/company-data-0.0.8.tgz","fileCount":46,"integrity":"sha512-sfU2IJUYoJqbsjQ6AQCIyoy1O9VzpwGhTMEDWhMI3RdLt7bjg4kwFjLLl0IM4zVXc0JSevU6gG8VrEPXreJdcg==","signatures":[{"sig":"MEUCIQCSOdjfFItMIX61r8QtmMlSJfsJ6OF5gbJ+X0anjnIcngIgT2x0f4hRGJEP/d27KoVeymhWH8zL7dssxopmpOlQWek=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@allus-fyi%2fcompany-data@0.0.8","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":475023},"main":"./dist/cjs/index.js","type":"module","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"efb6aba05b427c2074a935190ea59ab869f751f1","scripts":{"test":"node --test --import tsx test/*.test.ts","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:types && node scripts/fixup-cjs.mjs","clean":"rimraf dist","build:cjs":"tsc -p tsconfig.cjs.json","build:esm":"tsc -p tsconfig.esm.json","build:types":"tsc -p tsconfig.types.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:05068bde-d04d-4826-997a-97c135f9afe0"}},"repository":{"url":"git+https://github.com/allus-fyi/company-data-typescript.git","type":"git"},"_npmVersion":"11.18.0","description":"TypeScript/Node SDK for the allus company-data API: typed, plaintext, slug-keyed conclusions with transparent decryption.","directories":{},"_nodeVersion":"20.20.2","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","rimraf":"^5.0.0","typescript":"^5.6.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/company-data_0.0.8_1782811310547_0.04714419532261149","host":"s3://npm-registry-packages-npm-production"}},"0.0.9":{"name":"@allus-fyi/company-data","version":"0.0.9","keywords":["allus","allme","company-data","sdk"],"author":{"name":"allme.fyi"},"license":"MIT","_id":"@allus-fyi/company-data@0.0.9","maintainers":[{"name":"allus","email":"sdk@allme.fyi"}],"homepage":"https://github.com/allus-fyi/company-data-typescript#readme","bugs":{"url":"https://github.com/allus-fyi/company-data-typescript/issues"},"dist":{"shasum":"d38ead954684e6735bdba372c19356658899deb8","tarball":"https://registry.npmjs.org/@allus-fyi/company-data/-/company-data-0.0.9.tgz","fileCount":46,"integrity":"sha512-aa21O2Krc8A1wbcB5xy+RmKRtijpfXqym33c2MNc+c82UDAYugzzlviTDp8NhoNyNZkuYRU+uo5+RhcJmBCpGw==","signatures":[{"sig":"MEUCIDgUma17Ku5xvRScCUA/nmWPQGDY/X8tbflUrN/AVWP5AiEAnjpk+aB7LQZe/iMkStlRdH97mAZRcg5PEuRWx2Ht1vg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@allus-fyi%2fcompany-data@0.0.9","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":476359},"main":"./dist/cjs/index.js","type":"module","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"fcfe38d3c0af170170b2525b98416acbe522eeee","scripts":{"test":"node --test --import tsx test/*.test.ts","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:types && node scripts/fixup-cjs.mjs","clean":"rimraf dist","build:cjs":"tsc -p tsconfig.cjs.json","build:esm":"tsc -p tsconfig.esm.json","build:types":"tsc -p tsconfig.types.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:05068bde-d04d-4826-997a-97c135f9afe0"}},"repository":{"url":"git+https://github.com/allus-fyi/company-data-typescript.git","type":"git"},"_npmVersion":"11.18.0","description":"TypeScript/Node SDK for the allus company-data API: typed, plaintext, slug-keyed conclusions with transparent decryption.","directories":{},"_nodeVersion":"20.20.2","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","rimraf":"^5.0.0","typescript":"^5.6.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/company-data_0.0.9_1782812150703_0.3806112944320299","host":"s3://npm-registry-packages-npm-production"}},"0.0.10":{"name":"@allus-fyi/company-data","version":"0.0.10","keywords":["allus","allme","company-data","sdk"],"author":{"name":"allme.fyi"},"license":"MIT","_id":"@allus-fyi/company-data@0.0.10","maintainers":[{"name":"allus","email":"sdk@allme.fyi"}],"homepage":"https://github.com/allus-fyi/company-data-typescript#readme","bugs":{"url":"https://github.com/allus-fyi/company-data-typescript/issues"},"dist":{"shasum":"235491c90a0579b1d0eac0ff897732a9da49c8ae","tarball":"https://registry.npmjs.org/@allus-fyi/company-data/-/company-data-0.0.10.tgz","fileCount":46,"integrity":"sha512-Fq3NkNV9exNY1qTMtLwm/6v/Wp2ImYjZ586FJLKj1txu6kM92ofYeEUzhVx6/VQ/oc4WS6w6nz0v1GO9QXBF/A==","signatures":[{"sig":"MEYCIQCgx6kl28Be1EoSTtDCLmyNo+Cm6BBLgHrm/X4Jw+Us7QIhAIy4zuf1kMNEIJgE4rP9A8ZpQ0EJ12fMlPbmg2YyO62b","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@allus-fyi%2fcompany-data@0.0.10","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":476917},"main":"./dist/cjs/index.js","type":"module","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"0b55ee9c9aa34ce11ad4f71824d28808dd52098d","scripts":{"test":"node --test --import tsx test/*.test.ts","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:types && node scripts/fixup-cjs.mjs","clean":"rimraf dist","build:cjs":"tsc -p tsconfig.cjs.json","build:esm":"tsc -p tsconfig.esm.json","build:types":"tsc -p tsconfig.types.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:05068bde-d04d-4826-997a-97c135f9afe0"}},"repository":{"url":"git+https://github.com/allus-fyi/company-data-typescript.git","type":"git"},"_npmVersion":"11.18.0","description":"TypeScript/Node SDK for the allus company-data API: typed, plaintext, slug-keyed conclusions with transparent decryption.","directories":{},"_nodeVersion":"20.20.2","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","rimraf":"^5.0.0","typescript":"^5.6.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/company-data_0.0.10_1782813378514_0.7016348337658629","host":"s3://npm-registry-packages-npm-production"}},"0.0.11":{"name":"@allus-fyi/company-data","version":"0.0.11","keywords":["allus","allme","company-data","sdk"],"author":{"name":"allme.fyi"},"license":"MIT","_id":"@allus-fyi/company-data@0.0.11","maintainers":[{"name":"allus","email":"sdk@allme.fyi"}],"homepage":"https://github.com/allus-fyi/company-data-typescript#readme","bugs":{"url":"https://github.com/allus-fyi/company-data-typescript/issues"},"dist":{"shasum":"62e104e994fa5eaa24cce4ab235a520fc4bc3f1a","tarball":"https://registry.npmjs.org/@allus-fyi/company-data/-/company-data-0.0.11.tgz","fileCount":46,"integrity":"sha512-MOShD+nL48RdjHvdrwvxb4iLsRWnCbILSiJnRAOmrwEiSYDFCI3rJJhqzEIaZMFLr+tAzMm+9c+7shVApJ6rSQ==","signatures":[{"sig":"MEUCIH8OlDHpMFGolKuRpNqWddFa859TW+iS1K6V+srGmYTNAiEAqpurezQ1/IXMquFxPzSROdMyYHpMPJAyybvvMd+cpI8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@allus-fyi%2fcompany-data@0.0.11","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":477703},"main":"./dist/cjs/index.js","type":"module","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"8bc89e138ae4278fc2b7cde3a02dcc4bbefb4aa1","scripts":{"test":"node --test --import tsx test/*.test.ts","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:types && node scripts/fixup-cjs.mjs","clean":"rimraf dist","build:cjs":"tsc -p tsconfig.cjs.json","build:esm":"tsc -p tsconfig.esm.json","build:types":"tsc -p tsconfig.types.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:05068bde-d04d-4826-997a-97c135f9afe0"}},"repository":{"url":"git+https://github.com/allus-fyi/company-data-typescript.git","type":"git"},"_npmVersion":"11.18.0","description":"TypeScript/Node SDK for the allus company-data API: typed, plaintext, slug-keyed conclusions with transparent decryption.","directories":{},"_nodeVersion":"20.20.2","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","rimraf":"^5.0.0","typescript":"^5.6.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/company-data_0.0.11_1782818113220_0.27863145178670146","host":"s3://npm-registry-packages-npm-production"}},"0.0.12":{"name":"@allus-fyi/company-data","version":"0.0.12","keywords":["allus","allme","company-data","sdk"],"author":{"name":"allme.fyi"},"license":"MIT","_id":"@allus-fyi/company-data@0.0.12","maintainers":[{"name":"allus","email":"sdk@allme.fyi"}],"homepage":"https://github.com/allus-fyi/company-data-typescript#readme","bugs":{"url":"https://github.com/allus-fyi/company-data-typescript/issues"},"dist":{"shasum":"794f107c5e99e36496224b0779cd588829a22f35","tarball":"https://registry.npmjs.org/@allus-fyi/company-data/-/company-data-0.0.12.tgz","fileCount":49,"integrity":"sha512-rxmayEzzgD6TxwcWcGTSKBAcVLd96nmIklQNPeaX7Xx0IM+lUgJ1xSVb0spP/e8efBv/ceRn7hmq6ktUCoZzdA==","signatures":[{"sig":"MEUCICyjq4oDAIaodlWblwQdmLbwbw4vDt8YQFlyzhpgWgSvAiEAj3tqLfU481FPGBQygatDsNdyHFY7s1J+g0LNMDCi1WA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@allus-fyi%2fcompany-data@0.0.12","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":542787},"main":"./dist/cjs/index.js","type":"module","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"8b4dd49ee1ffda5e7e36f591cba597840a07c6c5","scripts":{"test":"node --test --import tsx test/*.test.ts","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:types && node scripts/fixup-cjs.mjs","clean":"rimraf dist","build:cjs":"tsc -p tsconfig.cjs.json","build:esm":"tsc -p tsconfig.esm.json","build:types":"tsc -p tsconfig.types.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:05068bde-d04d-4826-997a-97c135f9afe0"}},"repository":{"url":"git+https://github.com/allus-fyi/company-data-typescript.git","type":"git"},"_npmVersion":"11.18.0","description":"TypeScript/Node SDK for the allus company-data API: typed, plaintext, slug-keyed conclusions with transparent decryption.","directories":{},"_nodeVersion":"20.20.2","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","rimraf":"^5.0.0","typescript":"^5.6.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/company-data_0.0.12_1783502704639_0.7804048253268847","host":"s3://npm-registry-packages-npm-production"}},"0.0.13":{"name":"@allus-fyi/company-data","version":"0.0.13","keywords":["allus","allme","company-data","sdk"],"author":{"name":"allme.fyi"},"license":"MIT","_id":"@allus-fyi/company-data@0.0.13","maintainers":[{"name":"allus","email":"sdk@allme.fyi"}],"homepage":"https://github.com/allus-fyi/company-data-typescript#readme","bugs":{"url":"https://github.com/allus-fyi/company-data-typescript/issues"},"dist":{"shasum":"9324184dabcf9d4127dd39beddc7383e4c8c622e","tarball":"https://registry.npmjs.org/@allus-fyi/company-data/-/company-data-0.0.13.tgz","fileCount":55,"integrity":"sha512-KCDAa5aAN10c2ZAPzlK3GrSogB98sfeHEWKPE/ZNqzY51nnkQebLXMJlXlVGUJluBAeltrOIh9qiGBlz1Qz0bw==","signatures":[{"sig":"MEUCIB+/ZGPJh7ZAkwzCYMRestrVKuKxo4L2ZMRKxzzTR98pAiEAlMe15F9kp2coihGw8ALEHeOzCRWH75SpzX/au6zi52E=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@allus-fyi%2fcompany-data@0.0.13","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":578100},"main":"./dist/cjs/index.js","type":"module","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"5868c0713cb8a5041f3aabcf6426a091927b54e4","scripts":{"test":"node --test --import tsx test/*.test.ts","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:types && node scripts/fixup-cjs.mjs","clean":"rimraf dist","build:cjs":"tsc -p tsconfig.cjs.json","build:esm":"tsc -p tsconfig.esm.json","build:types":"tsc -p tsconfig.types.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:05068bde-d04d-4826-997a-97c135f9afe0"}},"repository":{"url":"git+https://github.com/allus-fyi/company-data-typescript.git","type":"git"},"_npmVersion":"12.0.1","description":"TypeScript/Node SDK for the allus company-data API: typed, plaintext, slug-keyed conclusions with transparent decryption.","directories":{},"_nodeVersion":"22.23.1","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","rimraf":"^5.0.0","typescript":"^5.6.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/company-data_0.0.13_1784122660370_0.5608289533789221","host":"s3://npm-registry-packages-npm-production"}},"0.0.14":{"name":"@allus-fyi/company-data","version":"0.0.14","keywords":["allus","allme","company-data","sdk"],"author":{"name":"allme.fyi"},"license":"MIT","_id":"@allus-fyi/company-data@0.0.14","maintainers":[{"name":"allus","email":"sdk@allme.fyi"}],"homepage":"https://github.com/allus-fyi/company-data-typescript#readme","bugs":{"url":"https://github.com/allus-fyi/company-data-typescript/issues"},"dist":{"shasum":"20a10f35411b9be77eab64c62d28e2d3c753e2a8","tarball":"https://registry.npmjs.org/@allus-fyi/company-data/-/company-data-0.0.14.tgz","fileCount":61,"integrity":"sha512-00ngFYZyABjw6ljwRYaRDYzwbJS/Cq4ldN7LsAzcha5jyr31jMZ9t0537wesmS/HhjG1r518xkYPyPeJnLqIhw==","signatures":[{"sig":"MEYCIQDZMy8Z5rpRbWYAjYJISahjIJ8Hygs8hKe9nzB6IVOy+wIhAIkAagdaPNeNt5sAeVmpelaiPj+MOz1GxjqToZ5I+AVm","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@allus-fyi%2fcompany-data@0.0.14","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":665905},"main":"./dist/cjs/index.js","type":"module","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"cc47794c18e24f44cf2d0df5e03d705414950c25","scripts":{"test":"node --test --import tsx test/*.test.ts","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:types && node scripts/fixup-cjs.mjs","clean":"rimraf dist","build:cjs":"tsc -p tsconfig.cjs.json","build:esm":"tsc -p tsconfig.esm.json","build:types":"tsc -p tsconfig.types.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:05068bde-d04d-4826-997a-97c135f9afe0"}},"repository":{"url":"git+https://github.com/allus-fyi/company-data-typescript.git","type":"git"},"_npmVersion":"12.0.1","description":"TypeScript/Node SDK for the allus company-data API: typed, plaintext, slug-keyed conclusions with transparent decryption.","directories":{},"_nodeVersion":"22.23.1","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","rimraf":"^5.0.0","typescript":"^5.6.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/company-data_0.0.14_1784998844926_0.08169766625843566","host":"s3://npm-registry-packages-npm-production"}},"0.0.15":{"name":"@allus-fyi/company-data","version":"0.0.15","keywords":["allus","allme","company-data","sdk"],"author":{"name":"allme.fyi"},"license":"MIT","_id":"@allus-fyi/company-data@0.0.15","maintainers":[{"name":"allus","email":"sdk@allme.fyi"}],"homepage":"https://github.com/allus-fyi/company-data-typescript#readme","bugs":{"url":"https://github.com/allus-fyi/company-data-typescript/issues"},"dist":{"shasum":"c8329c8072c4069dee5f7584689a95c2d6f30a1a","tarball":"https://registry.npmjs.org/@allus-fyi/company-data/-/company-data-0.0.15.tgz","fileCount":77,"integrity":"sha512-SbbVEjqf5kFv9ch8xuMj98dSq1B7/k0tK5GWAgunDhKHkJAGRWs7i6rjCJ2sbr323aN1JZgrEtha61dedkrT1w==","signatures":[{"sig":"MEQCIH2uEimuR+WOqd/d/nVtp9byvFFABA+irmBacsxVkZnzAiAq/cxvMQBq4pN01zIBgqL9RIrd7Heyfg45onIKDNg/5w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@allus-fyi%2fcompany-data@0.0.15","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":794242},"main":"./dist/cjs/index.js","type":"module","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"b79036ecef0670a4ea8c4071f858fc7865046861","scripts":{"test":"node --test --import tsx test/*.test.ts","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:types && node scripts/fixup-cjs.mjs","clean":"rimraf dist","build:cjs":"tsc -p tsconfig.cjs.json","build:esm":"tsc -p tsconfig.esm.json","build:types":"tsc -p tsconfig.types.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:05068bde-d04d-4826-997a-97c135f9afe0"}},"repository":{"url":"git+https://github.com/allus-fyi/company-data-typescript.git","type":"git"},"_npmVersion":"12.0.1","description":"TypeScript/Node SDK for the allus company-data API: typed, plaintext, slug-keyed conclusions with transparent decryption.","directories":{},"_nodeVersion":"22.23.1","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","rimraf":"^5.0.0","typescript":"^5.6.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/company-data_0.0.15_1785152613476_0.3348848867676313","host":"s3://npm-registry-packages-npm-production"}},"0.0.16":{"name":"@allus-fyi/company-data","version":"0.0.16","keywords":["allus","allme","company-data","sdk"],"author":{"name":"allme.fyi"},"license":"MIT","_id":"@allus-fyi/company-data@0.0.16","maintainers":[{"name":"allus","email":"sdk@allme.fyi"}],"homepage":"https://github.com/allus-fyi/company-data-typescript#readme","bugs":{"url":"https://github.com/allus-fyi/company-data-typescript/issues"},"dist":{"shasum":"1aad5473efb4e0aa3648e88baa86e97e91b57965","tarball":"https://registry.npmjs.org/@allus-fyi/company-data/-/company-data-0.0.16.tgz","fileCount":77,"integrity":"sha512-C/ljOskJNjht7HdzIHwDs0ENP5/s8GizlgGQbjW3hFoTvyw6ZVzYKl7x0clxYRbHwE4NBEs4z42NEf53+15BYQ==","signatures":[{"sig":"MEYCIQCpn8Lm00VwaOlapzf9EvnAflDIjsaeNOOHjb6epmO6agIhAKTZcCvChiAF0U7ZVHVaGIQd869z7oZ8fNjZ/PB5Robc","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@allus-fyi%2fcompany-data@0.0.16","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":794699},"main":"./dist/cjs/index.js","type":"module","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"d2576a2c54422805271457857011d3f1448e45f2","scripts":{"test":"node --test --import tsx test/*.test.ts","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:types && node scripts/fixup-cjs.mjs","clean":"rimraf dist","build:cjs":"tsc -p tsconfig.cjs.json","build:esm":"tsc -p tsconfig.esm.json","build:types":"tsc -p tsconfig.types.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:05068bde-d04d-4826-997a-97c135f9afe0"}},"repository":{"url":"git+https://github.com/allus-fyi/company-data-typescript.git","type":"git"},"_npmVersion":"12.0.1","description":"TypeScript/Node SDK for the allus company-data API: typed, plaintext, slug-keyed conclusions with transparent decryption.","directories":{},"_nodeVersion":"22.23.1","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","rimraf":"^5.0.0","typescript":"^5.6.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/company-data_0.0.16_1785156552675_0.29012345446323895","host":"s3://npm-registry-packages-npm-production"}},"0.0.17":{"name":"@allus-fyi/company-data","version":"0.0.17","keywords":["allus","allme","company-data","sdk"],"author":{"name":"allme.fyi"},"license":"MIT","_id":"@allus-fyi/company-data@0.0.17","maintainers":[{"name":"allus","email":"sdk@allme.fyi"}],"homepage":"https://github.com/allus-fyi/company-data-typescript#readme","bugs":{"url":"https://github.com/allus-fyi/company-data-typescript/issues"},"dist":{"shasum":"3f50d399abc7fd213d8345034d7892ebef411450","tarball":"https://registry.npmjs.org/@allus-fyi/company-data/-/company-data-0.0.17.tgz","fileCount":77,"integrity":"sha512-V72nQTTi1wZS7u1B87m8AzHU7jcY/PaLRHxSnz+LtFxK0hiv4+3q+QHo56pc+5RvDWH9qKZP3NS2wRKz521kNA==","signatures":[{"sig":"MEYCIQDsQylYs9TcMN436MIwaP1L6l+gzpShT3SMb8YzmZpckgIhAO46pTFRumQhesxTPwemnvt8NQvYGD+D6e02K6BXz734","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@allus-fyi%2fcompany-data@0.0.17","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":859974},"main":"./dist/cjs/index.js","type":"module","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"de0d613c759f856f4214c3144c69f2273eb0c208","scripts":{"test":"node --test --import tsx test/*.test.ts","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:types && node scripts/fixup-cjs.mjs","clean":"rimraf dist","build:cjs":"tsc -p tsconfig.cjs.json","build:esm":"tsc -p tsconfig.esm.json","build:types":"tsc -p tsconfig.types.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:05068bde-d04d-4826-997a-97c135f9afe0"}},"repository":{"url":"git+https://github.com/allus-fyi/company-data-typescript.git","type":"git"},"_npmVersion":"12.0.2","description":"TypeScript/Node SDK for the allus company-data API: typed, plaintext, slug-keyed conclusions with transparent decryption.","directories":{},"_nodeVersion":"22.23.1","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","rimraf":"^5.0.0","typescript":"^5.6.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/company-data_0.0.17_1785748739633_0.1770255210983862","host":"s3://npm-registry-packages-npm-production"}},"0.0.18":{"name":"@allus-fyi/company-data","version":"0.0.18","keywords":["allus","allme","company-data","sdk"],"author":{"name":"allme.fyi"},"license":"MIT","_id":"@allus-fyi/company-data@0.0.18","maintainers":[{"name":"allus","email":"sdk@allme.fyi"}],"homepage":"https://github.com/allus-fyi/company-data-typescript#readme","bugs":{"url":"https://github.com/allus-fyi/company-data-typescript/issues"},"dist":{"shasum":"54eefb4289abe76985d39b9e44a73eea95a3fabc","tarball":"https://registry.npmjs.org/@allus-fyi/company-data/-/company-data-0.0.18.tgz","fileCount":77,"integrity":"sha512-pK+vXOuDOhkekPDSRBac4ZV1/cnlJvIVMXCo+3JDYLPMghPleucF4gVFJnNVEnQBxZPK5XC/FkKewgp4abvYCA==","signatures":[{"sig":"MEQCIEZVnf8QvNNHT4d7PeYNbfYFO05PsyepmHTH+7Z8xSWOAiALmIoQwXFoUCM75dfFMZdkq5SjZ24MIEsDz+TNzLzOrg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@allus-fyi%2fcompany-data@0.0.18","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":862891},"main":"./dist/cjs/index.js","type":"module","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"befbd22afd9ae66b24bf2f6fc196953a5e6f4349","scripts":{"test":"node --test --import tsx test/*.test.ts","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:types && node scripts/fixup-cjs.mjs","clean":"rimraf dist","build:cjs":"tsc -p tsconfig.cjs.json","build:esm":"tsc -p tsconfig.esm.json","build:types":"tsc -p tsconfig.types.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:05068bde-d04d-4826-997a-97c135f9afe0"}},"repository":{"url":"git+https://github.com/allus-fyi/company-data-typescript.git","type":"git"},"_npmVersion":"12.0.2","description":"TypeScript/Node SDK for the allus company-data API: typed, plaintext, slug-keyed conclusions with transparent decryption.","directories":{},"_nodeVersion":"22.23.1","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","rimraf":"^5.0.0","typescript":"^5.6.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/company-data_0.0.18_1785828968161_0.9833008130610903","host":"s3://npm-registry-packages-npm-production"}},"0.0.19":{"name":"@allus-fyi/company-data","version":"0.0.19","keywords":["allus","allme","company-data","sdk"],"author":{"name":"allme.fyi"},"license":"MIT","_id":"@allus-fyi/company-data@0.0.19","maintainers":[{"name":"allus","email":"sdk@allme.fyi"}],"homepage":"https://github.com/allus-fyi/company-data-typescript#readme","bugs":{"url":"https://github.com/allus-fyi/company-data-typescript/issues"},"dist":{"shasum":"5398a317eeff2090a64805bfb90f8e5167f50575","tarball":"https://registry.npmjs.org/@allus-fyi/company-data/-/company-data-0.0.19.tgz","fileCount":77,"integrity":"sha512-L6bXmwhmz8Vr2Kf9doj3syHqFWxozEjjl9c4lS9WFeRIfZ9puaKl4VOQhKYQjWQnBilbNTtPz1G0f4awLPn/bA==","signatures":[{"sig":"MEUCIGkzAvG4tbB+q+710VoZ6R2cPXGYttaMLd3hNWaibjm8AiEAzeuJ8zREVbANtsk8Hz/AigBOrZmVF0RIUABa5+t38pg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@allus-fyi%2fcompany-data@0.0.19","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":867961},"main":"./dist/cjs/index.js","type":"module","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"6d8e44ec499a152814a3f568f1b0220aebeeecce","scripts":{"test":"node --test --import tsx test/*.test.ts","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:types && node scripts/fixup-cjs.mjs","clean":"rimraf dist","build:cjs":"tsc -p tsconfig.cjs.json","build:esm":"tsc -p tsconfig.esm.json","build:types":"tsc -p tsconfig.types.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:05068bde-d04d-4826-997a-97c135f9afe0"}},"repository":{"url":"git+https://github.com/allus-fyi/company-data-typescript.git","type":"git"},"_npmVersion":"12.0.2","description":"TypeScript/Node SDK for the allus company-data API: typed, plaintext, slug-keyed conclusions with transparent decryption.","directories":{},"_nodeVersion":"22.23.1","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","rimraf":"^5.0.0","typescript":"^5.6.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/company-data_0.0.19_1785840146521_0.1493005288998701","host":"s3://npm-registry-packages-npm-production"}},"0.0.21":{"name":"@allus-fyi/company-data","version":"0.0.21","keywords":["allus","allme","company-data","sdk"],"author":{"name":"allme.fyi"},"license":"MIT","_id":"@allus-fyi/company-data@0.0.21","maintainers":[{"name":"allus","email":"sdk@allme.fyi"}],"homepage":"https://github.com/allus-fyi/company-data-typescript#readme","bugs":{"url":"https://github.com/allus-fyi/company-data-typescript/issues"},"dist":{"shasum":"cf5a99e83a93936fdf8466af450f33b5c876b9ed","tarball":"https://registry.npmjs.org/@allus-fyi/company-data/-/company-data-0.0.21.tgz","fileCount":77,"integrity":"sha512-c18ficAkkjQhk2Vc1KAzktqBofHDzFEJKpvwf7HPXGZQwviS8CQN6yYBhS1LTgTZjrSK1PGc06sxY/w5abM74Q==","signatures":[{"sig":"MEUCIAIJQ93H6arvNWI9ECskhn85y3Vw2KhwSoEoevB99cVsAiEAqp5X0SE+tibyeszP2MZQHAuRGmtr7PI4q+tyJqvTgAA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@allus-fyi%2fcompany-data@0.0.21","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":919446},"main":"./dist/cjs/index.js","type":"module","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"db91e5e23bb302866bb7e9186312bd6f515439e8","scripts":{"test":"node --test --import tsx test/*.test.ts","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:types && node scripts/fixup-cjs.mjs","clean":"rimraf dist","build:cjs":"tsc -p tsconfig.cjs.json","build:esm":"tsc -p tsconfig.esm.json","build:types":"tsc -p tsconfig.types.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:05068bde-d04d-4826-997a-97c135f9afe0"}},"repository":{"url":"git+https://github.com/allus-fyi/company-data-typescript.git","type":"git"},"_npmVersion":"12.0.2","description":"TypeScript/Node SDK for the allus company-data API: typed, plaintext, slug-keyed conclusions with transparent decryption.","directories":{},"_nodeVersion":"22.23.1","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","rimraf":"^5.0.0","typescript":"^5.6.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/company-data_0.0.21_1786624886526_0.27320836432179907","host":"s3://npm-registry-packages-npm-production"}},"0.0.22":{"name":"@allus-fyi/company-data","version":"0.0.22","keywords":["allus","allme","company-data","sdk"],"author":{"name":"allme.fyi"},"license":"MIT","_id":"@allus-fyi/company-data@0.0.22","maintainers":[{"name":"allus","email":"sdk@allme.fyi"}],"homepage":"https://github.com/allus-fyi/company-data-typescript#readme","bugs":{"url":"https://github.com/allus-fyi/company-data-typescript/issues"},"dist":{"shasum":"4665a377fccc24808bc93b1acaf56c1f4cfcdef0","tarball":"https://registry.npmjs.org/@allus-fyi/company-data/-/company-data-0.0.22.tgz","fileCount":77,"integrity":"sha512-x5CHTv3DNdR5n+1c87CbWhHz8yTJphbBiGWyuI6d0wpSlBdKKqqZ7JqacMC0caU+sLIBgc7KSCW2EHmhRhU+mA==","signatures":[{"sig":"MEUCIQCgms/yR9HGYkA8Juk/6OY+wYvTufKELGOPf8iIQq3+0gIgaKznzH5aFkOg/Zm0cI214wRb+NmNsoIiNaAW94LXXOM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQC2nagungm45Z1/tyfz9UIsGSLfngqljAbA/65c5olRLAIgR6/mIfgp4CGmuXRzju2TKzCyzMF1+8ivJvwjCqV762s=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@allus-fyi%2fcompany-data@0.0.22","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":942933},"main":"./dist/cjs/index.js","type":"module","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"a9cfae92f472079643bc7781b0e874cdc048ebf9","scripts":{"test":"node --test --import tsx test/*.test.ts","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:types && node scripts/fixup-cjs.mjs","clean":"rimraf dist","build:cjs":"tsc -p tsconfig.cjs.json","build:esm":"tsc -p tsconfig.esm.json","build:types":"tsc -p tsconfig.types.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:05068bde-d04d-4826-997a-97c135f9afe0"}},"repository":{"url":"git+https://github.com/allus-fyi/company-data-typescript.git","type":"git"},"_npmVersion":"12.0.2","description":"TypeScript/Node SDK for the allus company-data API: typed, plaintext, slug-keyed conclusions with transparent decryption.","directories":{},"_nodeVersion":"22.23.2","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","rimraf":"^5.0.0","typescript":"^5.6.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/company-data_0.0.22_1788861539294_0.28078933554794183","host":"s3://npm-registry-packages-npm-production"}},"0.0.23":{"_id":"@allus-fyi/company-data@0.0.23","bugs":{"url":"https://github.com/allus-fyi/company-data-typescript/issues"},"dist":{"shasum":"10872e5e7061913726287b641e646d0ff9b9b387","tarball":"https://registry.npmjs.org/@allus-fyi/company-data/-/company-data-0.0.23.tgz","fileCount":80,"integrity":"sha512-fZ5k3m732z1/IVd9fkskLUj105orLZ1XnS6dJjTc0434YRICbT6hB0rFXsDGABIqngu8Mf1jX0FvLcog5OmyPA==","signatures":[{"sig":"MEQCIEYBnrQHOa36DhWSt/KpgrgpdzxlhdZChWhxT1l3EZ0CAiBoKd5BCGqufNnyH5RoOPBL4QRxCDD1W6ljrxTA+UUrHw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIHKjxAJSw56gX2DX3tqIj/L/wMTlG8BNWSJaNYE2sAxvAiA5YfE0D7kTRKtrirWMUvjZTqS9v+7NdYiUzvf4mWpN2A=="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@allus-fyi%2fcompany-data@0.0.23","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1045188},"main":"./dist/cjs/index.js","name":"@allus-fyi/company-data","type":"module","types":"./dist/types/index.d.ts","author":{"name":"allme.fyi"},"module":"./dist/esm/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"d53a15c9b788950becb3e1f62738363992965749","license":"MIT","scripts":{"test":"node --test --import tsx test/*.test.ts","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:types && node scripts/fixup-cjs.mjs","clean":"rimraf dist","build:cjs":"tsc -p tsconfig.cjs.json","build:esm":"tsc -p tsconfig.esm.json","build:types":"tsc -p tsconfig.types.json"},"version":"0.0.23","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:05068bde-d04d-4826-997a-97c135f9afe0"}},"homepage":"https://github.com/allus-fyi/company-data-typescript#readme","keywords":["allus","allme","company-data","sdk"],"repository":{"url":"git+https://github.com/allus-fyi/company-data-typescript.git","type":"git"},"_npmVersion":"12.0.2","description":"TypeScript/Node SDK for the allus company-data API: typed, plaintext, slug-keyed conclusions with transparent decryption.","directories":{},"maintainers":[{"name":"allus","email":"sdk@allme.fyi"}],"_nodeVersion":"22.23.2","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","rimraf":"^5.0.0","typescript":"^5.6.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/company-data_0.0.23_1788955748733_0.6654735692713047"}}},"time":{"created":"2026-06-18T13:53:47.238Z","modified":"2026-09-09T12:09:09.221Z","0.0.3":"2026-06-18T13:53:47.600Z","0.0.4":"2026-06-18T14:33:58.584Z","0.0.5":"2026-06-22T12:06:06.167Z","0.0.6":"2026-06-22T13:20:29.488Z","0.0.7":"2026-06-25T15:02:08.225Z","0.0.8":"2026-06-30T09:21:50.675Z","0.0.9":"2026-06-30T09:35:50.874Z","0.0.10":"2026-06-30T09:56:18.657Z","0.0.11":"2026-06-30T11:15:13.381Z","0.0.12":"2026-07-08T09:25:04.765Z","0.0.13":"2026-07-15T13:37:40.515Z","0.0.14":"2026-07-25T17:00:45.078Z","0.0.15":"2026-07-27T11:43:33.627Z","0.0.16":"2026-07-27T12:49:12.822Z","0.0.17":"2026-08-03T09:18:59.798Z","0.0.18":"2026-08-04T07:36:08.339Z","0.0.19":"2026-08-04T10:42:26.669Z","0.0.21":"2026-08-13T12:41:26.685Z","0.0.22":"2026-09-08T09:58:59.406Z","0.0.23":"2026-09-09T12:09:08.868Z"},"bugs":{"url":"https://github.com/allus-fyi/company-data-typescript/issues"},"author":{"name":"allme.fyi"},"license":"MIT","homepage":"https://github.com/allus-fyi/company-data-typescript#readme","keywords":["allus","allme","company-data","sdk"],"repository":{"url":"git+https://github.com/allus-fyi/company-data-typescript.git","type":"git"},"description":"TypeScript/Node SDK for the allus company-data API: typed, plaintext, slug-keyed conclusions with transparent decryption.","maintainers":[{"name":"allus","email":"sdk@allme.fyi"}],"readme":"# @allus-fyi/company-data (TypeScript / Node)\n\nThe TypeScript/Node SDK for the **allus company-data API**. Point it at a JSON\nconfig file and it hands back typed, plaintext, **your-slug-keyed conclusions**:\nfor each connected person, a map of *your request-field slug → plaintext value*\n(plus whether the value is live and when it last changed).\n\nThe SDK hides everything else — the OAuth token, the field catalog, the id\nplumbing, the hybrid decryption, binary fetching, the changes-queue mechanics,\nJSON-vs-XML. The platform is **zero-knowledge**: the API only ever holds\nciphertext, so all decryption happens inside the SDK with your service private\nkey. **The person's own field choices are never exposed** — you only ever see\nthe request slots you configured.\n\n> This SDK is one of six language ports that share an identical API surface.\n> This manual is the TypeScript view of it.\n\n**Contents:** [TL;DR — fetch new updates](#tldr--fetch-new-updates) ·\n[Quickstart](#quickstart) · [Every call](#every-call) ·\n[The typed value model](#the-typed-value-model) ·\n[The changes pump](#the-changes-pump) · [Webhooks](#webhooks) ·\n[Rate limits](#rate-limits) · [Errors](#errors) ·\n[How it's wired](#how-its-wired)\n\n---\n\n## TL;DR — fetch new updates\n\n```bash\nnpm install @allus-fyi/company-data\n```\n\nPoint a config.json at your service keys:\n\n```json\n{\n  \"api_url\": \"https://api.allme.fyi\",\n  \"client_id\": \"svc_xxx\",\n  \"client_secret\": \"xxx\",\n  \"service_private_key\": \"/path/to/service.pem\",\n  \"key_passphrase\": \"xxx\",\n  \"cache_dir\": \"./allus-cache\"\n}\n```\n\nDrain everything new, handled one update at a time:\n\n```ts\nimport { Client } from '@allus-fyi/company-data';\n\nconst client = Client.fromConfig('config.json');\n\nawait client.processChanges((change) => {\n  // event, person, slug, value, live, at\n  console.log(change.event, change.personId, change.slug, change.value, change.live, change.at);\n});\n```\n\n`processChanges` pulls every pending change, decrypts it, and hands them to your\ncallback ONE BY ONE, acking each only after your code returns. Crash mid-batch? The\nnext run replays exactly what wasn't acked — nothing is lost, and the API keeps no\nbacklog of its own. Run it on a schedule (cron / systemd timer); there is no\ndaemon/follow mode by design. Connections, binary values, and webhooks are\ndocumented below.\n\nDeeper reference pages live in [`docs/`](docs/):\n[config](docs/config.md) · [model](docs/model.md) · [pump](docs/pump.md) ·\n[webhooks](docs/webhooks.md) · [errors](docs/errors.md).\n\n---\n\n## Quickstart\n\nRequires **Node ≥ 18** (it uses the built-in global `fetch` and `node:crypto`).\nThe package ships **dual ESM + CommonJS** with bundled `.d.ts` types.\n\n```bash\nnpm install @allus-fyi/company-data\n# or, working from this repo:  npm install && npm run build   # from the repo root\n```\n\n```ts\n// ESM\nimport { Client } from '@allus-fyi/company-data';\n```\n```js\n// CommonJS\nconst { Client } = require('@allus-fyi/company-data');\n```\n\n### 1. Write a config file\n\nA single JSON file holds everything. Any field can be overridden by an `ALLUS_*`\nenv var, so secrets needn't live in the file. **No SDK method ever takes a key,\npassphrase, or secret as an argument** — they all come from here.\n\n`allus.json`:\n\n```json\n{\n  \"api_url\": \"https://api.allme.fyi\",\n  \"client_id\": \"svc_1a2b3c…\",\n  \"client_secret\": \"…\",\n  \"service_private_key\": \"./service-CRM.pem\",\n  \"key_passphrase\": \"…\",\n\n  \"account_private_key\": \"./account.pem\",\n  \"account_passphrase\": \"…\",\n\n  \"webhooks\": {\n    \"wh_abc123\": \"hmac_secret_for_that_webhook\"\n  },\n\n  \"cache_dir\": \"./allus-cache\",\n  \"format\": \"json\"\n}\n```\n\n| Field | Required | Meaning |\n|-------|----------|---------|\n| `api_url` | yes | API base, e.g. `https://api.allme.fyi`. |\n| `client_id` / `client_secret` | yes | The registered `client_credentials` credentials for **one** service. |\n| `service_private_key` | yes | Path to the OpenSSL-encrypted PKCS#8 PEM you downloaded from the portal. |\n| `key_passphrase` | yes | Decrypts that PEM in memory at startup. |\n| `account_private_key` / `account_passphrase` | only for `encrypt_payload` webhooks | The company **account** key, used to unwrap an encrypted webhook envelope. |\n| `webhooks` / `webhook_secret` | webhook auth — HMAC (default) | Per-webhook HMAC secrets keyed by webhook id (matched via the `X-Allus-Webhook-Id` header). A single-webhook service can use a flat `\"webhook_secret\": \"…\"` instead of the map. |\n| `webhook_bearer_token` | webhook auth — bearer | Verify `Authorization: Bearer <token>` deliveries. |\n| `webhook_basic` | webhook auth — basic | `{\"username\",\"password\"}` — verify HTTP Basic deliveries. |\n| `webhook_header` | webhook auth — header | `{\"name\",\"value\"}` — verify a custom-header delivery. |\n| `webhook_auth_none` | webhook auth — none | `true` — explicit opt-out; `verifyWebhook` always passes (use only behind your own gateway). **Configure at most one** webhook auth method (two+ → `ConfigError`). |\n| `cache_dir` | no (default `./allus-cache`) | Durable local buffer for the changes pump. Must be writable + durable. |\n| `format` | no (default `json`) | Wire format `json` or `xml`. Invisible in the output. |\n\nThe config-file keys are **snake_case** (`api_url`, `client_secret`, …); the SDK\nexposes them as camelCase (`config.apiUrl`, …). Env overrides use the `ALLUS_`\nprefix, e.g. `ALLUS_CLIENT_SECRET`, `ALLUS_KEY_PASSPHRASE`,\n`ALLUS_ACCOUNT_PASSPHRASE`, `ALLUS_WEBHOOK_SECRET`. A missing/invalid config (or an\nunreadable PEM / wrong passphrase) throws `ConfigError` at construction — fail\nfast.\n\n### 2. First call — list a connection's values\n\n```ts\nimport { Client } from '@allus-fyi/company-data';\n\nconst client = Client.fromConfig('allus.json');\n\n// Iterate every connected person (lazy, auto-paged).\nfor await (const conn of client.connections()) {\n  console.log(conn.displayName, conn.personId);\n  for (const [slug, val] of Object.entries(conn.values)) {\n    console.log(`  ${slug} = ${JSON.stringify(val.value)}  (live=${val.live}, updated=${val.updatedAt})`);\n  }\n  break; // just the first one for the demo\n}\n```\n\nOr fetch one connection by id:\n\n```ts\nconst conn = await client.connection('019xxxxxxxxxxxxxxxxxxxxxxxxx');\nconst email = conn.values['work_email'].value;        // \"alice@acme.com\"  (a string)\n```\n\n`Client.fromEnv()` builds the same client entirely from `ALLUS_*` env vars (no\nfile).\n\n---\n\n## Every call\n\n`Client` is the only object you construct. Build it from config, then:\n\n```ts\nClient.fromConfig(path, opts?): Client     // from a JSON file (env overrides secrets)\nClient.fromEnv(opts?):          Client      // entirely from ALLUS_* env vars\n```\n\n`opts` are advanced/optional: `http` (an injected `HttpClient`), `httpOptions`\n(passed to the default `HttpClient`: `transport`, `clock`, `maxRetries429`),\n`logger` (a console-compatible sink for the pump), `sleep` (a\n`(seconds) => Promise<void>`, for tests).\n\n### `requestFields()`\n\n```ts\nrequestFields(): Promise<RequestField[]>\n```\n\nYour request-field **definitions** — fetched once from\n`GET /api/company-data/request-fields` and cached for the life of the client (it\ntypes every value). Returns *your* request config, never the person's fields.\n\n* **Params:** none.\n* **Returns:** `Promise<RequestField[]>` — each `RequestField { slug, label, type, oneTime, mandatory, verified, verifiedMaxAgeDays, raw }`. `mandatory` is true when the field is mandatory-to-provide **or** mandatory-to-stay-connected.\n* **Throws:** `AuthError`, `ApiError`, `RateLimitError`.\n\n```ts\nfor (const f of await client.requestFields()) {\n  const flag = f.mandatory ? 'mandatory' : 'optional';\n  console.log(`${f.slug}  ${f.type}  ${flag}${f.oneTime ? ' (one-time)' : ''}`);\n}\n```\n\n### `connections(limit?, offset?)`\n\n```ts\nconnections(limit?: number, offset?: number): AsyncGenerator<Connection>\n```\n\nA **lazy async generator** that auto-pages\n`GET /api/company-data/connections?limit&offset` and yields one typed `Connection`\nat a time (bounded memory for a large book). Each `conn.values[slug]` is already\ndecrypted (or a lazy binary handle). It honors the response's `total` so it never\nover-fetches a page past the end (and also stops on a short page).\n\n* **Params:** `limit` — page size (default 100); `offset` — starting offset.\n* **Returns:** `AsyncGenerator<Connection>` — consume with `for await`.\n* **Throws:** `AuthError`, `ApiError`, `DecryptError` (per value, on access), `RateLimitError` (after the iterator's bounded internal backoff — see [Rate limits](#rate-limits)).\n\n> **Heavily rate-limited.** Use for the initial full sync + occasional\n> reconciliation only — never as a poll substitute for the changes feed. The\n> generator paces itself within the limit (backs off on `Retry-After`).\n\n```ts\n// Initial full sync, streaming so a 100k-connection book never lands in memory.\nfor await (const conn of client.connections(200)) {\n  await upsertLocalRecord(conn);\n}\n```\n\n### `connection(id)`\n\n```ts\nconnection(id: string): Promise<Connection>\n```\n\nFetch one connection by its connection id\n(`GET /api/company-data/connections/{id}`).\n\n* **Params:** `id` — the connection id (`Connection.id`).\n* **Returns:** `Promise<Connection>`. Note: this endpoint returns `{connection_id, user_id, values}` and **no** `displayName`/`connectedAt`, so those identity fields are `null` here (the list endpoint carries them).\n* **Throws:** `AuthError`, `ApiError` (404 if unknown), `DecryptError`, `RateLimitError`.\n\n```ts\nconst conn = await client.connection(connId);\nconst phone = conn.values['mobile'];\nif (phone) console.log(phone.value, phone.live ? 'live' : 'snapshot');\n```\n\n### `logs(limit?, offset?)`\n\n```ts\nlogs(limit?: number, offset?: number): Promise<LogEntry[]>\n```\n\nThe service's activity log (`GET /api/company-data/logs?limit&offset`) — **ops\nevents only** (email / purge / webhook), never person field data.\n\n* **Params:** `limit` (default 50), `offset` (default 0).\n* **Returns:** `Promise<LogEntry[]>` — each `LogEntry { type, message, metadata, at, raw }`.\n* **Throws:** `AuthError`, `ApiError`, `RateLimitError`.\n\n```ts\nfor (const entry of await client.logs(20)) {\n  console.log(entry.at, entry.type, entry.message);\n}\n```\n\n### `processChanges(handler, options?)`\n\n```ts\nprocessChanges(handler: (change: Change) => void | Promise<void>, options?): Promise<void>\n```\n\nThe crash-safe changes pump: drains the feed through `handler` **one `Change` at a\ntime**, durably buffering each batch before delivery, with per-item ack and\nretry → dead-letter → continue. Runs **until the feed is empty, then resolves** —\nthere is **no follow/daemon mode** (you schedule re-runs yourself). Delivery is\n**at-least-once**, so your handler **must be idempotent** (dedup on `Change.id`).\nSee [The changes pump](#the-changes-pump) for the full model.\n\n* **Params:** `handler` — your callback; called with one `Change`. Resolving/returning is an ack; throwing triggers retry. May be sync or async.\n* **Options:** `batchSize` (clamped to ≤ 500, default 100), `maxRetries` (default 3), `onError` (`\"deadletter\"` — default — or `\"halt\"`), `backoff` (`(attempt) => seconds`).\n* **Returns:** `Promise<void>` (resolves when the feed is empty + the buffer is drained).\n* **Throws:** `AuthError`, `ApiError`, `RateLimitError` (during a drain); `TypeError` (bad `onError`); whatever the handler throws if `onError=\"halt\"` and retries are exhausted.\n\n```ts\nasync function handle(change) {\n  if (await alreadyProcessed(change.id)) return;   // idempotency — dedup on the stable id\n  if (change.event === 'field_updated') {\n    await store(change.personId, change.slug, change.value);\n  } else if (change.event === 'connection_deleted' || change.event === 'field_deleted') {\n    await remove(change.personId, change.slug);\n  }\n  await markProcessed(change.id);\n}\n\nawait client.processChanges(handle);            // resolves when the feed is empty\n```\n\n> `logger` is **not** a `processChanges` option — pass it once to the `Client`\n> constructor (`Client.fromConfig('allus.json', { logger: myLogger })`).\n\n### Advanced changes primitives\n\n```ts\ndrainBatch(max?: number)                            : Promise<Change[]>     // raw, UNBUFFERED — you own durability\ndeadLetters()                                       : DeadLetterRecord[]    // the local dead-letter store\nretryDeadLetters(handler, options?)                 : Promise<number>       // re-drive dead-lettered events; resolves to count re-driven\n```\n\n* `drainBatch(max)` — fetches one batch (clamped ≤ 500) and returns the decrypted `Change`s directly. It does **not** persist anything, so a crash loses what the API already deleted. Prefer `processChanges` for safe consumption.\n* `deadLetters()` — each record is the stored (ciphertext) event plus a flattened `error` and `attempts`.\n* `retryDeadLetters(handler, options?)` — same `maxRetries` / `onError` / `backoff` options as `processChanges`; on success a record is removed, on repeated failure it stays dead-lettered (or re-throws under `\"halt\"`). Dead letters are never re-fetched from the API — the local store is their only home.\n\n```ts\nfor (const dl of client.deadLetters()) {\n  console.log('stuck:', dl.id, dl.error, 'after', dl.attempts, 'attempts');\n}\nconst n = await client.retryDeadLetters(handle);     // after you've fixed the bug\nconsole.log(`re-drove ${n} dead letters`);\n```\n\n### Key rotation — `key_rotated` and the public-key cache\n\nEvery client caches the RSA public keys it fetches: a person's key is immutable — until they\n**rotate** it. A person learns of a rotation from a silent push; your service gets no pushes, so the\n`key_rotated` change is your **only** signal. Without it a long-running worker keeps encrypting to\nthe rotated-away key for its whole lifetime, and the person can never read those values.\n\n**On the pump this is automatic** — the cached key is dropped as the change passes through, before\nyour handler sees it. **Over a webhook it is not:** the signature verifier is static and has no\nclient instance, so it cannot reach the cache. Call the invalidator yourself — noting that the two\nclients key their caches **differently**: the service client by `share_code`, the customer client by\nthe person's **user id**. Passing a share code to the customer client removes nothing and leaves you\nencrypting to the old key. Both identifiers ride every change, alongside `public_key_sha256` — the\nfingerprint of the person's new key.\n\n```ts\nif (change.event === 'key_rotated') {\n  client.invalidatePublicKey(change.shareCode);    // service Client — keyed by SHARE CODE\n  customer.invalidatePublicKey(change.personId);   // CustomerClient — keyed by PERSON USER ID\n  // change.publicKeySha256 = fingerprint of the NEW key, if you want to verify the refetch\n}\n```\n\nThis is **eventual, not fail-closed** — nothing rejects a document encrypted to a stale key, so a\nwindow remains between the rotation and your next drain. Drain often if that window matters.\n\n### `service_key_rotated` — the same thing, the other way round\n\nThe `CustomerClient` also caches the **service's** public key, the one you encrypt your consent\nanswers and documents *to*, keyed `\"companyCode/serviceCode\"`. When that company replaces its\nservice keypair, the `service_key_rotated` change on your account feed is your only signal — you\nreceive no pushes. Same shape, same guarantees, same automatic handling on the pump:\n\n```ts\nif (change.event === 'service_key_rotated') {\n  // Automatic on the pump. Over a webhook, from the raw event body:\n  customer.invalidateServiceKey(body.company_share_code, body.service_share_code);\n  // body.service_public_key_sha256 = fingerprint of the service's NEW key\n}\n```\n\nAlso **eventual, not fail-closed**. Note the identifiers are **share codes**, not the ids used by\n`invalidatePublicKey` — the two caches are keyed differently and the wrong call removes nothing.\n\n### Webhook helpers (on the client)\n\nThe webhook receiver helpers are also exposed as `Client` methods (they delegate\nto the module functions, fully config-driven — no key/secret arguments):\n\n```ts\nclient.verifyWebhook(rawBody: Buffer | Uint8Array | string, headers): boolean\nawait client.parseWebhook(rawBody, headers):  Change\nawait client.handleWebhook(rawBody, headers): Change   // verify + parse\n```\n\n* `verifyWebhook` — recomputes `HMAC-SHA256(rawBody, secret)` and constant-time-compares it to `X-Allus-Signature`. Returns `true`/`false`; **never throws** for a bad signature.\n* `parseWebhook` — body → a typed `Change`. Does **not** verify. Handles JSON, XML, and the `encrypt_payload` account-key envelope. Throws `WebhookError` on a malformed/unparseable body.\n* `handleWebhook` — verify **then** parse; throws `WebhookError` on a bad/unknown signature, otherwise returns the `Change`. The typical one-liner inside a route.\n\n> `parseWebhook`/`handleWebhook` are **async**: a webhook body is a payload like any\n> other, so a slug it names that the held catalog does not carry triggers one bounded\n> catalog refetch and, through it, one registry refetch — before the value is typed.\n> Awaiting is what makes that heal possible here. `verifyWebhook` stays synchronous\n> and makes no network call at all.\n\nThe same three are importable as standalone functions\n(`import { verifyWebhook, parseWebhook, handleWebhook } from '@allus-fyi/company-data'`),\nwhich take the `config` and the decrypt/type closures explicitly — but inside an\napp you'll almost always use the client methods. See [Webhooks](#webhooks).\n\n---\n\n## The typed value model\n\nYou work with these objects and nothing else (`import { … } from '@allus-fyi/company-data'`):\n\n```text\nRequestField { slug, label, type, oneTime, mandatory, verified, verifiedMaxAgeDays }\nConnection   { id, personId, displayName, connectedAt, values: {<slug>: Value} }\nValue        { value, live, updatedAt, verified, verifiedAt, verifiedExpiresAt,\n               verifiedMethod, verifiedProvider, verificationId }\nChange       { id, event, personId, slug?, value?, live?, at }\nLogEntry     { type, message, metadata, at }\n```\n\n### Keyed by *your* slug\n\n`conn.values['work_email'].value` → `\"alice@acme.com\"`. The key is the stable,\nexplicit slug you set per request field in the portal — rename the label freely,\nthe slug is the contract. **The person's source field is never exposed**: no\nsource slug, no `field_id`, not even via `.raw`.\n\n### `Value { value, live, updatedAt, verified, verifiedAt, verifiedExpiresAt, verifiedMethod, verifiedProvider, verificationId }`\n\n| Property | Meaning |\n|----------|---------|\n| `value` | The typed plaintext (see the table below). |\n| `live` | `true` if the person chose \"keep connected\" (auto-updates); `false` for a one-time snapshot. |\n| `updatedAt` | `Date` of when this answer last changed (per-answer, rides on the `Value`), or `null`. |\n| `verified` | `true` only when the verification hash recomputes over the decrypted plaintext **and** the verification has not lapsed. Absent metadata reads `false`, which means \"not attested\", not \"wrong\". |\n| `verifiedAt` | `Date` the answering field was verified, or `null`. A stamp, not a promise about today. |\n| `verifiedExpiresAt` | `Date` that verification lapses, or `null` when it does not. A document-backed verification dies with the document; once this is past, `verified` reads `false`. |\n| `verifiedMethod` | HOW allme bound the value: `email_code` \\| `sms_code` \\| `sumsub_id` \\| `sumsub_address`. |\n| `verifiedProvider` | WHO established the proof: `allme` \\| `sumsub`. |\n| `verificationId` | The proof id to quote back to allme in a dispute — it resolves the full record, including facts you never receive. |\n\nThe last three are the **proof metadata** and arrive **together or not at all**: a value bound before\nthe proof log existed carries the four verification keys and none of these, so all three read `null`.\nThey are readable whatever the verified boolean says — that boolean stays the only trust decision.\n\n### Value types — from the type's RESOLVED definition\n\nA contact-field TYPE is a ROW in the served field-type registry, not a name this SDK knows by\nheart. The client fetches that registry (`GET /api/contact-field-types`) beside your request-field\ncatalog and holds it for its life; a value's shape follows the type's resolved storage LANE and\nPRIMITIVE, so a type added as a row types itself with no SDK release.\n\n| The type's resolved… | JS `value` |\n|----------------------|------------|\n| storage lane `photo` / `document` | a lazy `BinaryHandle` — see below |\n| primitive `composite` | a parsed `object` — the decrypted plaintext is a JSON object, parsed for you |\n| primitive `date` | a `Date` (UTC midnight; falls back to the raw string if it can't be parsed) |\n| primitive `multilist` | an array of the chosen option strings |\n| anything else, and a type the registry does not carry | `string` |\n| unanswered / no value | `null` |\n\nFor the seeded types that means, unchanged: `email`/`phone`/`url`/`text` → `string` (`phone` is a\nsingle E.164-style string, `+` and digits); `country`/`nationality` → an ISO 3166-1 alpha-2 code\n(e.g. `'US'`, `'NL'`), not a display name; `address`/`bank`/`creditcard` → a parsed `object`;\n`date`/`date_of_birth` → a `Date`; `photo`, `document`, `legal_document` and the ID-document\nsubtypes `passport`, `photo_id`, `drivers_license` → a lazy `BinaryHandle`.\n\n```ts\nconst addr = conn.values['home_address'].value as Record<string, unknown>; // {street, city, …}\nconst dob  = conn.values['birthday'].value as Date;                          // Date(1990-05-17)\n```\n\nAn `address`'s `country`/`state` sub-fields are an ISO alpha-2 code / USPS 2-letter state code\nrespectively. `(await client.fieldTypes()).isFieldValueValid(type, value)` validates a plaintext\nagainst its type; `isValidCountryCode(code)` / `dialCodeFor(code)` check a code or look up its\nE.164 dial code.\n\nReach the registry itself as `await client.fieldTypes()` for `resolve()`, `accepts(requested, actual)`, `descendants()`, `isBinary()`, `labelFor()`, `ordered()` and `validate(type, value)`\n(`null` when valid, else the name of the first failing rule). A request row of a PARENT type MAY be answered by a field of any DESCENDANT — a `legal_document`\nslot can be answered with a passport — but that matching is the API's and stays there. The answer\nreaches you keyed by YOUR slug and typed by the SLOT's own type: the person's source field is\nnever exposed, so there is no source slug, no `field_id` and no source type to resolve, not even\nvia `.raw`. For a binary slot the API resolves slot → source → file itself, so the bytes you fetch\nare the answering field's whatever type that field carries.\n\n**A CHOICE type's options.** `select` and `multiselect` carry no options of their own — a flow\nelement supplies them — so `validate` / `isFieldValueValid` / `fieldValueError` take an optional\nthird argument, the caller's own option list. The ROW's resolved options govern whenever it has\nany, else the supplied list, and never a merge of the two; `(await client.fieldTypes()).optionsFor(type, options)` answers exactly the domain the\nvalidator will enforce, or `null` when neither source has one. A choice with no domain at all is\nrefused (rule name `options_unavailable`) rather than measured against an empty list.\n`submitFlowAnswers` passes the flow element's options for you.\n\n\n### Binary fields — the lazy `BinaryHandle`\n\nA photo/document value is a `BinaryHandle`. Nothing is fetched or decrypted until\nyou call `.bytes()` or `.save()`:\n\n```ts\nconst handle = conn.values['passport_scan'].value as BinaryHandle;  // no network yet\n\nconst data = await handle.bytes();                  // GET the slot file → the file bytes\nconst n    = await handle.save('/tmp/passport.jpg'); // same, written to disk; returns bytes written\nconsole.log(handle.valueUrl);                         // the opaque slot-keyed URL it fetches from\nconsole.log(handle.contentType, handle.contentSha256); // what arrived, and its digest\n```\n\n**The endpoint has two 200 shapes, and which one you get is the person's choice, not\nyours** — it depends on whether their source field is private, they can change it at\nany time, and nothing announces it in advance:\n\n* **private source** → `application/json` `{\"encrypted\": true, \"value\": <wrapper>}`. The\n  wrapper is decrypted with your service key into a JSON file-envelope\n  (`{\"full\": \"data:…\"}` for photos, `{\"file\": \"data:…\"}` for documents) whose data URI\n  base64-decodes to the file.\n* **plaintext source** → the file's own `Content-Type` (`image/jpeg`, `application/pdf`, …)\n  and the body already IS the file. Nothing is decrypted, and no service key is needed.\n\n`.bytes()`/`.save()` hide the difference and give you the file bytes either way; the\nhandle tells the two apart on the response `Content-Type` and never by sniffing the\nbody. There is no variant selection — one slot has one byte sequence and therefore one\ndigest. Every 200 carries `X-Allus-Content-Sha256`, the sha256 of exactly the bytes\nreturned, exposed as `handle.contentSha256` (and `handle.contentType`) after the first\nfetch, so you can record what you received and later show your archived copy has not\ndrifted. The result is cached on the handle, so repeated calls don't re-fetch.\n`.save()` is crash-safe (temp file → fsync → atomic rename).\n\nA frozen (share-once) answer is retained for 90 days. After that the endpoint answers\n**410** with `error_key: company_data.file_expired`, surfacing as an `ApiError` whose\n`details` carry the `content_sha256` the file had and its `expired_at` — so your copy\nis now the only one, and you can still prove what it is. The values map may also carry\n`content_sha256` / `expired` / `expired_at` on a binary slot ahead of the fetch; those\nride verbatim on `Value.raw` (and on `Change.raw` for a `field_deleted` event).\n\n### `Change { id, event, personId, slug?, value?, live?, at }`\n\nA change-feed / webhook event.\n\n| Property | Meaning |\n|----------|---------|\n| `id` | **The stable server change-row id — your dedup key** (captured before the server delete). |\n| `event` | `connection_created`, `connection_deleted`, `field_updated`, `field_deleted`, `consent_accepted`, `consent_declined`, `document_status_changed`, `message_received`. |\n| `personId` | The person the change is about (may be `null`). |\n| `slug`, `value`, `live` | Present only on `field_updated`; `value` is typed exactly like `Value.value` (incl. a lazy `BinaryHandle` for binaries). Connection/consent/document events carry no slot/value. |\n| `documentId`, `status` | Present only on `document_status_changed` — the affected document's id and its new lifecycle status. `null` on every other event. |\n| `connectionId`, `messageId`, `personPublicKey`, `messageBody` | Present only on `message_received` — a person messaged your service. `messageBody` is the **decrypted** text. See [Messaging](#messaging). |\n| `verified`, `verifiedAt`, `verifiedExpiresAt` | Present on `field_updated`, with the same meaning as on `Value`. |\n| `verifiedMethod`, `verifiedProvider`, `verificationId` | The proof metadata, same meaning and same all-or-none rule as on `Value`. |\n| `at` | `Date` of the change. (There is no separate `updatedAt` on a change.) |\n\n### `.raw`\n\nEvery model carries `.raw` — the underlying *hardened* API object — for debugging\nor an edge case the SDK didn't model. It still never contains the person's source\nfield.\n\nSee [`docs/model.md`](docs/model.md) for the full reference.\n\n---\n\n## The changes pump\n\nThe changes feed is a server-side **drain-on-fetch queue**:\n`GET /api/company-data/changes?limit=N` returns up to N events (default 100, max\n500) **and deletes exactly those rows in the same transaction** — no\noffset/cursor, and the API keeps no copy afterward. So consumption can't be a\nplain list: a consumer crash mid-batch would lose events the API already deleted,\nand a huge backlog must not materialize in memory. `processChanges` solves both.\n\n**Per run, repeating until the feed is empty then resolving:**\n\n1. **Replay first.** Deliver any un-acked events already in the local buffer (from a previous crashed run), oldest-first.\n2. **Drain.** When the buffer is empty, fetch one batch and **persist it to the durable file buffer (fsync) BEFORE handing anything out.** This is the backup the API no longer has.\n3. **Deliver one-by-one.** For each buffered event, oldest-first: decrypt its value *at delivery* (never on disk), build the typed `Change`, call `handler`.\n4. **Ack / retry / dead-letter.** On success, remove the event from the buffer (ack). On a handler error, retry with backoff up to `maxRetries`; then either move it to the dead-letter store and continue (`onError=\"deadletter\"`, default — one poison event never wedges the stream) or stop and re-throw (`onError=\"halt\"`). A `DecryptError` on a buffered event (corrupt/truncated ciphertext, rotated key) is **dead-lettered immediately** — re-decrypting can't fix it, so it does *not* burn retries (under `onError=\"halt\"` it re-throws). Either way it never propagates out and wedges replay.\n5. Repeat until a drain returns empty **and** the buffer is drained → resolve.\n\n### The durable buffer\n\n* Plain files under `cacheDir` (zero extra dependencies): `pending/` for un-acked events, `deadletter/` for ones that exhausted retries.\n* Stored events keep their **ciphertext** value — **no plaintext PII is ever written to disk**. Decryption happens only at delivery.\n* Writes are crash-safe (temp file → `fsyncSync` → atomic rename → dir fsync). Files are named with a monotonic, zero-padded sequence so they replay oldest-first.\n\n### Crash safety, at-least-once, and idempotency\n\nA batch is durably buffered *before* any delivery, and acked per-item only *after*\nthe handler succeeds. The ack can't be atomic with your side-effects — a crash\nbetween your handler's success and its ack re-delivers that event on the next run.\nThat makes delivery **at-least-once**, so:\n\n> **Your handler must be idempotent. Dedup on `Change.id`.**\n\n`Change.id` is the stable server change-row id, captured before the server delete,\nso it survives crash + replay unchanged.\n\n### No follow mode\n\n`processChanges` resolves when the feed empties. **You** schedule re-runs — a cron\njob, a `while (true) { await client.processChanges(handle); await sleep(5000); }`\nloop, a worker queue, whatever fits. The feed is cheap to poll (see\n[Rate limits](#rate-limits)).\n\n### Worked example\n\n```ts\nimport { Client } from '@allus-fyi/company-data';\n\nconst client = Client.fromConfig('allus.json');\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\nasync function handle(change) {\n  if (await seen(change.id)) return;            // idempotent: skip what we've applied\n  switch (change.event) {\n    case 'field_updated':\n      await storeValue(change.personId, change.slug, change.value, change.live);\n      break;\n    case 'field_deleted':\n      await clearValue(change.personId, change.slug);\n      break;\n    case 'connection_deleted':\n      await dropPerson(change.personId);\n      break;\n    case 'connection_created':\n    case 'consent_accepted':\n    case 'consent_declined':\n      await noteEvent(change.personId, change.event, change.at);\n      break;\n  }\n  await recordSeen(change.id);\n}\n\n// Schedule your own re-runs; processChanges itself resolves when empty.\nfor (;;) {\n  await client.processChanges(handle, { batchSize: 200, maxRetries: 5 });\n  await sleep(5000);\n}\n```\n\nIf a handler keeps failing, the event lands in the dead-letter store instead of\nblocking the stream; inspect with `client.deadLetters()` and re-drive with\n`client.retryDeadLetters(handle)` after fixing the cause. See\n[`docs/pump.md`](docs/pump.md).\n\n---\n\n## Webhooks\n\nWebhooks are the lower-latency push alternative to polling the changes feed. The\nplatform POSTs each change event to your configured webhook URL with:\n\n* `X-Allus-Webhook-Id` — which webhook this is (selects the HMAC secret from config).\n* `X-Allus-Signature` — `HMAC-SHA256(rawBody, secret)` as lowercase hex.\n* the body — the same slug-keyed `Change` shape as the pull feed (JSON or XML).\n\nAll secrets/keys come from config; the helpers take **no key or secret\narguments**. Use the raw request body **bytes** (`Buffer`) — do not re-serialize a\nparsed body, the HMAC is over the exact bytes the platform sent.\n\n### Delivery contract — effectively unique, rarely replayed\n\nEach queued event is POSTed **once**, and **only HTTP `200` counts as delivered** — a\n`202`, a `204`, a 3xx redirect and every 4xx/5xx are all treated as a failure. On anything\nother than `200` (or a timeout or connection error) the event is **not retried in place**:\nit and the rest of the webhook's queue move to a durable server-side backlog and the\nwebhook is marked bad. The backlog is delivered later, either automatically when\nthe webhook next probes healthy, or when you drain it yourself with\n`GET /api/company-data/changes?webhook_id=…` (delete-on-read).\n\nSo deliveries are **effectively unique** — with one rare exception. If your endpoint\nprocessed an event but the platform never saw your `200` (your response timed out, or\nyou crashed after committing but before responding), the event is treated as failed\nand replayed on recovery, so you receive it **again**. Nothing caps that at two: a\nfailed probe leaves its backlog row in place, so every later recovery attempt whose\n`200` is likewise lost replays the same event once more. Inside that window the\ncontract is **at-least-once** — plan for one or more repeats, not for exactly one.\n\n> **Do not use `change.id` as an idempotency key here.** On the webhook path the id is\n> neither reliably stable nor reliably fresh, and a receiver cannot tell which one it is\n> holding. A **live** delivery is built with no change row behind it, so its id is minted\n> for that single POST — the later replay of the same event is rebuilt from a durable\n> backlog row and therefore carries a **different** id. But a replayed delivery carries\n> **that row's** id, and the row stays in place until it is delivered successfully, so a\n> re-attempted replay arrives with the **same** id — which changes again if the event is\n> re-backlogged after a further failure. An id check therefore misses the duplicate you\n> are most likely to see and matches only a rarer one; it is not a contract. If you need\n> strict idempotency, key on the **content** — event + person + slug/document + payload —\n> never on the id.\n\n**Webhooks and the pull feed are alternative integrations — consume one, never both.**\nThe id-dedup guidance in the changes-pump section above applies to the pump only, where\n`change.id` is the real server change-row id.\n\n\n### In a web route (Express)\n\n```ts\nimport express from 'express';\nimport { Client, WebhookError } from '@allus-fyi/company-data';\n\nconst app = express();\nconst client = Client.fromConfig('allus.json');\nawait client.requestFields();   // optional: warm the catalog so the first webhook doesn't pay for it\n\n// IMPORTANT: capture the RAW body bytes — do not let a JSON body-parser replace them.\napp.post('/allus/webhook', express.raw({ type: '*/*' }), async (req, res) => {\n  let change;\n  try {\n    change = await client.handleWebhook(req.body /* Buffer */, req.headers);\n  } catch (e) {\n    if (e instanceof WebhookError) return res.sendStatus(401); // bad/unknown signature\n    throw e;\n  }\n  // Do NOT carry the pump's id-dedup over here: the webhook id is not an idempotency\n  // key (see \"Delivery contract\" above). Key on content if you need one.\n  applyChange(change);\n  res.sendStatus(200);   // 200 — the ONLY status allus counts as delivered\n});\n```\n\n`verifyWebhook` / `parseWebhook` let you split the steps if you prefer:\n\n```ts\nif (!client.verifyWebhook(rawBody, headers)) return res.sendStatus(401);\nconst change = await client.parseWebhook(rawBody, headers);\n```\n\n### Config-driven secrets\n\nPer-webhook HMAC secrets live in the config `webhooks` map, keyed by webhook id;\nthe SDK reads `X-Allus-Webhook-Id` off the request and looks up the matching\nsecret. A single-webhook service can use the flat `\"webhook_secret\": \"…\"`\nshortcut (or `ALLUS_WEBHOOK_SECRET`). An unknown/unconfigured id ⇒ verification\nreturns `false` (and `handleWebhook` throws `WebhookError`).\n\n### The `encrypt_payload` account-key envelope\n\nIf a webhook has `encrypt_payload` enabled, the body is **replaced** by a\n`{\"_enc\":1,…}` envelope encrypted to your company **account** key (and the HMAC is\nover that envelope — the final bytes sent). `parseWebhook`/`handleWebhook` unwrap\nit transparently using the configured `account_private_key` + `account_passphrase`,\nthen decrypt the inner field value with the service key — so an encrypted-payload\n`Change` is identical to a plain one. If you receive such a webhook without an\n`account_private_key` configured, you get a `WebhookError`.\n\n> The account-key envelope uses OAEP-**SHA1** (OpenSSL's default), distinct from\n> the OAEP-SHA256 used for person field values — the SDK handles this difference\n> internally; you only supply the account key in config.\n\nSee [`docs/webhooks.md`](docs/webhooks.md).\n\n---\n\n## Company documents\n\nA service can attach **documents** to a connection — contracts, statements,\nreceipts, anything — either **broadcast** to every connection or aimed at **one\nperson**. A document carries a small JSON payload (`payloadKind:'json'`) or a file\nblob (`payloadKind:'file'`), a `kind`/`name`/`description`, a lifecycle `status`,\nand free-form `metadata`.\n\n### The one rule: the *target* decides encryption, not `isPrivate`\n\n* **Per-person** (you pass `connectionId`, `personUserId`, **or** `shareCode`) →\n  the value is **always end-to-end encrypted to the recipient's public key** before\n  it leaves the process — for **every** per-person document, `isPrivate` or not. The\n  server only ever stores ciphertext. No method takes a key or secret argument; the\n  SDK resolves the recipient's share code (from `connectionId`/`personUserId`) and\n  fetches the key for you (pass `shareCode` to skip that lookup).\n* **Broadcast** (no target) → the value is sent **plaintext** (you can't single-key\n  encrypt to all of a service's connections), so a broadcast **must** be non-private.\n\n`isPrivate` is therefore **device-display-only** — it tells the recipient's app to\n*lock* the document (tap-to-reveal) vs *decrypt-on-load*; it does **not** change\nwhether the value is encrypted. Because a plaintext broadcast can't be locked,\n`isPrivate: true` **with no target throws** `ConfigError`.\n\n### Create\n\n```ts\n// BROADCAST — plaintext json, visible to every connection (no target; non-private)\nconst notice = await client.createDocument({\n  kind: 'notice',\n  name: 'August price update',\n  payloadKind: 'json',\n  jsonValue: { effective: '2026-08-01', changePct: 4 },\n});\n\n// PER-PERSON — automatically encrypted to the recipient (target by connectionId,\n// personUserId, or shareCode — any one resolves the key)\nconst contract = await client.createDocument({\n  kind: 'contract',\n  name: 'Service agreement',\n  payloadKind: 'json',\n  connectionId: 'conn-uuid',          // or personUserId / shareCode\n  isPrivate: true,                    // device locks it; value is encrypted regardless\n  jsonValue: { plan: 'pro', signedBy: null },\n  status: 'ready_to_sign',\n  metadata: { ref: 'CT-2026-0042' },\n});\n\n// PER-PERSON FILE — bytes are encrypted to the recipient too\nconst receipt = await client.createDocument({\n  kind: 'receipt',\n  name: 'Invoice 0042.pdf',\n  payloadKind: 'file',\n  personUserId: 'person-uuid',\n  fileBytes: pdfBuffer,              // Buffer | Uint8Array\n  fileMime: 'application/pdf',\n});\n```\n\n`payloadKind` selects the body: `'json'` requires `jsonValue` (any\nJSON-serialisable object); `'file'` requires `fileBytes` (and an optional\n`fileMime`). For per-person json docs, read the plaintext back with `.json()` —\nit decrypts transparently with the SDK's own key; broadcast json is already\nplaintext.\n\n### List, fetch, update, delete\n\n```ts\nlistDocuments(opts?: { personUserId?; status?; limit?; offset? }): Promise<Document[]>\ndocument(documentId): Promise<Document>\ndocumentFile(documentId): Promise<Buffer>                       // #491: the file BYTES\nupdateDocumentStatus(documentId, status): Promise<Document>     // offering|ready_to_sign|active|active_but_ending|ended\nupdateDocumentMetadata(documentId, { metadata?, name?, description? }): Promise<Document>\ndeleteDocument(documentId): Promise<void>                       // also removes the on-disk file\n```\n\n```ts\nconst docs = await client.listDocuments({ personUserId: 'person-uuid', status: 'active' });\nconst doc = await client.document(contract.id);\nconst payload = doc.json();                          // decrypted plaintext (per-person) or as-is (broadcast)\n\nawait client.updateDocumentStatus(contract.id, 'active');\nawait client.updateDocumentMetadata(contract.id, { metadata: { ref: 'CT-2026-0042', signed: true } });\nawait client.deleteDocument(notice.id);\n```\n\nA `Document` is `{ id, kind, name, description, status, payloadKind, isPrivate,\nvalue, metadata, createdAt, updatedAt, raw }` with a `.json()` helper for json docs.\n\n* `listDocuments(opts)` filters optionally by `personUserId` and/or `status` and pages with `limit`/`offset`.\n* `document(id)` fetches one. Call `.json()` on a `'json'` document to get the plaintext (it transparently decrypts a per-person, encrypted document; a broadcast doc is already plaintext).\n* A contract-flow-generated document can also read `waiting` — a run-participant copy whose signer has not been reached yet in the run's ordered signing plan. It is read-only: `updateDocumentStatus` throws (`error_key: 'documents.run_managed'`, 409) if you try to write `status` on a run-participant document while it is `waiting`, `ready_to_sign` or `offering` — that status moves only through flow generation, the run's own advance, sign/accept, or a run cancel/decline. Such a document's `runSignatures` carries the run's ordered signature summary.\n* `documentFile(id)` (#491) downloads a `'file'` document's BYTES — the metadata methods don't include them. A **broadcast** (plaintext) document's bytes are returned as-is; a **per-person / private** document is encrypted to the *recipient's* key (not your service key), so `documentFile` fails clearly with `documents.recipient_encrypted` (`ApiError`) rather than a doomed decrypt. For a generated flow contract's own copy use `flowRunDocument(runId)` below (that copy IS service-key-encrypted).\n\n### Contract flows & identity (#491)\n\n```ts\nflowRunAnswers(run: FlowRun | string): Promise<Record<string, unknown>>  // gap 1 — a completed run's DECRYPTED answers {slug: plaintext}\nflowRunDocument(runId): Promise<Buffer>                                  // gap 2 — the company's own copy of a run's generated contract (plaintext bytes)\nidentity(): Promise<{ company_user_id: string; service_id: string }>     // gap 3 — this client's own identity\n```\n\n* `flowRunAnswers(run)` returns a completed run's decrypted `{slug: plaintext}` answers (accepts a fetched `FlowRun` or a run id). It is the public accessor for a finished run's answers, which `processFlowRun` returns untouched.\n* `flowRunDocument(runId)` downloads the company's own service-key-encrypted copy of a run's generated contract and returns the plaintext file bytes (a 404 `ApiError` until the run generates a document) — the honest completion step (fill → complete → `flowRunAnswers` → `flowRunDocument`).\n* `identity()` returns this client's `{ company_user_id, service_id }` from `GET /api/company-data/whoami`, so a `triggerFlowRun` binding's **company** party can bind to `company_user_id` (the person party's user_id comes from the connection).\n\n### Reacting to status changes in the pump\n\nWhen a recipient acts on a document (e.g. signs it), the feed emits a\n`document_status_changed` event. The `Change` carries `documentId` and the new\n`status` (other change events leave both `null`):\n\n```ts\nasync function handle(change) {\n  if (await alreadyProcessed(change.id)) return;     // idempotency\n  if (change.event === 'document_status_changed') {\n    await onDocumentStatus(change.documentId, change.status);   // e.g. 'active'\n  } else if (change.event === 'field_updated') {\n    await store(change.personId, change.slug, change.value);\n  }\n  await markProcessed(change.id);\n}\n\nawait client.processChanges(handle);\n```\n\nSee [`docs/model.md`](docs/model.md) for the full `Document` / `Change` reference.\n\n---\n\n## Messaging\n\nYour service can hold a **conversation** with a connected person — the same\nmessaging surface the person already uses, with your service as the counterpart.\nTwo shapes:\n\n* **1-on-1** — `sendMessage(connectionId, text)`. **End-to-end encrypted**: the SDK\n  encrypts one copy to the person's public key and one to your service key before\n  anything leaves the process, so the person reads it in their app and you can\n  re-read your own outbound text. The platform stores ciphertext only.\n* **Broadcast** — `broadcastMessage(text)`. One **plaintext** message to every person\n  connected to the service (one body cannot be single-key-encrypted to all of them,\n  exactly as for a broadcast document). It seeds each person's ordinary 1-on-1\n  thread; their reply comes back end-to-end encrypted.\n\n`sendMessage` answers **201** with the created message carrying `message_id`, and returns\nthat id — the value you hand back as the acknowledgement boundary.\n\nInbound messages arrive on the **changes pump / webhook** as a `message_received`\nevent — a person→company message only. A broadcast raises no event of its own.\n\n```ts\nasync function handle(change) {\n  if (change.event !== 'message_received') return;\n  console.log(change.personId, change.messageBody);   // already decrypted for you\n\n  // Reply on the same connection. personPublicKey rides the event, so no second\n  // key lookup is needed.\n  await client.sendMessage(change.connectionId, \"Thanks — we're on it.\", {\n    personPublicKey: change.personPublicKey,\n  });\n\n  // Acknowledge what you handled. REQUIRED: without it the message stays unread\n  // forever, your unread count grows, and the person never sees a read receipt.\n  // Sending a reply does NOT acknowledge anything.\n  await client.markMessagesRead(change.connectionId, change.messageId);\n}\n\nawait client.processChanges(handle);\n```\n\n`markMessagesRead` is bounded by the boundary message: a message that arrived while\nyou were working is **not** swept, and a repeat is a no-op. The boundary must be a\nmessage the person sent on that connection — anything else is refused with\n`ApiError('company_data.ack_boundary_invalid')` (400).\n\n```ts\n// One plaintext announcement to everyone connected to the service.\nawait client.broadcastMessage(\"We're closed on Friday.\");\n```\n\nRefusals surface as `ApiError` carrying the platform `error_key`:\n\n| `error_key` | Status | Meaning |\n|-------------|--------|---------|\n| `messages.messaging_not_entitled` | 403 | The company's plan does not include messaging. |\n| `messages.not_connected` | 403 | The person is not connected to this service. |\n| `messages.messaging_suspended` | 403 | Messaging is suspended for this service (or the whole company). |\n| `messages.broadcast_suspended` | 403 | Broadcast alone is suspended for this service. |\n| `messages.encryption_required` | 400 | A 1-on-1 body was not a valid encrypted wrapper. |\n| `messages.broadcast_audience_too_large` | 422 | The service has more connections than a broadcast allows. |\n| `messages.rate_limited` | 429 | Too many 1-on-1 messages to the same person. |\n| `company_data.ack_boundary_invalid` | 400 | The ack boundary is not a message the person sent on that connection. |\n\n---\n\n## Rate limits\n\n| Endpoint | Limit | Use it for |\n|----------|-------|-----------|\n| `changes` (the pump) | **generous** | Poll **as often as you like** — it's a cheap drain-on-fetch queue. |\n| `request-fields`, `logs` | moderate | Occasional reads. |\n| `connections`, `connection(id)`, binary `/file` | **heavily limited** | Initial full sync + occasional reconciliation **only** — never as a poll substitute. |\n\nA 429 carries `Retry-After`. The SDK backs off and retries automatically:\n\n* The transport (`HttpClient`) retries a 429 a bounded number of times honoring `Retry-After`, then surfaces `RateLimitError`.\n* The `connections(...)` generator additionally backs off per `Retry-After` on a surfaced `RateLimitError` and retries the page a bounded number of times before re-throwing — so it paces itself within the limit instead of hammering.\n\nIf you catch a `RateLimitError`, its `.retryAfter` is the seconds to wait (or\n`null` when the header was absent).\n\nYour `client_credentials` token requests (`/oauth2/token`) are on their own rate-limit bucket,\nseparate from person logins — but it is keyed by **source IP, not by your `client_id`**, so it is\nshared with every other `client_credentials` caller reaching the API from the same address (another\nservice on your network, a second client on the same host). Caching the token, as described under\n**How it's wired** below, is what keeps that shared window from being spent needlessly — by you or\nanyone else behind the same IP. Every rate-limit refusal — this 429, and the platform's 503 when its\nown limiter store is unreadable — now carries a populated `.errorKey`, readable off the same\n`RateLimitError`/`ApiError`.\n\n---\n\n## Errors\n\nAll from `@allus-fyi/company-data`. Same taxonomy + names across all six SDKs.\nEvery error extends `AllusError`, so `catch (e) { if (e instanceof AllusError) … }`\ncaptures the whole taxonomy.\n\n| Error | When |\n|-------|------|\n| `ConfigError` | Missing/invalid config, unreadable key file, or wrong passphrase — at construction (fail fast). |\n| `AuthError` | Token fetch/refresh failed (bad `client_id`/`secret`, revoked client); or a 401 survives the one automatic refresh-and-retry. |\n| `ApiError` | Any non-2xx from the API; carries `status`, `errorKey` (the platform `error_key`, when present), and `apiMessage`. |\n| `DecryptError` | A ciphertext wrapper is malformed, the key is wrong, or the GCM tag mismatches. Surfaces when a value is accessed/decrypted. |\n| `WebhookError` | Signature verification failed, or an envelope couldn't be unwrapped/parsed. |\n| `RateLimitError` | A 429 from a rate-limited endpoint. Subclass of `ApiError` (status fixed at 429); carries `retryAfter` (seconds, or `null`). |\n\n```ts\nimport {\n  Client, AllusError, ConfigError, AuthError, ApiError,\n  DecryptError, WebhookError, RateLimitError,\n} from '@allus-fyi/company-data';\n\ntry {\n  const client = Client.fromConfig('allus.json');\n  for await (const conn of client.connections()) { /* … */ }\n} catch (e) {\n  if (e instanceof ConfigError) { /* fix the config / key file */ }\n  else if (e instanceof RateLimitError) { await wait((e.retryAfter ?? 60) * 1000); }\n  else if (e instanceof ApiError) { log(e.status, e.errorKey, e.apiMessage); }\n  else throw e;\n}\n```\n\nSee [`docs/errors.md`](docs/errors.md).\n\n---\n\n## How it's wired\n\nEverything below is what the SDK hides so your code only ever sees conclusions.\n\n**Auth / token.** An `HttpClient` owns a `client_credentials`-only token. On the\nfirst call (or when the cached token nears expiry) it POSTs\n`client_id`/`client_secret` to `{api_url}/oauth2/token` and caches the bearer\ntoken + its expiry; refresh is automatic. A mid-flight 401 triggers exactly one\nrefresh-and-retry, then `AuthError`. The token is scoped server-side to **one**\nservice, so every call is implicitly that service's data. The transport is over\nNode's global `fetch` by default, but injectable (`HttpTransport`) for tests.\n\n**Regions.** The configured `api_url` is the platform's global front door and also the\nstarting point for every request, including the token request. A `client_credentials`\ntoken is minted at your company's **home region**, and the token response names that\nregion's base in an `api_url` member — the SDK stores it and sends every subsequent\nrequest there, token requests included, because the token is valid only at that region\nand a company that moves region is followed by the next mint. A data call that still\nreaches the front door is refused with `421` + `error_key: region.rebase_required`,\ncarrying the same `api_url`; the SDK stores it and retries the call exactly once. The SDK\ndoes not validate a server-returned `api_url` against anything — it stores the base the\nserver names and uses it. An absent or empty `api_url` is never stored and the response\nsurfaces as the error it is.\n\n**Slug resolution.** `requestFields()` is fetched once and cached; its slug→type\nmap types every value (so `address` parses to an object, `photo` becomes a lazy\nbinary handle, etc.). A value or a change naming a slug that map does not carry —\na request slot configured after the client started — refetches the catalog ONCE,\nand a slug still absent afterwards is remembered and never asked for again. The\nconnection/changes endpoints return values keyed by **your** request slug — the\nperson's source field is dropped server-side and never reaches the SDK.\n\n**Decryption (zero-knowledge).** The service private key is loaded **once** at\nconstruction from the configured encrypted PEM + passphrase\n(`crypto.createPrivateKey({ key, passphrase })` — PBES2 handled by OpenSSL). A\n`decryptValue` closure over it is handed to every model factory and the pump — the\nkey never appears in a method signature. Each value is a hybrid wrapper\n(`{\"_enc\":1,\"k\":rsa_oaep_sha256(aesKey),\"iv\":…,\"d\":aes256gcm(…)}`); the SDK\nRSA-OAEP-SHA256 unwraps the AES key (`privateDecrypt({ …, oaepHash: 'sha256' })` —\nNode defaults to SHA-1, so the SHA-256 pin is essential), then AES-256-GCM decrypts\nthe payload (the 16-byte tag is the last 16 bytes of `d`). **The platform only ever\nholds ciphertext — it never sees your plaintext.**\n\n**Binary fetch.** A binary value is a lazy `BinaryHandle` over a slot-keyed\n`value_url`. On `.bytes()`/`.save()` it GETs that file endpoint and returns the file\nbytes. The endpoint answers in one of two shapes depending on whether the person's\nsource field is private: a `{\"encrypted\":true,\"value\":<wrapper>}` JSON envelope that\nthe same service-key decrypt turns into a JSON file-envelope whose data URI\nbase64-decodes to the file, or — for a plaintext source — the file's own\n`Content-Type` and the bytes themselves. The SDK classifies on `Content-Type` alone\nand never sniffs the body: mistaking a wrapper for file bytes would silently write\nciphertext to disk as if it were the document, while mistaking bytes for a wrapper\nfails loudly at the parse, so an absent `Content-Type` is treated as the JSON shape.\n(Slot-keyed, never source-field-keyed.)\n\n**XML, safely.** When `format: \"xml\"`, responses (and webhook bodies) are parsed by\na small, **XXE-safe** hand-written parser: no DOCTYPE/DTD processing, no custom or\nexternal entities — those vectors are simply absent, and a DOCTYPE / unknown entity\nis rejected. HMAC verification is always over the raw bytes, never the parsed tree.\n\n**The drain-on-fetch feed.** `processChanges` delegates to a `Pump` wired to a\n`fetchChanges` closure (`GET /changes?limit=`, returning raw ciphertext events) and\na `decrypt` closure (builds a typed `Change`). Because the fetch deletes the rows it\nreturns, the pump persists each batch to the durable file buffer (ciphertext at\nrest) before delivery, acks per-item after your handler succeeds, and replays the\nbuffer on restart — see [The changes pump](#the-changes-pump).\n\n---\n\n## Status\n\n**Crypto parity gate:** the decryption core is verified against the shared\ncross-language decryption vector — PEM-load (PBES2 / PBKDF2-SHA256 / AES-256-CBC,\n100k iters), text decrypt, and the binary decrypt → envelope → inner-bytes hash —\nplus an independent OpenSSL cross-check (anti-circularity). The full test suite\n(config, crypto, http/auth, models, the crash-safe pump, webhooks, and the XXE-safe\nXML parser) is green under `npm test`.\n\n## Sign in with allme (OAuth, #195)\n\n```ts\nimport { OAuthClient } from '@allus-fyi/company-data';\n\nconst oauth = OAuthClient.fromConfig('idw-config.json');\nconst url = oauth.authorizeUrl('signin', { state, codeChallenge });   // the button target\n// ...user approves; your redirect receives ?code=...\nconst { user, mode, values, values_cipher, attestations } = await oauth.completeSignIn(code, verifier);\n```\n\nModes: `signin` | `one_time` (claim values decrypted for you) | `connect` |\n`2fa_enroll` (opt a person into 2FA — see below). `pollResult(state)` drives the detached mode.\n\n**#498 — a claim IS a request field.** You describe what you need and the **person** picks which of their\nown fields answers it; you never name a field. A claim carries a mandatory unique `name` (everything that\ncomes back is keyed by it — `values`, `values_cipher`, `attestations`, and their stored choice for a repeat\nlogin), a field `type`, an optional suggested slug, `required`, and `verified` (\"only a #311-verified answer\nwill do\"). A nameless or duplicate claim raises a config error at the call rather than failing at the API.\n`verified` is accepted only on the OIDC flow and only for a type allme can verify (today `email`); elsewhere\nit is refused with `invalid_request` rather than quietly dropped.\n\n`verifiedMaxAgeDays` narrows a `verified` claim to a RECENT verification, and the merge is **tighten-only**: the app's\nregistered configuration is a FLOOR, a request may only tighten it, and the effective limit is the minimum\nof the two stated ages. An omitted age tightens nothing — omitting it sends nothing at all, never an\nexplicit null — and a value below 1 raises `ConfigError` at the call.\n\nThe sign-in result carries `values`, `values_cipher` **and** `attestations`.\n* `sub` **is** the person's share code and equals `share_code` — byte-identical to the id_token's `sub`.\n  `display_name` is gone: ask for a `name` claim and read the value under that key.\n* `values_cipher` is an additive sibling of `values`, keyed the same way: the raw app-key ciphertext\n  wrapper each plaintext value was decrypted from, exactly as `userinfo` delivered it. Lets you show that a\n  value really came from encrypted delivery rather than trusting it verbatim. Empty for a mode/claim that\n  carries no ciphertext (`signin`, or `plaintext` delivery) — that emptiness is the honest answer.\n* `attestations` is an additive sibling map keyed by the same claim name, present only for a `verified`\n  claim under encrypted delivery. Each entry carries a `verified` boolean **the SDK computes itself**, in\n  constant time, over the plaintext it just decrypted — plus the raw hash/salt/verifiedAt/verifiedExpiresAt,\n  and the proof metadata `verifiedMethod`/`verifiedProvider`/`verificationId` read from the opened seal\n  (HOW the value was bound, by WHOM, and the id to quote back to allme in a dispute — all three together\n  or not at all; a seal built before the proof log existed carries none of them and every one reads `null`).\n  **A slug ABSENT from the map is \"not attested\", never \"wrong\"** (treat that value as unverified);\n  **an entry present with `verified` false is a MISMATCH and you must reject the value.** `verifiedAt`\n  attests the value as verified *at that moment*, not verified today; `verifiedExpiresAt` is when that\n  verification lapses on its own (`null` = it does not), and an **expired attestation is unverified** —\n  the computed `verified` already reads false once it has passed.\n\n**`resolveUserinfo(accessToken, fallbackMode?)`** is the second half of `completeSignIn` — the `userinfo`\nread + decrypt + attest, without the token exchange — for a caller whose exchange already ran through a\ndifferent client (a standards-only third-party OIDC library, say, that verified the id_token itself but\ncannot read a claim value the id_token never carries). Config-only key handling applies exactly as it does\nto `completeSignIn`: you pass no key or passphrase, only the access token you already hold. Returns the\nidentical shape (`values`, `values_cipher`, `attestations`) and carries the same mismatch-rejection duty on\nthe caller. `completeSignIn` is implemented on top of this method. `fallbackMode` is used only when\n`userinfo` itself omits `mode` — pass the mode your own token response carried, or omit it if you have\nnone.\n\n\n## 2FA by allme (#436, #481)\n\nAsk a connected person to approve a login inside the allme app. On the same service data client (no new\nconfig), via the `twoFactor` sub-client:\n\n```ts\nimport { Client } from '@allus-fyi/company-data';\n\nconst client = Client.fromConfig('allus.json');\n\n// Raise a challenge. `idempotencyKey` is REQUIRED — a repeat with the same key within the TTL returns the\n// SAME challenge and sends no second push. `context` is plain text shown on the person's card.\nconst ch = await client.twoFactor.challenge('2I6UF3', {\n  idempotencyKey: 'login-8f3c1a',\n  context: 'Sign-in from Chrome',\n});\nif (ch.matchingDigits) {                       // number matching is on for this service\n  showOnLoginPage(ch.matchingDigits);          // the person types these back into the app; the server checks them\n}\n\n// Wait for the terminal outcome — polls result() for you (defaults: 600s timeout, 2s interval),\n// rejects with ApiError on timeout.\nconst res = await client.twoFactor.waitForResult(ch.challengeId);   // or result(ch.challengeId) to poll once yourself\nif (res.status === 'approved') grantLogin();\n```\n\n- **Burn-on-read.** The first read of a terminal state (`approved` | `denied` | `expired` | `revoked`)\n  delivers it and burns it — a later read is `gone`. Read it once and persist your own outcome;\n  `waitForResult` returns that first terminal read and never re-reads a consumed challenge.\n- **Webhook variant.** The `2fa_challenge_completed` change/webhook carries the same terminal `status`, so a\n  webhook consumer need not poll. **Expiry fires no webhook/Change** — only `approved`/`denied`/`revoked`\n  reach the feed, so a lapsed challenge is observable only by polling.\n- **Enrollment.** Only an enrolled person can be challenged (an un-enrolled `share_code` is `404`).\n  Enrollment is a one-time consent on the `web.allme.fyi/auth` surface via the OAuth helper's `2fa_enroll`\n  mode — a redirect button (`oauth.authorizeUrl('2fa_enroll', { state })`), or server-to-server with\n  `{ responseMode: 'detached' }` + `pollResult(state)`, which returns `{ enrolled: true, state }` once the\n  person confirms.\n- **Errors.** `404` (unknown / not-enrolled share code). A `429` is either the plain rate limit (retried with\n  backoff → `RateLimitError`) or `twofa.pending_cap` (too many challenges already open for this person) — the\n  latter surfaces immediately as `ApiError` and is never retried, since a retry cannot clear it.\n","readmeFilename":"README.md"}