{"_id":"featurebase-js","_rev":"13-1569e64af79469f062f8bd2382ba4c7e","name":"featurebase-js","dist-tags":{"next":"0.3.0-beta.0","beta":"0.3.0-beta.1","latest":"1.0.3"},"versions":{"0.1.0-beta.1":{"name":"featurebase-js","version":"0.1.0-beta.1","keywords":["featurebase","messenger","support","in-app-chat","help-center","changelog"],"author":{"name":"Featurebase","email":"hello@featurebase.app"},"license":"MIT","_id":"featurebase-js@0.1.0-beta.1","maintainers":[{"name":"brunofeaturebase","email":"bruno@featurebase.app"}],"homepage":"https://featurebase.app","bugs":{"url":"https://featurebase.app/contact"},"dist":{"shasum":"d138c249051eb0f6657a6c829969977e5bf52149","tarball":"https://registry.npmjs.org/featurebase-js/-/featurebase-js-0.1.0-beta.1.tgz","fileCount":11,"integrity":"sha512-bkKVtCk+vgOV8M4fhEZh2NZY4BEZD8gobhGMnxO9+eSvrEyWmUHqsW/ciaz0ul/czADEN37ABNwhjDVsfqyLuQ==","signatures":[{"sig":"MEYCIQDewL61N+dC7zdbQbbxuWT/PtixfBXliff/t6+dxMzSFwIhAJVA5RRS1ySqr9P9M3IdPBR+/2Mek/7d14qWh22y7FgJ","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":62249},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./react":{"types":"./dist/react.d.ts","import":"./dist/react.js","require":"./dist/react.cjs"},"./package.json":"./package.json"},"gitHead":"b30f0096ae9b14f428748011cc21dd1189c8b18e","private":false,"scripts":{"test":"jest --config jest.config.cjs","build":"tsup","check":"node scripts/check-publish-surface.cjs","typecheck":"tsc --noEmit -p tsconfig.json","prepublishOnly":"npm run build && npm run typecheck && npm run test && npm run check"},"_npmUser":{"name":"brunofeaturebase","email":"bruno@featurebase.app"},"repository":{"url":"git+https://github.com/FeaturebaseHQ/sdk.git","type":"git","directory":"client"},"_npmVersion":"10.8.2","description":"Typed client SDK for the Featurebase Messenger.","directories":{},"sideEffects":false,"_nodeVersion":"20.20.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","tsup":"^8.3.5","react":"^19.0.0","ts-jest":"^29.2.5","react-dom":"^19.0.0","typescript":"^5.7.2","@types/jest":"^29.5.14","@types/node":"^22.10.0","@types/react":"^19.0.2","@types/react-dom":"^19.0.2","@testing-library/react":"^16.1.0","jest-environment-jsdom":"^29.7.0"},"peerDependencies":{"react":">=17"},"peerDependenciesMeta":{"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/featurebase-js_0.1.0-beta.1_1777503267467_0.47536964191327624","host":"s3://npm-registry-packages-npm-production"}},"0.2.0-beta.1":{"name":"featurebase-js","version":"0.2.0-beta.1","keywords":["featurebase","messenger","support","in-app-chat","help-center","changelog","feedback-widget","survey","survey-widget"],"author":{"name":"Featurebase","email":"hello@featurebase.app"},"license":"MIT","_id":"featurebase-js@0.2.0-beta.1","maintainers":[{"name":"brunofeaturebase","email":"bruno@featurebase.app"}],"homepage":"https://featurebase.app","bugs":{"url":"https://featurebase.app/contact"},"dist":{"shasum":"5b3a264523fca28d22d64ce58f5bc791a8433bac","tarball":"https://registry.npmjs.org/featurebase-js/-/featurebase-js-0.2.0-beta.1.tgz","fileCount":11,"integrity":"sha512-I9bktVv51nwDMNR2CFzqxC4f2lqRuS1DZCHWPaJhgKwhjSO9PnpOu1yJ4i0RNiszGQu9PRvThSM0oEBhZPMahw==","signatures":[{"sig":"MEQCIEOZ0kkEC0WmEadiFkVs5PlAtLL2qKXUMGN82ZnNCNQiAiB2A5+aR0Obt/lH6W1dzBnT3mXVxD8DqwyCZ/SZ4ubySw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":90706},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./react":{"types":"./dist/react.d.ts","import":"./dist/react.js","require":"./dist/react.cjs"},"./package.json":"./package.json"},"gitHead":"b30f0096ae9b14f428748011cc21dd1189c8b18e","private":false,"scripts":{"test":"jest --config jest.config.cjs","build":"tsup","check":"node scripts/check-publish-surface.cjs","typecheck":"tsc --noEmit -p tsconfig.json","prepublishOnly":"npm run build && npm run typecheck && npm run test && npm run check"},"_npmUser":{"name":"brunofeaturebase","email":"bruno@featurebase.app"},"repository":{"url":"git+https://github.com/FeaturebaseHQ/sdk.git","type":"git","directory":"client"},"_npmVersion":"10.8.2","description":"Typed client SDK for Featurebase — messenger, changelog widget, feedback widget, and surveys.","directories":{},"sideEffects":false,"_nodeVersion":"20.20.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"jest":"^29.7.0","tsup":"^8.3.5","react":"^19.0.0","ts-jest":"^29.2.5","react-dom":"^19.0.0","typescript":"^5.7.2","@types/jest":"^29.5.14","@types/node":"^22.10.0","@types/react":"^19.0.2","@types/react-dom":"^19.0.2","@testing-library/react":"^16.1.0","jest-environment-jsdom":"^29.7.0"},"peerDependencies":{"react":">=17"},"peerDependenciesMeta":{"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/featurebase-js_0.2.0-beta.1_1777557634932_0.7045819883812015","host":"s3://npm-registry-packages-npm-production"}},"0.2.0-beta.3":{"name":"featurebase-js","version":"0.2.0-beta.3","keywords":["featurebase","messenger","support","in-app-chat","help-center","changelog","feedback-widget","survey","survey-widget"],"author":{"name":"Featurebase","email":"hello@featurebase.app"},"license":"MIT","_id":"featurebase-js@0.2.0-beta.3","maintainers":[{"name":"brunofeaturebase","email":"bruno@featurebase.app"}],"homepage":"https://featurebase.app","bugs":{"url":"https://featurebase.app/contact"},"dist":{"shasum":"41a7a14e22a16ed569745c82145e0fd583366e5e","tarball":"https://registry.npmjs.org/featurebase-js/-/featurebase-js-0.2.0-beta.3.tgz","fileCount":13,"integrity":"sha512-n8NwWmO1uZJH3xShSf3qzLFAD6byZ93Sc9QgDI4gg0cgat9ftqaASwV4oDu1eMomTtGCSp+NqaJIjvtQS7bfMw==","signatures":[{"sig":"MEUCIAGIJtMPN9wFM6PHIaqQihmLdJrm14LIVHSjM6ZHf+E5AiEA1gW21OXZzN77O0OKxDIOBjoaDzFlg6thLMQdMB02Y78=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":116849},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./react":{"types":"./dist/react.d.ts","import":"./dist/react.js","require":"./dist/react.cjs"},"./package.json":"./package.json"},"gitHead":"b30f0096ae9b14f428748011cc21dd1189c8b18e","private":false,"scripts":{"test":"jest --config jest.config.cjs","build":"tsup","check":"node scripts/check-publish-surface.cjs","typecheck":"tsc --noEmit -p tsconfig.json","prepublishOnly":"npm run build && npm run typecheck && npm run test && npm run check"},"_npmUser":{"name":"brunofeaturebase","email":"bruno@featurebase.app"},"repository":{"url":"git+https://github.com/FeaturebaseHQ/sdk.git","type":"git","directory":"client"},"_npmVersion":"10.8.2","description":"Typed client SDK for Featurebase — messenger, changelog widget, feedback widget, and surveys.","directories":{},"sideEffects":false,"_nodeVersion":"20.20.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"jest":"^29.7.0","tsup":"^8.3.5","react":"^19.0.0","ts-jest":"^29.2.5","react-dom":"^19.0.0","typescript":"^5.7.2","@types/jest":"^29.5.14","@types/node":"^22.10.0","@types/react":"^19.0.2","@types/react-dom":"^19.0.2","@testing-library/react":"^16.1.0","jest-environment-jsdom":"^29.7.0"},"peerDependencies":{"react":">=17"},"peerDependenciesMeta":{"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/featurebase-js_0.2.0-beta.3_1777560301106_0.6226182887871299","host":"s3://npm-registry-packages-npm-production"}},"0.2.0-beta.4":{"name":"featurebase-js","version":"0.2.0-beta.4","keywords":["featurebase","messenger","support","in-app-chat","help-center","changelog","feedback-widget","survey","survey-widget"],"author":{"name":"Featurebase","email":"hello@featurebase.app"},"license":"MIT","_id":"featurebase-js@0.2.0-beta.4","maintainers":[{"name":"brunofeaturebase","email":"bruno@featurebase.app"}],"homepage":"https://featurebase.app","bugs":{"url":"https://featurebase.app/contact"},"dist":{"shasum":"a1240e6faece070dea56666faae72e0201795ea2","tarball":"https://registry.npmjs.org/featurebase-js/-/featurebase-js-0.2.0-beta.4.tgz","fileCount":11,"integrity":"sha512-DkrJJ723zYjqfG4i197jvKn86p+6RfMd+4vfX+Jn0p+s0rjxWFHZBo62/XJg+0DtgAqzQ2uGlbeopSL0UjUp9w==","signatures":[{"sig":"MEQCIDFlOKLZreL0ypG35KltGbGMylafl/gKZMOhWoBP3ZULAiBWb8K1b9sDLasY5spvhdKuqZ7NrDJUIM1PZu82yrSrIg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":138425},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./react":{"types":"./dist/react.d.ts","import":"./dist/react.js","require":"./dist/react.cjs"},"./package.json":"./package.json"},"gitHead":"b30f0096ae9b14f428748011cc21dd1189c8b18e","private":false,"scripts":{"test":"jest --config jest.config.cjs","build":"tsup","check":"node scripts/check-publish-surface.cjs","typecheck":"tsc --noEmit -p tsconfig.json","prepublishOnly":"npm run build && npm run typecheck && npm run test && npm run check"},"_npmUser":{"name":"brunofeaturebase","email":"bruno@featurebase.app"},"repository":{"url":"git+https://github.com/FeaturebaseHQ/sdk.git","type":"git","directory":"client"},"_npmVersion":"10.8.2","description":"Typed client SDK for Featurebase — messenger, changelog widget, feedback widget, and surveys.","directories":{},"sideEffects":false,"_nodeVersion":"20.20.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"jest":"^29.7.0","tsup":"^8.3.5","react":"^19.0.0","ts-jest":"^29.2.5","react-dom":"^19.0.0","typescript":"^5.7.2","@types/jest":"^29.5.14","@types/node":"^22.10.0","@types/react":"^19.0.2","@types/react-dom":"^19.0.2","@testing-library/react":"^16.1.0","jest-environment-jsdom":"^29.7.0"},"peerDependencies":{"react":">=17"},"peerDependenciesMeta":{"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/featurebase-js_0.2.0-beta.4_1777561423868_0.531841038122465","host":"s3://npm-registry-packages-npm-production"}},"0.3.0-beta.0":{"name":"featurebase-js","version":"0.3.0-beta.0","keywords":["featurebase","messenger","support","in-app-chat","help-center","changelog","feedback-widget","survey","survey-widget"],"author":{"name":"Featurebase","email":"hello@featurebase.app"},"license":"MIT","_id":"featurebase-js@0.3.0-beta.0","maintainers":[{"name":"brunofeaturebase","email":"bruno@featurebase.app"}],"homepage":"https://featurebase.app","bugs":{"url":"https://featurebase.app/contact"},"dist":{"shasum":"eeb98eed128ca9b528308ff9d9f6d833a89342b7","tarball":"https://registry.npmjs.org/featurebase-js/-/featurebase-js-0.3.0-beta.0.tgz","fileCount":11,"integrity":"sha512-hLrY06nfzkHcJqFFTcqF/HqxKLJ+tuobLVPPE0f9Aj0/ube6SMS1xHVkwSnrFBS4KJPLeOLT0wYQsigVBsboQA==","signatures":[{"sig":"MEYCIQC3EfWAKSk2GBWqjgOXoHr788VPgGxDHhf4CyYjhodvIwIhAP5G/in+7Ntr3ebCb/LQ5b8+/sJYxZjyUtBMXqUXspmO","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":186024},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./react":{"types":"./dist/react.d.ts","import":"./dist/react.js","require":"./dist/react.cjs"},"./package.json":"./package.json"},"gitHead":"b30f0096ae9b14f428748011cc21dd1189c8b18e","private":false,"scripts":{"test":"jest --config jest.config.cjs","build":"tsup","check":"node scripts/check-publish-surface.cjs","typecheck":"tsc --noEmit -p tsconfig.json","prepublishOnly":"npm run build && npm run typecheck && npm run test && npm run check"},"_npmUser":{"name":"brunofeaturebase","email":"bruno@featurebase.app"},"repository":{"url":"git+https://github.com/FeaturebaseHQ/sdk.git","type":"git","directory":"client"},"_npmVersion":"10.8.2","description":"Typed client SDK for Featurebase — messenger, changelog widget, feedback widget, and surveys.","directories":{},"sideEffects":false,"_nodeVersion":"20.20.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"jest":"^29.7.0","tsup":"^8.3.5","react":"^19.0.0","ts-jest":"^29.2.5","react-dom":"^19.0.0","typescript":"^5.7.2","@types/jest":"^29.5.14","@types/node":"^22.10.0","@types/react":"^19.0.2","@types/react-dom":"^19.0.2","@testing-library/react":"^16.1.0","jest-environment-jsdom":"^29.7.0"},"peerDependencies":{"react":">=17"},"peerDependenciesMeta":{"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/featurebase-js_0.3.0-beta.0_1777909911885_0.36946319411344697","host":"s3://npm-registry-packages-npm-production"}},"0.3.0-beta.1":{"name":"featurebase-js","version":"0.3.0-beta.1","keywords":["featurebase","messenger","support","in-app-chat","help-center","changelog","feedback-widget","survey","survey-widget"],"author":{"name":"Featurebase","email":"hello@featurebase.app"},"license":"MIT","_id":"featurebase-js@0.3.0-beta.1","maintainers":[{"name":"brunofeaturebase","email":"bruno@featurebase.app"}],"homepage":"https://featurebase.app","bugs":{"url":"https://featurebase.app/contact"},"dist":{"shasum":"771b3feb9f79aa5cc9faae54268c70d2240cd4f8","tarball":"https://registry.npmjs.org/featurebase-js/-/featurebase-js-0.3.0-beta.1.tgz","fileCount":11,"integrity":"sha512-3Kl44L+DPPeHg08IqfGXBL1MX3m5tMKdKCFxNDhJteetsp11JSwFl/B3hOGOIDg7E4DCxYxuzwlFh0oSBiJq+Q==","signatures":[{"sig":"MEUCIELMHGqVPKNfdWnGVCghyLEOH5QNZhKyt9Yq4hHpbkjdAiEA1zys+c43oQbN6sXrxM5nHpnQD+bTQeRuS3IXp9bFGK0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":191132},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./react":{"types":"./dist/react.d.ts","import":"./dist/react.js","require":"./dist/react.cjs"},"./package.json":"./package.json"},"gitHead":"2ea5cdd1d505b0cbd969dc2c26f4d1206f770046","private":false,"scripts":{"test":"jest --config jest.config.cjs","build":"tsup","check":"node scripts/check-publish-surface.cjs","typecheck":"tsc --noEmit -p tsconfig.json","prepublishOnly":"npm run build && npm run typecheck && npm run test && npm run check"},"_npmUser":{"name":"brunofeaturebase","email":"bruno@featurebase.app"},"repository":{"url":"git+https://github.com/FeaturebaseHQ/sdk.git","type":"git","directory":"client"},"_npmVersion":"10.8.2","description":"Typed client SDK for Featurebase — messenger, changelog widget, feedback widget, and surveys.","directories":{},"sideEffects":false,"_nodeVersion":"20.20.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"jest":"^29.7.0","tsup":"^8.3.5","react":"18.2.0","ts-jest":"^29.2.5","react-dom":"18.2.0","typescript":"^5.7.2","@types/jest":"^29.5.14","@types/node":"^22.10.0","@types/react":"18.2.0","@types/react-dom":"18.2.0","@testing-library/react":"^16.1.0","jest-environment-jsdom":"^29.7.0"},"peerDependencies":{"react":">=17"},"peerDependenciesMeta":{"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/featurebase-js_0.3.0-beta.1_1778254601370_0.060393822408456144","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"name":"featurebase-js","version":"1.0.0","keywords":["featurebase","messenger","support","in-app-chat","help-center","changelog","feedback-widget","survey","survey-widget"],"author":{"name":"Featurebase","email":"hello@featurebase.app"},"license":"MIT","_id":"featurebase-js@1.0.0","maintainers":[{"name":"brunofeaturebase","email":"bruno@featurebase.app"}],"homepage":"https://featurebase.app","bugs":{"url":"https://featurebase.app/contact"},"dist":{"shasum":"f8466ea9b66799d43feae3d565a217d11a33a50b","tarball":"https://registry.npmjs.org/featurebase-js/-/featurebase-js-1.0.0.tgz","fileCount":11,"integrity":"sha512-Se/GZByix5o4yCHDAe82Zd45zHDN57YfBmo04/7J4dFs340wUEyBUVFhPO2Gnhf/wLVKlWJvq92wFmuO8O870w==","signatures":[{"sig":"MEYCIQDuiPq40xP5vVqexKjzCGw4b0fYxgG1kxrb6CdrLNYUkgIhAII6Gf14CRZJrCEtKxEZd1NH2Ol2WmQeUdHvIFZMm/u7","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":191314},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./react":{"types":"./dist/react.d.ts","import":"./dist/react.js","require":"./dist/react.cjs"},"./package.json":"./package.json"},"gitHead":"2ea5cdd1d505b0cbd969dc2c26f4d1206f770046","private":false,"scripts":{"test":"jest --config jest.config.cjs","build":"tsup","check":"node scripts/check-publish-surface.cjs","typecheck":"tsc --noEmit -p tsconfig.json","prepublishOnly":"npm run build && npm run typecheck && npm run test && npm run check"},"_npmUser":{"name":"brunofeaturebase","email":"bruno@featurebase.app"},"repository":{"url":"git+https://github.com/FeaturebaseHQ/sdk.git","type":"git","directory":"client"},"_npmVersion":"10.8.2","description":"Typed client SDK for Featurebase — messenger, changelog widget, feedback widget, and surveys.","directories":{},"sideEffects":false,"_nodeVersion":"20.20.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","tsup":"^8.3.5","react":"18.2.0","ts-jest":"^29.2.5","react-dom":"18.2.0","typescript":"^5.7.2","@types/jest":"^29.5.14","@types/node":"^22.10.0","@types/react":"18.2.0","@types/react-dom":"18.2.0","@testing-library/react":"^16.1.0","jest-environment-jsdom":"^29.7.0"},"peerDependencies":{"react":">=17"},"peerDependenciesMeta":{"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/featurebase-js_1.0.0_1778255950130_0.8376673374654531","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"featurebase-js","version":"1.0.1","keywords":["featurebase","messenger","support","in-app-chat","help-center","changelog","feedback-widget","survey","survey-widget"],"author":{"name":"Featurebase","email":"hello@featurebase.app"},"license":"MIT","_id":"featurebase-js@1.0.1","maintainers":[{"name":"brunofeaturebase","email":"bruno@featurebase.app"}],"homepage":"https://featurebase.app","bugs":{"url":"https://featurebase.app/contact"},"dist":{"shasum":"62bbf1e8c5b73872e82581560b9db4e3738ba190","tarball":"https://registry.npmjs.org/featurebase-js/-/featurebase-js-1.0.1.tgz","fileCount":11,"integrity":"sha512-aGsc69ahso7/60cWQBFln3XU/cf2J3KqsPLSD7zvHNoSCVQQSvv7j1enkO2ThpKTDzt8fd40sUdmsPadWKTESQ==","signatures":[{"sig":"MEUCIEzrqCLLzWO8/v+EXnHVK0rPmbtrpPDW/jfeqcoG5PRwAiEAsv8ZHf81h9FnfyqI6ApgRBUeC5QrqWDD0NefJ81yrSg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":193217},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./react":{"types":"./dist/react.d.ts","import":"./dist/react.js","require":"./dist/react.cjs"},"./package.json":"./package.json"},"gitHead":"539b84cc2c763b3391b44941b5262eee60ea662c","private":false,"scripts":{"test":"jest --config jest.config.cjs","build":"tsup","check":"node scripts/check-publish-surface.cjs","typecheck":"tsc --noEmit -p tsconfig.json","prepublishOnly":"npm run build && npm run typecheck && npm run test && npm run check"},"_npmUser":{"name":"brunofeaturebase","email":"bruno@featurebase.app"},"repository":{"url":"git+https://github.com/FeaturebaseHQ/sdk.git","type":"git","directory":"client"},"_npmVersion":"10.8.2","description":"Typed client SDK for Featurebase — messenger, changelog widget, feedback widget, and surveys.","directories":{},"sideEffects":false,"_nodeVersion":"20.20.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","tsup":"^8.3.5","react":"18.2.0","ts-jest":"^29.2.5","react-dom":"18.2.0","typescript":"^5.7.2","@types/jest":"^29.5.14","@types/node":"^22.10.0","@types/react":"18.2.0","@types/react-dom":"18.2.0","@testing-library/react":"^16.1.0","jest-environment-jsdom":"^29.7.0"},"peerDependencies":{"react":">=17"},"peerDependenciesMeta":{"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/featurebase-js_1.0.1_1778257846496_0.5585790281575567","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"featurebase-js","version":"1.0.2","keywords":["featurebase","messenger","support","in-app-chat","help-center","changelog","feedback-widget","survey","survey-widget"],"author":{"name":"Featurebase","email":"hello@featurebase.app"},"license":"MIT","_id":"featurebase-js@1.0.2","maintainers":[{"name":"brunofeaturebase","email":"bruno@featurebase.app"}],"homepage":"https://featurebase.app","bugs":{"url":"https://featurebase.app/contact"},"dist":{"shasum":"230dbbfc8eed0c7a517e8f9ba84e5104671b8465","tarball":"https://registry.npmjs.org/featurebase-js/-/featurebase-js-1.0.2.tgz","fileCount":11,"integrity":"sha512-UgfyC4bMg2iyKU2KJ7PlGaAPVMQzAtvyqIvkgjZEGw+Y6IQOHtav+wh3h6U8kUnP2UkvpNRG5akUb8R+kxGCHw==","signatures":[{"sig":"MEQCIGdV7ckQC0jnr6jm6tNMjatekytjJy9hdXzlkCi3mqdmAiAtWd46Qbe8wVVhQdPClpkF7MqoIg5axAnu7QQejaDDNg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":192978},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./react":{"types":"./dist/react.d.ts","import":"./dist/react.js","require":"./dist/react.cjs"},"./package.json":"./package.json"},"gitHead":"539b84cc2c763b3391b44941b5262eee60ea662c","private":false,"scripts":{"test":"jest --config jest.config.cjs","build":"tsup","check":"node scripts/check-publish-surface.cjs","typecheck":"tsc --noEmit -p tsconfig.json","prepublishOnly":"npm run build && npm run typecheck && npm run test && npm run check"},"_npmUser":{"name":"brunofeaturebase","email":"bruno@featurebase.app"},"repository":{"url":"git+https://github.com/FeaturebaseHQ/sdk.git","type":"git","directory":"client"},"_npmVersion":"10.8.2","description":"Typed client SDK for Featurebase — messenger, changelog widget, feedback widget, and surveys.","directories":{},"sideEffects":false,"_nodeVersion":"20.20.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","tsup":"^8.3.5","react":"18.2.0","ts-jest":"^29.2.5","react-dom":"18.2.0","typescript":"^5.7.2","@types/jest":"^29.5.14","@types/node":"^22.10.0","@types/react":"18.2.0","@types/react-dom":"18.2.0","@testing-library/react":"^16.1.0","jest-environment-jsdom":"^29.7.0"},"peerDependencies":{"react":">=17"},"peerDependenciesMeta":{"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/featurebase-js_1.0.2_1778272675315_0.44741321525061095","host":"s3://npm-registry-packages-npm-production"}},"1.0.3":{"name":"featurebase-js","version":"1.0.3","keywords":["featurebase","messenger","support","in-app-chat","help-center","changelog","feedback-widget","survey","survey-widget"],"author":{"name":"Featurebase","email":"hello@featurebase.app"},"license":"MIT","_id":"featurebase-js@1.0.3","maintainers":[{"name":"brunofeaturebase","email":"bruno@featurebase.app"}],"homepage":"https://featurebase.app","bugs":{"url":"https://featurebase.app/contact"},"dist":{"shasum":"e99c05a7e04064dcc390afa9c2be73998c4c1027","tarball":"https://registry.npmjs.org/featurebase-js/-/featurebase-js-1.0.3.tgz","fileCount":11,"integrity":"sha512-Q52xH6KEy6mYpxBl0ZRDzCaxW8EhoUjUxLbgG5e2GIMeiaS4uhKGHPK44yTRUmD+PTBgJ79/dc1GDFSwzz/sgg==","signatures":[{"sig":"MEUCIFy5z9xsV5kXFdHiS7nrc71icCbOA5fV9V4Y9Rzdo5+MAiEAz6TLrtIbxN9woeg36LjUEx9nhT9pknOIlecqPvKi3+g=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":217734},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./react":{"types":"./dist/react.d.ts","import":"./dist/react.js","require":"./dist/react.cjs"},"./package.json":"./package.json"},"gitHead":"be82abcff9c88ebb461b6a0b3b9720451bdc7fb0","private":false,"scripts":{"test":"jest --config jest.config.cjs","build":"tsup","check":"node scripts/check-publish-surface.cjs","typecheck":"tsc --noEmit -p tsconfig.json","prepublishOnly":"npm run build && npm run typecheck && npm run test && npm run check"},"_npmUser":{"name":"brunofeaturebase","email":"bruno@featurebase.app"},"repository":{"url":"git+https://github.com/FeaturebaseHQ/sdk.git","type":"git","directory":"client"},"_npmVersion":"10.8.2","description":"Typed client SDK for Featurebase — messenger, changelog widget, feedback widget, and surveys.","directories":{},"sideEffects":false,"_nodeVersion":"20.20.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","tsup":"^8.3.5","react":"18.2.0","ts-jest":"^29.2.5","react-dom":"18.2.0","typescript":"^5.7.2","@types/jest":"^29.5.14","@types/node":"^22.10.0","@types/react":"18.2.0","@types/react-dom":"18.2.0","@testing-library/react":"^16.1.0","jest-environment-jsdom":"^29.7.0"},"peerDependencies":{"react":">=17"},"peerDependenciesMeta":{"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/featurebase-js_1.0.3_1778489367649_0.7479698957109713","host":"s3://npm-registry-packages-npm-production"}}},"time":{"created":"2026-04-29T22:54:27.367Z","modified":"2026-06-09T13:46:37.729Z","0.1.0-beta.1":"2026-04-29T22:54:27.613Z","0.2.0-beta.1":"2026-04-30T14:00:35.125Z","0.2.0-beta.3":"2026-04-30T14:45:01.299Z","0.2.0-beta.4":"2026-04-30T15:03:44.068Z","0.3.0-beta.0":"2026-05-04T15:51:52.084Z","0.3.0-beta.1":"2026-05-08T15:36:41.547Z","1.0.0":"2026-05-08T15:59:10.286Z","1.0.1":"2026-05-08T16:30:46.650Z","1.0.2":"2026-05-08T20:37:55.456Z","1.0.3":"2026-05-11T08:49:27.785Z"},"bugs":{"url":"https://featurebase.app/contact"},"author":{"name":"Featurebase","email":"hello@featurebase.app"},"license":"MIT","homepage":"https://featurebase.app","keywords":["featurebase","messenger","support","in-app-chat","help-center","changelog","feedback-widget","survey","survey-widget"],"repository":{"url":"git+https://github.com/FeaturebaseHQ/sdk.git","type":"git","directory":"client"},"description":"Typed client SDK for Featurebase — messenger, changelog widget, feedback widget, and surveys.","maintainers":[{"email":"robi@featurebase.app","name":"rohukas"},{"email":"bruno@featurebase.app","name":"brunofeaturebase"}],"readme":"# featurebase-js\n\nTyped JavaScript / TypeScript SDK for [Featurebase](https://featurebase.app).\nOne package, full coverage for every embeddable surface:\n\n- **Messenger** — in-app chat, help center, changelog feed, news.\n- **Changelog widget** — dropdown / popup / floating \"What's new\" card.\n- **Feedback widget** — floating button to browse boards and submit posts.\n- **Surveys** — auto-displays active surveys based on dashboard targeting.\n- **Embed widget** — embed any Featurebase board / roadmap page directly inside your app.\n\nThe runtime script (`https://do.featurebase.app/js/sdk.js`) is auto-injected\non first use — you never need to add a `<script>` tag yourself. Every\nexport is tree-shakable, fully typed, and SSR-safe.\n\n## Install\n\n```bash\nnpm  install featurebase-js\npnpm add     featurebase-js\nyarn add     featurebase-js\n```\n\nPeer dependency: **React ≥ 17** (only required if you import from\n`featurebase-js/react`).\n\n---\n\n# 1. Setup\n\n**One field, one call.** Pass your `appId` and the SDK figures\neverything out:\n\n- Boots the messenger when your dashboard has **Manage modules →\n  Support** enabled (flip the toggle later, no code change needed).\n- Sets identity defaults for every standalone widget below (changelog /\n  feedback / surveys), so you never repeat them per widget.\n\nFind your `appId` in the dashboard under **Settings → Developers →\nInstallation**.\n\n## Path A — React\n\nAdd **one** Provider at your app root.\n\n```tsx\n// app/layout.tsx (Next.js) — must be a Client Component\n'use client';\n\nimport { FeaturebaseProvider } from 'featurebase-js/react';\n\nexport default function RootLayout({ children, user }) {\n  return (\n    <FeaturebaseProvider\n      appId=\"abc123\"                         // Settings → Developers → Installation\n      featurebaseJwt={user?.featurebaseJwt}  // Optional — see Section 8\n    >\n      {children}\n    </FeaturebaseProvider>\n  );\n}\n```\n\n> ✅ **Done.** Anonymous visitors are fully supported — `appId` alone is\n> enough to render every surface. To attribute conversations / posts /\n> survey responses to a logged-in user, see\n> [Section 8 — Identifying users](#8-identifying-users-jwt).\n\n> Jump to [Section 2 (Messenger)](#2-messenger) to control it, or\n> [Section 3 / 4 / 5 / 6](#3-changelog-widget) to wire up the standalone\n> widgets — you don't have to repeat `appId`.\n\n`FeaturebaseProvider` props (full list):\n\n| Prop | Type | Purpose |\n|---|---|---|\n| `appId` | `string` | Your Featurebase organization id. Drives both messenger and widgets. |\n| `featurebaseJwt` | `string` | **Recommended for logged-in users.** Server-signed JWT carrying identity + custom attributes. See [Section 8](#8-identifying-users-jwt). |\n| `messenger` | `boolean` | Advanced. `false` opts out of the messenger even when Support is enabled in the dashboard. Default `true`. |\n| `userId`, `email`, `name` | identity | Only needed for non-secured installs, or as non-sensitive supplements when secure-update is disabled per-attribute. Prefer the JWT (see [Section 8](#8-identifying-users-jwt)). |\n| `createdAt`, `customData`, `company`, `companies` | profile | Forwarded to the contact. Prefer placing these in the JWT when sensitive. |\n| `theme`, `language` | `string` | Initial messenger appearance. |\n| `hideDefaultLauncher`, `alignment`, `horizontalPadding`, `verticalPadding` | launcher | Position/visibility of the floating launcher. |\n| `jwtToken` | `string` | Pre-minted JWT for widget identity (advanced; usually leave unset). |\n| `[key: string]` | `unknown` | Any other key is forwarded verbatim onto the contact profile. |\n\n## Path B — Vanilla (any framework or no framework)\n\nA single `Featurebase()` call. Same shape as the Provider above:\n\n```ts\nimport Featurebase from 'featurebase-js';\n\nFeaturebase({\n  appId: 'abc123',                       // Settings → Developers → Installation\n  featurebaseJwt: user?.featurebaseJwt,  // Optional — see Section 8\n});\n```\n\nSubsequent calls with the same `appId` re-identify the visitor without\nre-rendering the messenger, so it's safe to call inside any auth-change\nhandler.\n\n> ✅ **Done.** Anonymous visitors are fully supported — pass `appId`\n> alone and skip `featurebaseJwt` entirely. To tie conversations / posts\n> / survey responses to a logged-in user, see\n> [Section 8 — Identifying users](#8-identifying-users-jwt).\n\n> Jump to [Section 2 (Messenger)](#2-messenger) to control it, or\n> [Section 3 / 4 / 5 / 6](#3-changelog-widget) to add the standalone\n> widgets — they inherit `appId` and identity from this call.\n\n## What boots when?\n\nThe SDK reads your dashboard's **General → Manage modules** toggles to\ndecide which surfaces to start. You don't repeat the answer in code.\n\n| Module enabled in dashboard? | Effect on `Featurebase({ appId })` |\n|---|---|\n| Support **on** | Messenger boots automatically. |\n| Support **off** | Messenger stays off — nothing is rendered, no console warnings. Flip it on later, no code change needed. |\n| Feedback / Changelog / Help Center / Surveys **on** | Available to the matching `init*()` / `useXxxWidget()` call. |\n| Module **off** | The runtime refuses to load it and logs a `[featurebase-js]` warning when you call the matching `init*()`. |\n\n> 💡 **Want the messenger off even when Support is enabled?** Pass\n> `messenger: false`. Power-user only — most installs leave it unset.\n\n```ts\n// Don't boot the messenger here, even if dashboard has Support on.\nFeaturebase({ appId: 'abc123', messenger: false });\n```\n\n## Widgets-only install\n\nSame `Featurebase({ appId })` / `<FeaturebaseProvider appId>` setup as\nabove. If your workspace has Support disabled in **Manage modules**,\nthe messenger silently skips boot — there's nothing to do client-side.\n\n## On login / logout\n\nBoth paths handle auth changes gracefully:\n\n| | React | Vanilla |\n|---|---|---|\n| **Login** (user becomes known) | Provider re-renders with new `featurebaseJwt` → auto re-identifies. No re-mount. | Call `Featurebase({ appId, featurebaseJwt, ... })` again — same call as setup. |\n| **Logout** (user becomes anonymous) | Conditionally render the Provider, OR call `shutdown()` from `useFeaturebase()`. | Call `shutdown()` (messenger) and/or `clearConfig()` (widgets). |\n\nSee [Common patterns](#7-common-patterns) for full code.\n\n---\n\n# 2. Messenger\n\n> ✅ **Already installed** by your setup step above. This section is\n> purely about controlling it.\n\n> 👤 **Anonymous works.** The messenger renders fine without identifying\n> the user. To attribute conversations to a Featurebase contact, see\n> [Section 8 — Identifying users](#8-identifying-users-jwt).\n\nThe messenger gives you in-app chat, a help center, conversations, and a\nnews/changelog feed — all behind one floating launcher. The launcher is\nvisible by default; opt out with `hideDefaultLauncher: true` and use\nyour own button.\n\n## Open and close\n\n| Method | What it does |\n|---|---|\n| `show()` | Open to the default landing space. |\n| `hide()` | Close the messenger. |\n| `showSpace(space)` | Open to a specific space: `'home' \\| 'messages' \\| 'help' \\| 'changelog'` (or any future space id). |\n| `showMessages()` | Shortcut for `showSpace('messages')`. |\n\n```ts\n// Vanilla\nimport { show, hide, showSpace, showMessages } from 'featurebase-js';\n\nshow();\nhide();\nshowSpace('help');\nshowSpace('changelog');\nshowMessages();\n```\n\n```tsx\n// React\nconst { show, hide, showSpace, showMessages } = useFeaturebase();\nreturn <button onClick={show}>Help</button>;\n```\n\n## Open specific content\n\n| Method | What it does |\n|---|---|\n| `showArticle(id)` | Open a specific help-center article by id or slug. |\n| `showChangelog(id?)` | Open the changelog index, or a specific entry when `id` is given. |\n| `showNews(id)` | Alias for `showChangelog(id)`. |\n| `showConversation(id)` | Open a specific conversation thread. |\n| `showNewMessage(text?)` | Open the new-message composer, optionally pre-filled. |\n\n```ts\nimport {\n  showArticle,\n  showChangelog,\n  showConversation,\n  showNewMessage,\n} from 'featurebase-js';\n\nshowArticle('billing-faq');\nshowChangelog();                              // index\nshowChangelog('cl_release_4_2_0');            // specific entry\nshowConversation('conv_8q2x');\nshowNewMessage();                             // empty composer\nshowNewMessage('I want to upgrade my plan');  // pre-filled composer\n```\n\n## Appearance\n\n| Method | What it does |\n|---|---|\n| `setTheme('light' \\| 'dark')` | Switch the messenger theme at runtime. |\n| `setLanguage(code)` | Switch the messenger language (BCP-47 short code). |\n\n```ts\nimport { setTheme, setLanguage } from 'featurebase-js';\n\n// Sync the messenger with your app's theme toggle\nconst mql = window.matchMedia('(prefers-color-scheme: dark)');\nsetTheme(mql.matches ? 'dark' : 'light');\nmql.addEventListener('change', (e) => setTheme(e.matches ? 'dark' : 'light'));\n\nsetLanguage('de');\nsetLanguage('pt-BR');\n```\n\nSee [`FeaturebaseLocale`](#featurebaselocale) for every supported code.\n\n## Events\n\n| Method | Fires when |\n|---|---|\n| `onShow(cb)` | The messenger becomes visible. |\n| `onHide(cb)` | The messenger is dismissed. |\n| `onUnreadCountChange(cb)` | The unread-conversation count changes. Receives `(count: number)`. |\n| `onExternalLinkOpen(cb)` | The user clicks an outbound link inside the messenger. Receives `(url: string)`. |\n\n> ⚠️ Wrap subscriptions in `whenReady(...)` if you call them in the\n> same tick as setup — the messenger boot is async and you'd otherwise\n> see \"Please call 'boot' first\" in the console.\n\n```ts\nimport {\n  whenReady,\n  onShow,\n  onHide,\n  onUnreadCountChange,\n  onExternalLinkOpen,\n} from 'featurebase-js';\n\nwhenReady(() => {\n  onShow(() => analytics.track('messenger_opened'));\n  onHide(() => analytics.track('messenger_closed'));\n\n  onUnreadCountChange((count) => {\n    document.title = count > 0 ? `(${count}) Acme` : 'Acme';\n  });\n\n  onExternalLinkOpen((url) => analytics.track('outbound_link', { url }));\n});\n```\n\nIn React, `useFeaturebase()` exposes `unreadCount` directly as reactive\nstate — no manual subscription needed:\n\n```tsx\nconst { show, unreadCount } = useFeaturebase();\nreturn <button onClick={show}>Chat ({unreadCount})</button>;\n```\n\n## Update identity / shut down\n\n| Method | What it does |\n|---|---|\n| `update(settings?)` | Re-identify the current visitor without re-booting. Reuses the previously booted `appId` if you don't pass one. |\n| `shutdown()` | Tear down the messenger UI, real-time connection, and cached identity. After `shutdown()`, the next `Featurebase()` call fully re-boots. |\n\n```ts\nimport { update, shutdown } from 'featurebase-js';\n\n// User updates their email in your settings page\nupdate({ email: 'new@email.com' });\n\n// Attach a freshly-onboarded company\nupdate({ company: { companyId: 'co_42', plan: 'enterprise' } });\n\n// Logout\nshutdown();\n```\n\nIn React, the Provider does this automatically when its props change.\nYou only need to call `shutdown()` if you want to fully tear down before\nunmounting.\n\n```tsx\nconst { update, shutdown } = useFeaturebase();\n```\n\n## Reference: every messenger setting\n\nAccepted by `Featurebase()`, `boot()`, `update()`, and the\n`<FeaturebaseProvider>` props.\n\n| Field | Type | Default | Notes |\n|---|---|---|---|\n| `appId` | `string` | — | **Required.** Your Featurebase organization id (the value the dashboard's **Settings → Developers → Installation** copy block exposes). Drives both messenger and widgets. Alias: `app_id`. |\n| `messenger` | `boolean` | `true` | Advanced. `false` opts out of the messenger even when **General → Manage modules** has Support enabled. |\n| `featurebaseJwt` | `string` | — | **Recommended for logged-in users.** Server-signed JWT carrying identity + custom attributes. See [Section 8](#8-identifying-users-jwt). |\n| `userId` | `string \\| number` | — | Use only when an attribute is explicitly configured as non-secured in the dashboard. Otherwise put inside the JWT. Alias: `user_id`. |\n| `email` | `string` | — | Same as `userId`. |\n| `name` | `string` | — | Display name. |\n| `createdAt` | `number \\| string` | — | Account creation date (Unix ms or ISO 8601). Alias: `created_at`. |\n| `company` | `FeaturebaseCompany` | — | Single company association. Prefer the JWT for sensitive fields. |\n| `companies` | `FeaturebaseCompany[]` | — | Multiple companies (multi-tenant users). |\n| `$brandId` | `string` | — | Active brand id when running multi-brand. |\n| `theme` | `'light' \\| 'dark'` | `'light'` | Aliases: `themeMode`, `theme_mode`. |\n| `language` | `string` | `'en'` | BCP-47 short code. Aliases: `languageOverride`, `language_override`. |\n| `hideDefaultLauncher` | `boolean` | `false` | Hide the floating launcher (use your own with `show()`). Alias: `hide_default_launcher`. |\n| `alignment` | `'left' \\| 'right'` | `'right'` | Launcher side. |\n| `horizontalPadding` | `number` | runtime default | px from the side. Alias: `horizontal_padding`. |\n| `verticalPadding` | `number` | runtime default | px from the bottom. Alias: `vertical_padding`. |\n| `customData` | `Record<string, unknown>` | — | Non-secured custom user attributes. Alias: `custom_data`. |\n| `[key: string]` | `unknown` | — | Any other key is forwarded verbatim onto the contact's profile. |\n\n`FeaturebaseCompany` shape:\n\n| Field | Type | Notes |\n|---|---|---|\n| `companyId` | `string` | Stable company id. Alias: `company_id`. |\n| `name`, `plan`, `website`, `industry` | `string` | |\n| `createdAt` | `number \\| string` | |\n| `monthlySpend` | `number` | MRR or similar. |\n| `size` | `number` | Headcount. |\n| `[key: string]` | `unknown` | Catchall — forwarded to the company profile. |\n\nBoth camelCase (preferred) and snake_case keys are accepted; the wrapper\nnormalizes to camelCase before dispatching.\n\n> 🔒 **Sensitive vs non-sensitive attributes.** Anything that should be\n> trusted (display name, plan, role, etc.) belongs **inside the JWT**.\n> Plain-text props like `userId` / `email` outside the JWT are only safe\n> when the corresponding attribute is explicitly marked non-secured in\n> your dashboard settings. See the [security guide](https://help.featurebase.app/en/help/articles/5402549-secure-your-installation-required-by-default).\n\n---\n\n# 3. Changelog widget\n\n> 👤 **Anonymous works.** Renders the latest entries fine without\n> identifying the user. To pre-personalize the popup greeting and track\n> per-user read state, see\n> [Section 8 — Identifying users](#8-identifying-users-jwt).\n\nA standalone \"What's new\" surface, independent from the messenger. Three\nsub-modes you can mix and match:\n\n- **dropdown** — attaches to a `<button data-featurebase-changelog>` in\n  your nav and renders a dropdown panel.\n- **popup** — auto-displays a fullscreen popup of the latest unread\n  update.\n- **changelogCard** — floating bottom-corner card.\n\n## Standalone install — no messenger\n\nIf your workspace has Support disabled (or you set `messenger: false`),\n[Section 1](#1-setup) already gave you the only setup call you need.\nThe snippets below are complete copy-paste samples for the changelog\nwidget alone.\n\n### Vanilla\n\n```ts\nimport Featurebase, { initChangelog } from 'featurebase-js';\n\n// 1) One-time setup. Same call as Section 1 — appId drives the slug.\nFeaturebase({\n  appId: 'abc123',\n  featurebaseJwt: user?.featurebaseJwt, // optional, see Section 8\n});\n\n// 2) Boot the widget. Re-call to change config; `destroyChangelog()` to remove.\n//    If 1) hasn't finished resolving yet, this call is queued and fires\n//    automatically once the slug lands.\ninitChangelog({\n  theme: 'light',\n  dropdown: { enabled: true, placement: 'bottom' },\n});\n```\n\n```html\n<button data-featurebase-changelog>What's new</button>\n```\n\n### React\n\n```tsx\nimport { FeaturebaseProvider, useChangelogWidget } from 'featurebase-js/react';\n\nfunction App({ user }) {\n  return (\n    <FeaturebaseProvider appId=\"abc123\" featurebaseJwt={user?.featurebaseJwt}>\n      <Header />\n    </FeaturebaseProvider>\n  );\n}\n\nfunction Header() {\n  const { getUnviewedCount } = useChangelogWidget({\n    theme: 'light',\n    dropdown: { enabled: true, placement: 'bottom' },\n  });\n  return (\n    <button data-featurebase-changelog>\n      What's new ({getUnviewedCount() ?? 0})\n    </button>\n  );\n}\n```\n\nThe hook boots the widget on mount, tears it down on unmount, and\nre-inits only when the resolved config meaningfully changes (so passing\ninline object literals doesn't thrash).\n\n## Already set up?\n\nIdentity (`appId`, `featurebaseJwt`) is inherited from\n[Setup](#1-setup) — `<FeaturebaseProvider>` in React or your\n`Featurebase()` call in vanilla. Drop straight into:\n\n```tsx\n// React\nuseChangelogWidget({ theme: 'light', dropdown: { enabled: true } });\n```\n\n```ts\n// Vanilla\ninitChangelog({ theme: 'light', dropdown: { enabled: true } });\n```\n\n## Sub-modes\n\n### Dropdown\n\n```ts\ninitChangelog({\n  theme: 'light',\n  dropdown: {\n    enabled: true,\n    placement: 'bottom',           // 'bottom' | 'top' | 'left' | 'right' | 'auto'\n    lazyLoad: true,                // defer fetching until first open\n  },\n});\n```\n\nBind to any element matching `button[data-featurebase-changelog]`:\n\n```html\n<button data-featurebase-changelog>What's new</button>\n```\n\n### Popup\n\nAuto-displays a fullscreen popup of the latest unread update.\n\n```ts\ninitChangelog({\n  theme: 'dark',\n  popup: {\n    enabled: true,\n    autoOpenForNewUpdates: true,\n    usersName: user.firstName,     // personalizes the greeting\n  },\n});\n```\n\n### Floating card\n\nA small announcement card pinned to a corner.\n\n```ts\ninitChangelog({\n  theme: 'light',\n  changelogCard: {\n    enabled: true,\n    openInNewTab: true,\n    layout: { position: 'bottom-right', marginBottom: 24, maxWidth: 360 },\n    theme: { borderRadius: 12, backgroundColor: '#ffffff', titleColor: '#111827' },\n  },\n});\n```\n\n### All three together\n\n```ts\ninitChangelog({\n  theme: 'light',\n  dropdown: { enabled: true },\n  popup: { enabled: true, autoOpenForNewUpdates: true },\n  changelogCard: { enabled: true, layout: { position: 'bottom-right' } },\n});\n```\n\n## Imperative methods\n\nAvailable from any tick after `initChangelog` has been called.\n\n| Method | Returns | Notes |\n|---|---|---|\n| `openChangelogPopup(data?)` | `void` | Open the popup (requires `popup.enabled` in init). |\n| `markAllChangelogsAsViewed()` | `void` | Mark every entry as read for the current visitor. |\n| `getUnviewedChangelogCount()` | `number \\| undefined` | Synchronous read of the unread count. `undefined` before init or in SSR. |\n| `destroyChangelog()` | `void` | Tear down DOM, listeners, and cached state. |\n\n```ts\nimport {\n  openChangelogPopup,\n  markAllChangelogsAsViewed,\n  getUnviewedChangelogCount,\n  destroyChangelog,\n} from 'featurebase-js';\n\nconst unread = getUnviewedChangelogCount() ?? 0;\ndocument.querySelector('#whatsnew-badge').textContent = String(unread);\n\nopenChangelogPopup();\nmarkAllChangelogsAsViewed();\ndestroyChangelog();\n```\n\nIn React these are returned from `useChangelogWidget()`:\n\n```tsx\nconst { openPopup, markAllAsViewed, getUnviewedCount } = useChangelogWidget({ ... });\n```\n\n## Reference: `ChangelogWidgetConfig`\n\n| Field | Type | Required | Notes |\n|---|---|---|---|\n| `theme` | `'light' \\| 'dark'` | ✓ | Widget shell theme. |\n| `dropdown` | `{ enabled, placement?, lazyLoad? }` | one of dropdown/popup/changelogCard | See above. |\n| `popup` | `{ enabled, usersName?, autoOpenForNewUpdates? }` | \"\" | See above. |\n| `changelogCard` | `{ enabled, openInNewTab?, layout?, theme? }` | \"\" | See sub-table below. |\n| `category` | `string[]` | — | Filter to a subset of category slugs. |\n| `locale` | `FeaturebaseLocale` | — | UI language. Defaults to `'en'`. |\n| `featurebaseJwt` | `string` | — | Server-signed JWT. Inherited from setup; pass to override. |\n| `email`, `userId` | identity | — | Only when configured as non-secured in dashboard. Prefer the JWT. |\n\n`changelogCard` sub-fields:\n\n| Field | Type | Notes |\n|---|---|---|\n| `enabled` | `boolean` | |\n| `openInNewTab` | `boolean` | |\n| `layout.position` | `'bottom-left' \\| 'bottom-right'` | |\n| `layout.marginBottom` | `number` | px from the bottom. |\n| `layout.marginSide` | `number` | px from the side. |\n| `layout.maxWidth` | `number` | px max-width of the card. |\n| `theme.borderRadius` | `number` | |\n| `theme.backgroundColor`, `theme.titleColor`, `theme.descriptionColor`, `theme.borderColor` | `string` | Any CSS color value. |\n\n> At least one of `dropdown`, `popup`, or `changelogCard` must have\n> `enabled: true` — otherwise the widget has nothing to render.\n\n---\n\n# 4. Feedback widget\n\n> 👤 **Anonymous works.** Visitors can browse boards and submit posts\n> without being logged in. To submit posts as the signed-in user (no\n> email prompt) and tie them to a Featurebase contact, see\n> [Section 8 — Identifying users](#8-identifying-users-jwt).\n\nA floating button (bottom-left or bottom-right) that opens a panel where\nusers browse your Featurebase boards and submit posts without leaving\nyour app.\n\n## Standalone install — no messenger\n\n```ts\nimport Featurebase, { initFeedback, destroyFeedback } from 'featurebase-js';\n\nFeaturebase({\n  appId: 'abc123',\n  featurebaseJwt: user?.featurebaseJwt,    // optional\n});\n\ninitFeedback({\n  theme: 'light',\n  placement: 'bottom-right',\n  defaultBoard: 'feature-requests',\n  metadata: { plan: 'pro', appVersion: '4.2.0' },\n});\n\n// Later (e.g. when navigating away in an SPA)\ndestroyFeedback();\n```\n\n```tsx\n// React equivalent\nimport { FeaturebaseProvider, useFeedbackWidget } from 'featurebase-js/react';\n\nfunction App({ user }) {\n  return (\n    <FeaturebaseProvider appId=\"abc123\" featurebaseJwt={user?.featurebaseJwt}>\n      <FeedbackHost />\n    </FeaturebaseProvider>\n  );\n}\n\nfunction FeedbackHost() {\n  useFeedbackWidget({\n    theme: 'light',\n    placement: 'bottom-right',\n    defaultBoard: 'feature-requests',\n  });\n  return null;\n}\n```\n\nThe hook boots the widget on mount and tears it down on unmount.\n\n## Already set up?\n\nIdentity inherited from [Setup](#1-setup). Just drop:\n\n```tsx\nuseFeedbackWidget({ theme: 'light', placement: 'bottom-right' });\n```\n\n```ts\ninitFeedback({ theme: 'light', placement: 'bottom-right' });\n```\n\n## Reference: `FeedbackWidgetConfig`\n\n| Field | Type | Required | Notes |\n|---|---|---|---|\n| `theme` | `'light' \\| 'dark'` | ✓ | Widget shell theme. |\n| `placement` | `'bottom-right' \\| 'bottom-left' \\| 'left' \\| 'right'` | — | Floating button position. Defaults to `'bottom-right'`. |\n| `defaultBoard` | `string` | — | Slug of the board to open the panel on. |\n| `metadata` | `Record<string, string> \\| null` | — | Arbitrary string attributes attached to every post submitted while the widget is mounted (e.g. plan, version). |\n| `locale` | `FeaturebaseLocale` | — | UI language. |\n| `featurebaseJwt` | `string` | — | Server-signed JWT. Inherited from setup; pass to override. |\n| `email`, `userId` | identity | — | Only when configured as non-secured in dashboard. Prefer the JWT. |\n\n---\n\n# 5. Surveys\n\n> 👤 **Anonymous works** for any survey whose dashboard targeting\n> doesn't reference user attributes. To target by plan / role / segment\n> or tie responses to a Featurebase contact, see\n> [Section 8 — Identifying users](#8-identifying-users-jwt).\n\nBackground runtime that polls for active surveys whose targeting rules\nmatch the current page (URL pattern, CSS selector, segment) and\nauto-displays them. **Targeting is owned by the dashboard** — there's no\nimperative `showSurvey(id)`.\n\n## Standalone install — no messenger\n\n```ts\nimport Featurebase, { initSurveys } from 'featurebase-js';\n\nFeaturebase({\n  appId: 'abc123',\n  featurebaseJwt: user?.featurebaseJwt,  // required for user-attribute targeting; see Section 8\n});\n\ninitSurveys();                 // mount once and forget — no destroy needed\n```\n\n```tsx\n// React equivalent\nimport { FeaturebaseProvider, useSurveyWidget } from 'featurebase-js/react';\n\nfunction App({ user }) {\n  return (\n    <FeaturebaseProvider appId=\"abc123\" featurebaseJwt={user?.featurebaseJwt}>\n      <SurveyHost />\n    </FeaturebaseProvider>\n  );\n}\n\nfunction SurveyHost() {\n  useSurveyWidget();\n  return null;\n}\n```\n\n## Already set up?\n\nIdentity inherited from [Setup](#1-setup). Mount once at the app root:\n\n```tsx\nuseSurveyWidget();\n```\n\n```ts\ninitSurveys();\n```\n\nYou can still pass overrides per call when you need them:\n\n```ts\ninitSurveys({ placement: 'bottom-right', featurebaseJwt: otherJwt });\n```\n\n## Runtime behavior\n\n- Fetches all active surveys for the org once on init, then **polls every\n  5 seconds** to see whether any targeting rule matches the current page.\n- Once a user **responds or dismisses** a survey, its id is stored in\n  `localStorage.featurebaseRespondedSurveys` and won't show again on the\n  same browser.\n- `initSurveys` is idempotent — calling it again while a survey is\n  already mounted is a no-op.\n- There is **no `destroySurveys`** in the runtime today; mount it once.\n\n## Reference: `SurveyWidgetConfig`\n\n| Field | Type | Required | Notes |\n|---|---|---|---|\n| `placement` | `'bottom-left' \\| 'bottom-right' \\| null` | — | Where the panel anchors. `null` defers to the dashboard. |\n| `theme` | `string` | — | Override the survey's configured theme. |\n| `locale` | `FeaturebaseLocale` | — | UI language. |\n| `featurebaseJwt` | `string` | — | Server-signed JWT. Required when targeting includes \"logged-in users only\" or segment rules that depend on the user's profile. Inherited from setup. |\n| `email`, `userId` | identity | — | Only when configured as non-secured in dashboard. Prefer the JWT. |\n\n---\n\n# 6. Embed widget\n\n> 👤 **Anonymous works.** Visitors can browse the embedded portal, vote,\n> and submit posts without being logged in. To attribute their actions to\n> a Featurebase contact (and skip the email prompt on submit), see\n> [Section 8 — Identifying users](#8-identifying-users-jwt).\n\nMounts a Featurebase board, roadmap, or changelog page **inside your own\napp** as an iframe — no popup, no floating launcher. Use it to ship a\nfully-branded `/feedback`, `/roadmap`, or `/changelog` route that lives\nunder your domain instead of redirecting users to a Featurebase-hosted\nportal.\n\nThe runtime mounts into any element with the `data-featurebase-embed`\nattribute; you control its size, layout, and surrounding chrome. The\nembed bundle itself is **lazy-loaded** — only customers who actually call\n`initEmbedWidget()` / `useEmbedWidget()` download the iframe-resizer\nchunk. Messenger-only and widget-only customers pay nothing for it.\n\n## Standalone install — no messenger\n\n```ts\nimport Featurebase, { initEmbedWidget } from 'featurebase-js';\n\nFeaturebase({\n  appId: 'abc123',\n  featurebaseJwt: user?.featurebaseJwt,    // optional\n});\n\ninitEmbedWidget(\n  {\n    embedOptions: {\n      // '/' (boards), '/roadmap', '/changelog', '/p/<post-slug>'\n      path: '/',\n      // Optional query-string filters (without the leading '?').\n      // Example: pre-filter the boards index to one board, sorted by upvotes.\n      filters: '',  // e.g. 'b=63f827df2d62cb301468aac4&sortBy=upvotes:desc'\n      // ADVANCED — mirror iframe routes onto the host URL for deep-linking.\n      // routeSyncingBasePath: '/feedback',\n    },\n    stylingOptions: {\n      // 'light' | 'dark' | '' (auto-detect from prefers-color-scheme)\n      theme: 'light',\n      hideMenu: false,   // hide the top nav (Boards / Roadmap / Changelog)\n      hideLogo: false,   // hide the Featurebase logo in the nav bar\n    },\n    // Identity is auto-forwarded from the universal Featurebase({ appId,\n    // featurebaseJwt }) call above. Optional: attach arbitrary metadata\n    // to posts / votes submitted from inside the iframe.\n    // user: { metadata: { plan: 'pro', appVersion: '4.2.0' } },\n  },\n  // Optional callback — fires on lifecycle events (`loaded`, `widgetReady`)\n  // and on every route change inside the iframe.\n  (error, evt) => {\n    if (error) return console.error('[embed]', error);\n    if (evt?.action === 'widgetReady') console.log('embed ready');\n  },\n);\n```\n\n```html\n<!-- Anywhere in the page; the SDK mounts here. -->\n<div data-featurebase-embed style=\"height: 100vh\"></div>\n```\n\n```tsx\n// React equivalent\nimport { FeaturebaseProvider, useEmbedWidget } from 'featurebase-js/react';\n\nfunction App({ user }) {\n  return (\n    <FeaturebaseProvider appId=\"abc123\" featurebaseJwt={user?.featurebaseJwt}>\n      <FeedbackPage />\n    </FeaturebaseProvider>\n  );\n}\n\nfunction FeedbackPage() {\n  useEmbedWidget({\n    embedOptions: {\n      path: '/',                            // '/' | '/roadmap' | '/changelog' | '/p/<post>'\n      // filters: 'b=63f827df2d62cb301468aac4&sortBy=upvotes:desc',\n      // routeSyncingBasePath: '/feedback', // ADVANCED — see below\n    },\n    stylingOptions: {\n      theme: 'light',                       // 'light' | 'dark' | '' (auto)\n      hideMenu: false,\n      hideLogo: false,\n    },\n  });\n  return <div data-featurebase-embed style={{ height: '100vh' }} />;\n}\n```\n\nThe hook re-inits whenever the resolved config changes — the runtime\ntreats subsequent calls as **in-place updates** of the existing iframe,\nso flipping themes or paths doesn't remount the board (no flash, no\nscroll-to-top). The iframe itself is owned by the\n`data-featurebase-embed` element you put in your tree, so unmounting\nthe host element tears it down via the React lifecycle — no separate\n`destroy*()` call is needed.\n\n## Already set up?\n\nIdentity inherited from [Setup](#1-setup). Drop straight into:\n\n```tsx\nuseEmbedWidget({\n  stylingOptions: { theme: 'light' },\n  embedOptions: { path: '/roadmap' },\n});\n```\n\n```ts\ninitEmbedWidget({\n  stylingOptions: { theme: 'light' },\n  embedOptions: { path: '/roadmap' },\n});\n```\n\n## Common recipes\n\n### Embedded roadmap page\n\n```tsx\nuseEmbedWidget({\n  stylingOptions: { theme: 'light', hideMenu: true, hideLogo: true },\n  embedOptions: { path: '/roadmap' },\n});\n```\n\n### Embedded board with URL syncing\n\nReflect navigation inside the iframe (e.g. opening a post) onto your\nhost URL so users can deep-link / back-button:\n\n```tsx\nuseEmbedWidget({\n  stylingOptions: { theme: 'light' },\n  embedOptions: {\n    path: '/',\n    routeSyncingBasePath: '/feedback',  // host URL becomes /feedback/p/abc123\n  },\n});\n```\n\n### Following the host app theme\n\n```tsx\nconst { theme } = useTheme();             // your own theme hook\nuseEmbedWidget({\n  stylingOptions: { theme },              // 'light' | 'dark'\n  embedOptions: { path: '/' },\n});\n```\n\n## Reference: `FeaturebaseEmbedWidgetConfig`\n\n| Field | Type | Required | Notes |\n|---|---|---|---|\n| `embedOptions.path` | `string \\| null` | — | Path to mount inside the portal. Common values: `'/'` (feedback boards index — default), `'/roadmap'`, `'/changelog'`, `'/p/<post-slug>'`. |\n| `embedOptions.filters` | `string \\| null` | — | Query-string filters applied to the mounted page (without the leading `?`). Example: `'b=63f827df2d62cb301468aac4&sortBy=upvotes:desc'` pre-filters the boards index to one board sorted by upvotes. |\n| `embedOptions.routeSyncingBasePath` | `string \\| null` | — | **Advanced.** Reflect iframe route changes onto the host URL under this prefix. Example: `'/feedback'` → host URL becomes `/feedback/p/abc123` when the visitor opens a post. Defaults to no syncing. |\n| `stylingOptions.theme` | `'light' \\| 'dark' \\| ''` | — | Force a theme inside the iframe. `''` auto-detects from `prefers-color-scheme`. Omitted / `null` falls back to the workspace default theme. |\n| `stylingOptions.hideMenu` | `boolean` | — | Hide the top navigation bar inside the iframe (Boards / Roadmap / Changelog tabs). Useful when embedding a single surface and the host page has its own navigation. Defaults to `false`. |\n| `stylingOptions.hideLogo` | `boolean` | — | Hide the Featurebase logo in the navigation bar. Defaults to `false`. |\n| `user.metadata` | `Record<string, string>` | — | Arbitrary string attributes attached to posts / votes submitted from inside the iframe (e.g. `{ plan: 'pro', appVersion: '4.2.0' }`). Not user identity — see below. |\n\n> 🔑 **Identity & workspace are wrapper-managed.** You never pass\n> `appId` / `organization` / `userId` / `email` / `featurebaseJwt` per\n> embed call. They flow in once through the\n> `Featurebase({ appId, featurebaseJwt })` call (or `<FeaturebaseProvider>`\n> in [Setup](#1-setup)) and the wrapper auto-forwards them to every\n> mount. To re-identify, update the universal install — not the embed\n> config.\n\n### Callback events\n\n`initEmbedWidget(config, callback)` and `useEmbedWidget(config, callback)`\nboth accept an optional callback that fires for runtime lifecycle events\nand route changes from inside the iframe:\n\n| `evt.action` | When |\n|---|---|\n| `'loaded'` | Iframe document has loaded but the embed UI is still booting. |\n| `'widgetReady'` | Embed UI is fully booted and interactive. |\n| `'routeChange'` | User navigated inside the iframe (carries `path` / `url`). |\n\nFuture actions are surfaced verbatim — the runtime forwards anything new\nwithout an SDK upgrade, so it's safe to switch on `evt.action` with a\ndefault branch.\n\n```ts\ninitEmbedWidget(config, (err, evt) => {\n  if (err) return console.error('[embed]', err);\n  switch (evt?.action) {\n    case 'widgetReady':\n      analytics.track('embed_ready');\n      break;\n    case 'routeChange':\n      analytics.track('embed_navigate', { path: evt.path });\n      break;\n  }\n});\n```\n\n---\n\n# 7. Common patterns\n\n## Login\n\n### React — automatic\n\nThe Provider re-identifies on its own. Just keep its props in sync with\nyour auth state.\n\n```tsx\nfunction Root() {\n  const { user } = useAuth();\n  return (\n    <FeaturebaseProvider\n      appId=\"org_xyz\"\n      featurebaseJwt={user?.featurebaseJwt}\n    >\n      <App />\n    </FeaturebaseProvider>\n  );\n}\n```\n\nWhen `user` becomes truthy, the SDK re-identifies the visitor without\nre-rendering the messenger, and widget hooks pick up the new identity\nautomatically.\n\n### Vanilla\n\nJust re-call `Featurebase()` with the new identity. Idempotent —\nre-runs `identify` for the messenger and refreshes widget defaults in\nthe same call. Same one-field shape as setup:\n\n```ts\nimport Featurebase from 'featurebase-js';\n\nasync function onLogin(user) {\n  Featurebase({\n    appId: 'abc123',\n    featurebaseJwt: user.featurebaseJwt,\n  });\n}\n```\n\n## Logout\n\n```tsx\n// React — either conditionally render the Provider…\n{user ? (\n  <FeaturebaseProvider {...identity}><App /></FeaturebaseProvider>\n) : (\n  <App />\n)}\n\n// …or call shutdown explicitly from useFeaturebase\nconst { shutdown } = useFeaturebase();\nfunction onLogout() {\n  shutdown();\n}\n```\n\n```ts\n// Vanilla — tear down whatever you booted\nimport { shutdown, clearConfig } from 'featurebase-js';\n\nfunction onLogout() {\n  shutdown();         // messenger (skip if you didn't boot one)\n  clearConfig();      // widget identity defaults\n}\n```\n\n## Themed messenger that follows the app\n\n```ts\nimport { setTheme } from 'featurebase-js';\n\nconst mql = window.matchMedia('(prefers-color-scheme: dark)');\nsetTheme(mql.matches ? 'dark' : 'light');\nmql.addEventListener('change', (e) => setTheme(e.matches ? 'dark' : 'light'));\n```\n\n## Per-route widgets in an SPA\n\n```ts\n// React — automatic, useFeedbackWidget tears down on unmount\n{onFeedbackPage && <FeedbackHost />}\n\n// Vanilla\nrouter.beforeEach((to, from, next) => {\n  if (from.path.startsWith('/app')) destroyFeedback();\n  if (to.path.startsWith('/app'))\n    initFeedback({ theme: 'light', placement: 'bottom-right' });\n  next();\n});\n```\n\n---\n\n# 8. Identifying users (JWT)\n\n> Skip this section if you only need anonymous use. Every surface in\n> Sections 2–6 render fine without it.\n\nIdentifying a user lets Featurebase tie messenger conversations,\nfeedback posts, changelog reads, and survey responses to a real contact\nin your dashboard — and unlocks user-attribute targeting (e.g. only show\nthis survey to `plan: 'pro'` users). Featurebase secures this with\n**JWTs signed on your server**: without a valid JWT, the runtime refuses\nto identify the user and you'll see a console warning.\n\n> 📚 Full guide: [Secure your installation](https://help.featurebase.app/en/help/articles/5402549-secure-your-installation-required-by-default)\n> · [Creating and signing a JWT](https://help.featurebase.app/en/articles/5257986-creating-and-signing-a-jwt)\n\n## 8.1. Get your JWT secret\n\nDashboard → **Settings → Access & Security → Security**. Copy the\nprivate key into a server-side env var. **Never expose it to the\nbrowser.**\n\n```bash\nFEATUREBASE_JWT_SECRET=…\n```\n\n## 8.2. Generate the JWT on your server\n\nSign every authenticated user's profile data with HS256. Both `email`\nand `userId` should be included when you have them; one of the two is\nrequired.\n\n```ts\n// Node / your backend\nimport jwt from 'jsonwebtoken';\n\nfunction generateFeaturebaseJwt(user) {\n  return jwt.sign(\n    {\n      // Identity (one of email/userId is required)\n      userId: user.id,\n      email: user.email,\n      name: user.name,\n      profilePicture: user.avatarUrl,\n\n      // Optional custom attributes — must be configured in dashboard first\n      plan: user.plan,\n      title: user.jobTitle,\n\n      // Optional company associations\n      companies: [\n        {\n          id: user.companyId,           // required\n          name: user.companyName,        // required\n          monthlySpend: 500,             // optional\n          createdAt: '2023-05-19T15:35:49.915Z',\n        },\n      ],\n    },\n    process.env.FEATUREBASE_JWT_SECRET,\n    { algorithm: 'HS256' },\n  );\n}\n```\n\n## 8.3. Pass it once at setup\n\nSend the resulting string to the browser via your normal API / SSR\nprops, then pass it as `featurebaseJwt` once in your [Section 1 setup\ncall](#1-setup). The SDK auto-propagates it to every surface (messenger\n+ widgets):\n\n```ts\n// React: identity flows through one Provider tag\n<FeaturebaseProvider featurebaseJwt={user.featurebaseJwt} {...rest}>...</FeaturebaseProvider>\n\n// Vanilla: identity flows through one Featurebase() call\nFeaturebase({ featurebaseJwt: user.featurebaseJwt, ...rest });\n```\n\nThe JWT carries `userId`, `email`, `name`, custom attributes, and\ncompanies, so you don't need to pass them as separate props (and\nshouldn't — anything you'd want secured belongs inside the JWT).\n\n## 8.4. Validate it works\n\nDashboard → **Settings → Access & Security → Security → Validate JWT**.\nPaste a freshly-generated token to confirm everything is signed\ncorrectly before going to production.\n\n> ⚠️ For local testing only, you can disable secure login in the\n> dashboard. **Never** ship to production with it disabled — anyone\n> could impersonate any user.\n\nFor login / logout flows that re-identify or tear down the visitor, see\n[Section 7 — Common patterns](#7-common-patterns).\n\n---\n\n# React reference\n\n## `<FeaturebaseProvider>`\n\nSee [Setup → Path A](#path-a--react) for the full props table.\n\nWhat it does on mount/unmount/re-render:\n\n- **Mount** (with `appId`): resolves `appId → { slug, modules }` (cached\n  aggressively), boots the messenger when **General → Manage modules**\n  has Support enabled (or `messenger` prop is `true`), subscribes to the\n  unread-count stream, and exposes identity to descendants via context.\n- **Re-render with new identity props**: dispatches `identify`. Messenger\n  iframe stays mounted; auth token refreshes.\n- **Unmount**: dispatches `shutdown()`.\n- **Errors**: any throw from the runtime is caught and logged with\n  `[featurebase-js]` prefix. It never bubbles into your React tree.\n\n## `useFeaturebase()`\n\nImperative + reactive access to the messenger. Methods are stable\nreferences; only `unreadCount` triggers re-renders.\n\n```ts\nconst {\n  show, hide, showSpace, showMessages,\n  showArticle, showChangelog, showNews, showConversation, showNewMessage,\n  setTheme, setLanguage,\n  update, shutdown,\n  unreadCount,\n} = useFeaturebase();\n```\n\n## Widget hooks\n\n| Hook | Returns | Tears down on unmount? |\n|---|---|---|\n| `useChangelogWidget(config)` | `{ openPopup, markAllAsViewed, getUnviewedCount }` | Yes |\n| `useFeedbackWidget(config)` | `void` | Yes |\n| `useSurveyWidget(config?)` | `void` | No (runtime has no destroy) |\n| `useEmbedWidget(config, callback?)` | `void` | Yes — via the host element's lifecycle |\n\nAll three:\n\n- Inherit the resolved org slug and identity (`featurebaseJwt`,\n  `jwtToken`, plus non-secured `userId` / `email` when explicitly passed)\n  from `<FeaturebaseProvider>` context.\n- Re-init only when the **resolved** config (after merge with context)\n  meaningfully changes — passing inline object literals doesn't thrash.\n- Tolerate the brief tick where the resolver hasn't returned the slug\n  yet: the underlying `init*()` queues itself and fires once the slug\n  lands.\n- Wrap the runtime call in try/catch — any runtime error (including a\n  truly unresolvable slug) is logged with `[featurebase-js]` prefix and\n  never crashes the React tree.\n\n---\n\n# Vanilla reference\n\n## `Featurebase(settings)` — unified entry point\n\nDefault export. Pass `appId` and the SDK does the right thing:\n\n| `settings` contains | Behavior |\n|---|---|\n| `appId` only | Resolves `appId → { slug, modules }`, sets widget defaults from the slug, and boots the messenger when `modules.support === true`. |\n| `appId, messenger: false` | Same as above, but skips the messenger boot. |\n| Missing `appId` | Throws — `appId` is required. |\n\nSafe to call inside React effects or any re-running lifecycle hook.\n`featurebaseJwt` (and other identity fields) sync to both surfaces in\none shot.\n\n## `boot(settings)` — strict messenger-only\n\nLower-level than `Featurebase()`. Requires `appId`, only boots the\nmessenger, **never** mutates the global widget-defaults state, and\n**never** consults the module-flag autoboot logic. Use this when you\nwant to bypass the resolver entirely and force a messenger boot.\n\n## `resolveOrganization(appId)` — sync helper (advanced)\n\nReturns a `Promise<OrganizationInfo>` with the org's `slug`,\n`displayName`, and per-product `modules` flags. Cached in-memory and via\n`localStorage` (1h TTL). Most callers don't need to invoke this directly\n— `Featurebase()` does it for you. Useful when you want to inspect\n`modules` before mounting your own UI.\n\n## `update(settings?)` — re-identify without rebooting\n\nDispatches `identify` directly. Reuses the previously booted `appId` if\nomitted.\n\n## `shutdown()` — tear everything down\n\nRemoves the messenger DOM/iframe, kills the websocket, drops cached\nidentity. Next `Featurebase({ appId })` call fully reboots.\n\n## `whenReady(cb)` — defer until boot finishes\n\nThe runtime's boot is async. Subscribe to events from inside `whenReady`\nto avoid the \"Please call 'boot' first\" warning.\n\n## `configure(defaults)` — widget defaults (advanced)\n\nLower-level than `Featurebase()`. Sets widget identity defaults\ndirectly. Most callers don't need this — `Featurebase({ appId })`\nalready syncs identity defaults and resolves the workspace slug. Use\n`configure()` only when you want to refresh widget identity without\nre-running the appId resolver.\n\n```ts\nimport Featurebase, { configure, clearConfig, getWidgetDefaults } from 'featurebase-js';\n\nFeaturebase({ appId: 'abc123', messenger: false });\nconfigure({ featurebaseJwt: 'jwt_v1' });\nconfigure({ featurebaseJwt: 'jwt_v2' });        // merges (last-write-wins per key)\ngetWidgetDefaults();                             // { featurebaseJwt: 'jwt_v2' }\nclearConfig();                                   // wipes identity + resolved workspace slug (use on logout)\n```\n\n`WidgetDefaults` shape:\n\n| Field | Type | Notes |\n|---|---|---|\n| `featurebaseJwt` | `string` | **Recommended.** Server-signed JWT carrying identity. |\n| `jwtToken` | `string` | Pre-minted JWT (advanced). |\n| `userId`, `email` | `string` | Only when configured as non-secured in dashboard. Prefer the JWT. |\n| `locale` | `FeaturebaseLocale` | Default UI locale for every widget. |\n\nBehavior:\n\n- **Explicit fields on the call always win** over `configure()` defaults.\n- **Resolver-aware** — `init*()` calls made before `Featurebase({ appId })`'s\n  resolver settles are queued and dispatched once the slug lands. If no\n  `Featurebase({ appId })` call ever initializes the workspace, the call\n  throws on the next attempt with a clear message naming the caller.\n- **SSR-safe** — every function is a no-op when `window` is undefined.\n\n---\n\n# TypeScript\n\nEvery config is a typed interface; importing them is encouraged for\nshared component props:\n\n```ts\nimport type {\n  // Messenger\n  FeaturebaseSettings,\n  FeaturebaseCompany,\n  ThemeMode,\n  Space,\n  // Widgets\n  ChangelogWidgetConfig,\n  ChangelogWidgetConfigInput,    // workspace slug is resolved from appId\n  FeedbackWidgetConfig,\n  FeedbackWidgetConfigInput,\n  SurveyWidgetConfig,\n  SurveyWidgetConfigInput,\n  FeaturebaseEmbedWidgetConfig,\n  FeaturebaseEmbedWidgetConfigInput,\n  FeaturebaseEmbedAction,\n  FeaturebaseEmbedCallback,\n  // Shared\n  FeaturebaseLocale,\n  FeaturebaseWidgetIdentity,\n  WidgetDefaults,\n} from 'featurebase-js';\n```\n\nForward-compat: every config has an index signature\n(`[key: string]: unknown`) so future runtime fields are accepted without\nneeding an SDK upgrade.\n\n## `FeaturebaseLocale`\n\nBCP-47 short codes accepted by every surface. Unknown codes are\nforwarded to the runtime verbatim.\n\n```\nbn  bs  pt-BR  bg  ca  hr  cs  da  nl  en  et  fi  fr  de  el  hi\nhu  id  it  ja  ko  lv  lt  ms  mn  nb  pl  pt  ro  ru  sr  zh-CN\nsl  es  sw  sv  th  zh-TW  tr  uk  vi  sk\n```\n\n---\n\n# SSR / Next.js\n\nEvery export is a no-op when `window` is undefined, so server-side\nimports are safe:\n\n```tsx\n// app/page.tsx — Server Component, runs on the server\nimport Featurebase from 'featurebase-js';\n\nFeaturebase({ appId: 'org_xyz' });   // safe — silently no-ops\n```\n\nFor real client-side boot, use the React Provider in a `'use client'`\nboundary (recommended) or call `Featurebase()` from a `useEffect`.\n\n```tsx\n'use client';\nimport { FeaturebaseProvider } from 'featurebase-js/react';\n\nexport function FeaturebaseRoot({ children }) {\n  return (\n    <FeaturebaseProvider appId=\"org_xyz\">{children}</FeaturebaseProvider>\n  );\n}\n```\n\n---\n\n# Browser support\n\nModern evergreen browsers (Chrome / Edge / Firefox / Safari, last 2\nversions). The bundle ships ES2018; no polyfills required.\n\n# Versioning\n\nThe SDK follows [semantic versioning](https://semver.org/) from `1.0.0`\nonward: breaking changes only land in major bumps (`2.0.0`, `3.0.0`, …),\nnew features in minor bumps (`1.x.0`), and fixes in patch bumps\n(`1.0.x`). Pre-release builds publish under the `next` and `beta`\ndist-tags so production installs of `latest` stay stable until a release\nis promoted.\n\n# Source / contributing\n\nThe wrapper is the public surface of the\n[Featurebase runtime](https://github.com/FeaturebaseHQ/sdk). To file a\nbug or request, open a ticket from the in-app chat at\n[featurebase.app](https://featurebase.app) — that lands directly in our\ndashboard.\n\n# License\n\nMIT © Featurebase\n","readmeFilename":"README.md"}