{"_id":"@drytis/meeting-sdk","_rev":"19-dca41ec407d3a14aa3edaed0873c73e8","name":"@drytis/meeting-sdk","dist-tags":{"latest":"1.0.18"},"versions":{"0.1.0":{"name":"@drytis/meeting-sdk","version":"0.1.0","keywords":["drytis","meeting","livekit","planning","lifeguard","sdk","react"],"license":"MIT","_id":"@drytis/meeting-sdk@0.1.0","maintainers":[{"name":"drytis","email":"hikmat@drytis.com"}],"homepage":"https://github.com/drytis/meeting-sdk#readme","bugs":{"url":"https://github.com/drytis/meeting-sdk/issues"},"dist":{"shasum":"c70830166027d2f395e0f238c7cd70bbaf25d748","tarball":"https://registry.npmjs.org/@drytis/meeting-sdk/-/meeting-sdk-0.1.0.tgz","fileCount":8,"integrity":"sha512-yNrPLVBvV44VWYasULZsMk1TdnTQXNem3h/aRbkkQqYaTU6Cl9fT/FO7Xma3uWoLtn6oxveE0PlUMiIxSrz5tg==","signatures":[{"sig":"MEUCIQDQDubJXeWFELMyaH1Xm17IFasbchioJXxfTamaRw93ygIgYjlTKvQamLDacnUMGD3JbGL/TPF4Xj+muIk9dWe8oa4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":258802},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","gitHead":"46fe1daf8deaf51f12b151e880a426a89b2c294d","scripts":{"dev":"tsup --watch","build":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"drytis","email":"hikmat@drytis.com"},"repository":{"url":"git+https://github.com/drytis/meeting-sdk.git","type":"git"},"_npmVersion":"10.9.3","description":"React TypeScript SDK for Drytis Planning and Lifeguard meeting sessions","directories":{},"_nodeVersion":"22.20.0","dependencies":{"livekit-client":"^2.8.3"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","typescript":"^5","@types/react":"^19","@types/react-dom":"^19"},"peerDependencies":{"react":">=18","react-dom":">=18"},"_npmOperationalInternal":{"tmp":"tmp/meeting-sdk_0.1.0_1775679112828_0.9093370225678086","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@drytis/meeting-sdk","version":"0.2.0","keywords":["drytis","meeting","livekit","planning","lifeguard","sdk","react"],"license":"MIT","_id":"@drytis/meeting-sdk@0.2.0","maintainers":[{"name":"drytis","email":"hikmat@drytis.com"}],"homepage":"https://github.com/drytis/meeting-sdk#readme","bugs":{"url":"https://github.com/drytis/meeting-sdk/issues"},"dist":{"shasum":"f009e37b596714f7ad048a38eb6a325feaf7de5c","tarball":"https://registry.npmjs.org/@drytis/meeting-sdk/-/meeting-sdk-0.2.0.tgz","fileCount":8,"integrity":"sha512-E67eMWRq0TXYTTV6VjzeW5AR3TzD9gO1cvwyPpi/Knz0+jp2TNHO2peMaLKhsTIp7+0/zHQVyZ0S5MzFOlCNEQ==","signatures":[{"sig":"MEYCIQCvue8eRUyDxlWntJusSijtPD6EZO8agBv9Akgyo9VRZAIhAJbXFoM6BUe2Y02OUO4yidS1tnzbFT77IUT5MmCY02YI","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":353051},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","gitHead":"46fe1daf8deaf51f12b151e880a426a89b2c294d","scripts":{"dev":"tsup --watch","build":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"drytis","email":"hikmat@drytis.com"},"repository":{"url":"git+https://github.com/drytis/meeting-sdk.git","type":"git"},"_npmVersion":"10.9.3","description":"React TypeScript SDK for Drytis Planning and Lifeguard meeting sessions","directories":{},"_nodeVersion":"22.20.0","dependencies":{"livekit-client":"^2.8.3"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","typescript":"^5","@types/react":"^19","@types/react-dom":"^19"},"peerDependencies":{"react":">=18","react-dom":">=18"},"_npmOperationalInternal":{"tmp":"tmp/meeting-sdk_0.2.0_1775807723237_0.3475722359652589","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@drytis/meeting-sdk","version":"0.3.0","keywords":["drytis","meeting","livekit","planning","lifeguard","sdk","react"],"license":"MIT","_id":"@drytis/meeting-sdk@0.3.0","maintainers":[{"name":"drytis","email":"hikmat@drytis.com"}],"homepage":"https://github.com/drytis/meeting-sdk#readme","bugs":{"url":"https://github.com/drytis/meeting-sdk/issues"},"dist":{"shasum":"07dcfb6140050e4cb77f4d552d251b3e5c65fa17","tarball":"https://registry.npmjs.org/@drytis/meeting-sdk/-/meeting-sdk-0.3.0.tgz","fileCount":8,"integrity":"sha512-MSw/P4DEkeAgnW2/do1XJThkHQfZ2gPO2yEaX7q0XOML0hFPAxMKH8CH1QDm+j2MTKvmDwPDjHf6MFDkL8+iPA==","signatures":[{"sig":"MEYCIQCAOriILlajNsIT1nsZhdLQg6VfI6OHcxETXo93hHh8sAIhAP11B7H+yVqfAiyVebr07dNO6SC94sGWginWDbWFbjzV","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":258797},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","gitHead":"6014182a6bd6ead33a35128593e3ae1166251b6c","scripts":{"dev":"tsup --watch","build":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"drytis","email":"hikmat@drytis.com"},"repository":{"url":"git+https://github.com/drytis/meeting-sdk.git","type":"git"},"_npmVersion":"10.9.3","description":"React TypeScript SDK for Drytis Planning and Lifeguard meeting sessions","directories":{},"_nodeVersion":"22.20.0","dependencies":{"livekit-client":"^2.8.3"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","typescript":"^5","@types/react":"^19","@types/react-dom":"^19"},"peerDependencies":{"react":">=18","react-dom":">=18"},"_npmOperationalInternal":{"tmp":"tmp/meeting-sdk_0.3.0_1776445805900_0.13097028342578154","host":"s3://npm-registry-packages-npm-production"}},"1.0.4":{"name":"@drytis/meeting-sdk","version":"1.0.4","keywords":["drytis","meeting","livekit","planning","lifeguard","sdk","react"],"license":"MIT","_id":"@drytis/meeting-sdk@1.0.4","maintainers":[{"name":"drytis","email":"hikmat@drytis.com"}],"homepage":"https://github.com/drytis/meeting-sdk#readme","bugs":{"url":"https://github.com/drytis/meeting-sdk/issues"},"dist":{"shasum":"e24bdea4312ebc31be34820705be69e568606a49","tarball":"https://registry.npmjs.org/@drytis/meeting-sdk/-/meeting-sdk-1.0.4.tgz","fileCount":8,"integrity":"sha512-9HLqTW8nspOWGn1kS93OsqsfxonctaEKKJQfJ67ksVOlCre437aD0P/QyyLRT2tMguKbeZhO8OeDvBW+YS9ElA==","signatures":[{"sig":"MEUCIQCRVBaFjk/h3nkQVaS1ZiFv+XGsolvOu9YeMS8k/1iVcgIgY2eRGMb2jAN8mE1UDQyGfDa1ME+0C7NLC/V9jVJ4FnU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":267002},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","gitHead":"9548f1911d36a6a0783a18480dbba0a0b8adb2ae","scripts":{"dev":"tsup --watch","build":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"drytis","email":"hikmat@drytis.com"},"repository":{"url":"git+https://github.com/drytis/meeting-sdk.git","type":"git"},"_npmVersion":"10.9.3","description":"React TypeScript SDK for Drytis Planning and Lifeguard meeting sessions","directories":{},"_nodeVersion":"22.20.0","dependencies":{"livekit-client":"^2.8.3"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","typescript":"^5","@types/react":"^19","@types/react-dom":"^19"},"peerDependencies":{"react":">=18","react-dom":">=18"},"_npmOperationalInternal":{"tmp":"tmp/meeting-sdk_1.0.4_1776449987532_0.07905002347160472","host":"s3://npm-registry-packages-npm-production"}},"1.0.5":{"name":"@drytis/meeting-sdk","version":"1.0.5","keywords":["drytis","meeting","livekit","planning","lifeguard","sdk","react"],"license":"MIT","_id":"@drytis/meeting-sdk@1.0.5","maintainers":[{"name":"drytis","email":"hikmat@drytis.com"}],"homepage":"https://github.com/drytis/meeting-sdk#readme","bugs":{"url":"https://github.com/drytis/meeting-sdk/issues"},"dist":{"shasum":"e4563a741e337636007ef719eca09cbd1b16db91","tarball":"https://registry.npmjs.org/@drytis/meeting-sdk/-/meeting-sdk-1.0.5.tgz","fileCount":8,"integrity":"sha512-BBGe+NFRZfI+9MFCkL13ztcOXTjUW9regci6TKg3KXMWswoWZu385ISnl0AIgwBofZXSKUxBoqu1acP/U8UfFw==","signatures":[{"sig":"MEQCIBx0qVjm/EKCvirNitIMSiiXQ/7gjOPw0YDyJHTIrBSLAiBVPObKGhHH+oKXMbIcSyN6GBwoOiD6MPg3CRteh1Qi5g==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":283422},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","gitHead":"9548f1911d36a6a0783a18480dbba0a0b8adb2ae","scripts":{"dev":"tsup --watch","build":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"drytis","email":"hikmat@drytis.com"},"repository":{"url":"git+https://github.com/drytis/meeting-sdk.git","type":"git"},"_npmVersion":"10.9.3","description":"React TypeScript SDK for Drytis Planning and Lifeguard meeting sessions","directories":{},"_nodeVersion":"22.20.0","dependencies":{"livekit-client":"^2.8.3"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","typescript":"^5","@types/react":"^19","@types/react-dom":"^19"},"peerDependencies":{"react":">=18","react-dom":">=18"},"_npmOperationalInternal":{"tmp":"tmp/meeting-sdk_1.0.5_1776450809324_0.4498107042974091","host":"s3://npm-registry-packages-npm-production"}},"1.0.6":{"name":"@drytis/meeting-sdk","version":"1.0.6","keywords":["drytis","meeting","livekit","planning","lifeguard","sdk","react"],"license":"MIT","_id":"@drytis/meeting-sdk@1.0.6","maintainers":[{"name":"drytis","email":"hikmat@drytis.com"}],"homepage":"https://github.com/drytis/meeting-sdk#readme","bugs":{"url":"https://github.com/drytis/meeting-sdk/issues"},"dist":{"shasum":"a9259f464078d2a569f5b77cee3d45fb743a08fe","tarball":"https://registry.npmjs.org/@drytis/meeting-sdk/-/meeting-sdk-1.0.6.tgz","fileCount":8,"integrity":"sha512-5A7q1O1gwN/Q0Z5T1QmSjtirj8EJ31eQYl7/U1ItoOKklMOJekEYQwsdCdFUpMZdatQRUjNtQhmZ8+fGKJ0SPQ==","signatures":[{"sig":"MEUCIH2gCpIahIUz++UGlHJ9RuiJOrwKibziTtdbrQsvgFaUAiEAukYXprxe5IQQ6lKPWrFy8+CmRsMVVXwWFBeNsucTXLU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":282092},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","gitHead":"9548f1911d36a6a0783a18480dbba0a0b8adb2ae","scripts":{"dev":"tsup --watch","build":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"drytis","email":"hikmat@drytis.com"},"repository":{"url":"git+https://github.com/drytis/meeting-sdk.git","type":"git"},"_npmVersion":"10.9.3","description":"React TypeScript SDK for Drytis Planning and Lifeguard meeting sessions","directories":{},"_nodeVersion":"22.20.0","dependencies":{"livekit-client":"^2.8.3"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","typescript":"^5","@types/react":"^19","@types/react-dom":"^19"},"peerDependencies":{"react":">=18","react-dom":">=18"},"_npmOperationalInternal":{"tmp":"tmp/meeting-sdk_1.0.6_1776462354445_0.15374750893860778","host":"s3://npm-registry-packages-npm-production"}},"1.0.7":{"name":"@drytis/meeting-sdk","version":"1.0.7","keywords":["drytis","meeting","livekit","planning","lifeguard","sdk","react"],"license":"MIT","_id":"@drytis/meeting-sdk@1.0.7","maintainers":[{"name":"drytis","email":"hikmat@drytis.com"}],"homepage":"https://github.com/drytis/meeting-sdk#readme","bugs":{"url":"https://github.com/drytis/meeting-sdk/issues"},"dist":{"shasum":"36d84aa6df480b94c025f1a4a00144035a151604","tarball":"https://registry.npmjs.org/@drytis/meeting-sdk/-/meeting-sdk-1.0.7.tgz","fileCount":8,"integrity":"sha512-1CnenhR+lQ4Zy1Ks7E3zKXfxppyoQ+Ec9ugUAP/ZfK9DV0ddfNOeUCkp22jjptswfksM/9yE0BgkJkvXUN78Cg==","signatures":[{"sig":"MEUCIQDVA09qTcSq0mSrEPsKm7pJT5dRMYLSxvg437RnNKID4wIgTcRWlOJc2bH2I9ZrzKNzkrfpkR89dabKCMGySLke2zA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":282425},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","gitHead":"9548f1911d36a6a0783a18480dbba0a0b8adb2ae","scripts":{"dev":"tsup --watch","build":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"drytis","email":"hikmat@drytis.com"},"repository":{"url":"git+https://github.com/drytis/meeting-sdk.git","type":"git"},"_npmVersion":"10.9.3","description":"React TypeScript SDK for Drytis Planning and Lifeguard meeting sessions","directories":{},"_nodeVersion":"22.20.0","dependencies":{"livekit-client":"^2.8.3"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","typescript":"^5","@types/react":"^19","@types/react-dom":"^19"},"peerDependencies":{"react":">=18","react-dom":">=18"},"_npmOperationalInternal":{"tmp":"tmp/meeting-sdk_1.0.7_1776462529989_0.46097514160872044","host":"s3://npm-registry-packages-npm-production"}},"1.0.8":{"name":"@drytis/meeting-sdk","version":"1.0.8","keywords":["drytis","meeting","livekit","planning","lifeguard","sdk","react"],"license":"MIT","_id":"@drytis/meeting-sdk@1.0.8","maintainers":[{"name":"drytis","email":"hikmat@drytis.com"}],"homepage":"https://github.com/drytis/meeting-sdk#readme","bugs":{"url":"https://github.com/drytis/meeting-sdk/issues"},"dist":{"shasum":"7e969d9762860aa05c69430fd6dae20b63bddffb","tarball":"https://registry.npmjs.org/@drytis/meeting-sdk/-/meeting-sdk-1.0.8.tgz","fileCount":8,"integrity":"sha512-A0yEn7Segdu5RuVii53HbAKZDN4b+7d2yeyBhlmG7zFgadRTQgDKpO/Hew7X/wd45hSCX4PaLYv+XjxgoXmGiQ==","signatures":[{"sig":"MEUCIQD+EPsc7CdzeGAJqfja7Kys71b2XF65Sec8lZcoFF9RswIgfBVnpbehDa0crHhcWmPxTeLJ68WnSxIt682OCNhcZ6U=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":248616},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","gitHead":"9548f1911d36a6a0783a18480dbba0a0b8adb2ae","scripts":{"dev":"tsup --watch","build":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"drytis","email":"hikmat@drytis.com"},"repository":{"url":"git+https://github.com/drytis/meeting-sdk.git","type":"git"},"_npmVersion":"10.9.3","description":"React TypeScript SDK for Drytis Planning and Lifeguard meeting sessions","directories":{},"_nodeVersion":"22.20.0","dependencies":{"livekit-client":"^2.8.3"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","typescript":"^5","@types/react":"^19","@types/react-dom":"^19"},"peerDependencies":{"react":">=18","react-dom":">=18"},"_npmOperationalInternal":{"tmp":"tmp/meeting-sdk_1.0.8_1776464231725_0.20789769389091406","host":"s3://npm-registry-packages-npm-production"}},"1.0.9":{"name":"@drytis/meeting-sdk","version":"1.0.9","keywords":["drytis","meeting","livekit","planning","lifeguard","sdk","react"],"license":"MIT","_id":"@drytis/meeting-sdk@1.0.9","maintainers":[{"name":"drytis","email":"hikmat@drytis.com"}],"homepage":"https://github.com/drytis/meeting-sdk#readme","bugs":{"url":"https://github.com/drytis/meeting-sdk/issues"},"dist":{"shasum":"f809111b7017285e5412df400921cc04acebe9d2","tarball":"https://registry.npmjs.org/@drytis/meeting-sdk/-/meeting-sdk-1.0.9.tgz","fileCount":8,"integrity":"sha512-wSVgxOyrBZnVQ1X/zCcsc5rAnkYb8cfE50lA9A040GwWNue5is0iF+r9RJ+gx+YSHKnJILhVJfFgiSMJgheH3g==","signatures":[{"sig":"MEQCIG32hGChjGvU5NrRC8QDTXjVcMFsyK8yHtblFTjiYzALAiBN2zzIJ7tn+zZCJXY666yW5r5medYKC7iWxAsQe8O/Hg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":237194},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","gitHead":"9548f1911d36a6a0783a18480dbba0a0b8adb2ae","scripts":{"dev":"tsup --watch","build":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"drytis","email":"hikmat@drytis.com"},"repository":{"url":"git+https://github.com/drytis/meeting-sdk.git","type":"git"},"_npmVersion":"10.9.3","description":"React TypeScript SDK for Drytis Planning and Lifeguard meeting sessions","directories":{},"_nodeVersion":"22.20.0","dependencies":{"livekit-client":"^2.8.3"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","typescript":"^5","@types/react":"^19","@types/react-dom":"^19"},"peerDependencies":{"react":">=18","react-dom":">=18"},"_npmOperationalInternal":{"tmp":"tmp/meeting-sdk_1.0.9_1776464492422_0.32430246290804066","host":"s3://npm-registry-packages-npm-production"}},"1.0.10":{"name":"@drytis/meeting-sdk","version":"1.0.10","keywords":["drytis","meeting","livekit","planning","lifeguard","sdk","react"],"license":"MIT","_id":"@drytis/meeting-sdk@1.0.10","maintainers":[{"name":"drytis","email":"hikmat@drytis.com"}],"homepage":"https://github.com/drytis/meeting-sdk#readme","bugs":{"url":"https://github.com/drytis/meeting-sdk/issues"},"dist":{"shasum":"9d687d1dfab64199eb7e035d0f27b4b8160cc58e","tarball":"https://registry.npmjs.org/@drytis/meeting-sdk/-/meeting-sdk-1.0.10.tgz","fileCount":8,"integrity":"sha512-nXvMpUEFwYeONfyZojaGHFUnuvpkTeN60Qs7mUmx8MJhhrXMrKXmZDycOofKpmWkFuExIvj6rn/nRrR499jZdQ==","signatures":[{"sig":"MEQCIF0U2enkwvNMt1Fc92Otzqa4BOHPnuv+8jNkr2uoJfQIAiBqipCmqtnQuZI2BiQB5vnLygy6fI73bRmh24/pKJLWiQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":243568},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","gitHead":"9548f1911d36a6a0783a18480dbba0a0b8adb2ae","scripts":{"dev":"tsup --watch","build":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"drytis","email":"hikmat@drytis.com"},"repository":{"url":"git+https://github.com/drytis/meeting-sdk.git","type":"git"},"_npmVersion":"10.9.3","description":"React TypeScript SDK for Drytis Planning and Lifeguard meeting sessions","directories":{},"_nodeVersion":"22.20.0","dependencies":{"livekit-client":"^2.8.3"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","typescript":"^5","@types/react":"^19","@types/react-dom":"^19"},"peerDependencies":{"react":">=18","react-dom":">=18"},"_npmOperationalInternal":{"tmp":"tmp/meeting-sdk_1.0.10_1776464690905_0.9582520569827","host":"s3://npm-registry-packages-npm-production"}},"1.0.11":{"name":"@drytis/meeting-sdk","version":"1.0.11","keywords":["drytis","meeting","livekit","planning","lifeguard","sdk","react"],"license":"MIT","_id":"@drytis/meeting-sdk@1.0.11","maintainers":[{"name":"drytis","email":"hikmat@drytis.com"}],"dist":{"shasum":"13e63e4f82383f466ef433232a122a5e2030fa47","tarball":"https://registry.npmjs.org/@drytis/meeting-sdk/-/meeting-sdk-1.0.11.tgz","fileCount":8,"integrity":"sha512-Hhlf7kR1b989MB0888kHeguQG3gYozCwBsNpzDmuC5TXgn6BtvZIv1C2t7St0EswwHPA/duGHtVD+6Xcu0fxZw==","signatures":[{"sig":"MEUCIQDFk0m5E1OqgQpyoqCrxVxBPHF1E7KE/vn0xQ056aHbCQIgL3TkeGn9hdlffHcxD3+CgAgtNxb1OnEuB5woiYTOREU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":243571},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","gitHead":"9548f1911d36a6a0783a18480dbba0a0b8adb2ae","scripts":{"dev":"tsup --watch","build":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"drytis","email":"hikmat@drytis.com"},"repository":{"url":"https://git.drytis.ai/hikmat/meeting-sdk","type":"git"},"_npmVersion":"10.9.3","description":"React TypeScript SDK for Drytis Planning and Lifeguard meeting sessions","directories":{},"_nodeVersion":"22.20.0","dependencies":{"livekit-client":"^2.8.3"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","typescript":"^5","@types/react":"^19","@types/react-dom":"^19"},"peerDependencies":{"react":">=18","react-dom":">=18"},"_npmOperationalInternal":{"tmp":"tmp/meeting-sdk_1.0.11_1776467626438_0.935400483745088","host":"s3://npm-registry-packages-npm-production"}},"1.0.12":{"name":"@drytis/meeting-sdk","version":"1.0.12","keywords":["drytis","meeting","livekit","planning","lifeguard","sdk","react"],"license":"MIT","_id":"@drytis/meeting-sdk@1.0.12","maintainers":[{"name":"drytis","email":"hikmat@drytis.com"}],"dist":{"shasum":"91610634d8569097ecca0c5b48bf78baa412791c","tarball":"https://registry.npmjs.org/@drytis/meeting-sdk/-/meeting-sdk-1.0.12.tgz","fileCount":8,"integrity":"sha512-6U13EQY9HwgHgZudulicz095HcFnN+q1OEIUrnfVsS3GloVQFfsQ0vRFC2W/ppoAN6WIpIAQj+I9vEYI1w3FCg==","signatures":[{"sig":"MEYCIQCv7Hpq5nxpEH4LzEif90vpGNmzXX9kgMKp373+mF7wAwIhANBTcN2n3IkD8qzj0OX0dMS+jipyC6ykUjgYHEezZjK1","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":273482},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","gitHead":"291eb27e02b36dc937d4eaba382046537154acd7","scripts":{"dev":"tsup --watch","build":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"drytis","email":"hikmat@drytis.com"},"repository":{"url":"https://git.drytis.ai/hikmat/meeting-sdk","type":"git"},"_npmVersion":"10.9.3","description":"React TypeScript SDK for Drytis Planning and Lifeguard meeting sessions","directories":{},"_nodeVersion":"22.20.0","dependencies":{"livekit-client":"^2.8.3"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","typescript":"^5","@types/react":"^19","@types/react-dom":"^19"},"peerDependencies":{"react":">=18","react-dom":">=18"},"_npmOperationalInternal":{"tmp":"tmp/meeting-sdk_1.0.12_1777490867243_0.02998213374999059","host":"s3://npm-registry-packages-npm-production"}},"1.0.13":{"name":"@drytis/meeting-sdk","version":"1.0.13","keywords":["drytis","meeting","livekit","planning","lifeguard","sdk","react"],"license":"MIT","_id":"@drytis/meeting-sdk@1.0.13","maintainers":[{"name":"drytis","email":"hikmat@drytis.com"}],"dist":{"shasum":"b5fde4093ab62f90679f72bc55fa896ddfed562e","tarball":"https://registry.npmjs.org/@drytis/meeting-sdk/-/meeting-sdk-1.0.13.tgz","fileCount":8,"integrity":"sha512-S7u04phyv6/z3O+ZECg2VnHS77TZI2cHARUp98IPaT/Gf4AWmZroZdO5KYSdFxkqL5Nv2oNMiqNVgAfWQ/MWRA==","signatures":[{"sig":"MEYCIQD30nkXMAlVNZWa7ab64zP2UdqTmPBcHNZJvWe6gDfHdwIhAM3Nq6/z4S+co+fsDdbWz6NxPRbE3gyHEkrlgXMKDIWw","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":309091},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"gitHead":"fc55c7f8c9d08e41b5f5dba4a6e73f240ef68d03","scripts":{"dev":"tsup --watch","build":"tsup","prepare":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"drytis","email":"hikmat@drytis.com"},"repository":{"url":"https://git.drytis.ai/hikmat/meeting-sdk","type":"git"},"_npmVersion":"10.9.3","description":"React TypeScript SDK for Drytis Planning and Lifeguard meeting sessions","directories":{},"_nodeVersion":"22.20.0","dependencies":{"livekit-client":"^2.8.3"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","typescript":"^5","@types/react":"^19","@types/react-dom":"^19"},"peerDependencies":{"react":">=18","react-dom":">=18"},"_npmOperationalInternal":{"tmp":"tmp/meeting-sdk_1.0.13_1777660760139_0.5221203476111347","host":"s3://npm-registry-packages-npm-production"}},"1.0.14":{"name":"@drytis/meeting-sdk","version":"1.0.14","keywords":["drytis","meeting","livekit","planning","lifeguard","sdk","react"],"license":"MIT","_id":"@drytis/meeting-sdk@1.0.14","maintainers":[{"name":"drytis","email":"hikmat@drytis.com"}],"dist":{"shasum":"0f1f9ce4b72945354990c6b0bfdc57fcc25dafb2","tarball":"https://registry.npmjs.org/@drytis/meeting-sdk/-/meeting-sdk-1.0.14.tgz","fileCount":8,"integrity":"sha512-lRy7tCtqzkq/5XNvA3+eVaNLQnAB+H+DTHCKEtkhMiSGW5ts+W5LYBAa1Ua553yEpAO1ZWg+KoKH7XzybaW9Gg==","signatures":[{"sig":"MEUCIQCMzMbDXUAIy2wzpb0z+AAn2M27n+245uFz1NEeWavc1AIgH3sAQEYznQRm1PU9Mbpf6QwWbzsIfk61/zQMI7psqf8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":313771},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"gitHead":"beeae253c03e722ec083ad4d16898648d247f840","scripts":{"dev":"tsup --watch","build":"tsup","prepare":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"drytis","email":"hikmat@drytis.com"},"repository":{"url":"https://git.drytis.ai/hikmat/meeting-sdk","type":"git"},"_npmVersion":"10.9.3","description":"React TypeScript SDK for Drytis Planning and Lifeguard meeting sessions","directories":{},"_nodeVersion":"22.20.0","dependencies":{"livekit-client":"^2.8.3"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","typescript":"^5","@types/react":"^19","@types/react-dom":"^19"},"peerDependencies":{"react":">=18","react-dom":">=18"},"_npmOperationalInternal":{"tmp":"tmp/meeting-sdk_1.0.14_1777891161493_0.8349787940301356","host":"s3://npm-registry-packages-npm-production"}},"1.0.15":{"name":"@drytis/meeting-sdk","version":"1.0.15","keywords":["drytis","meeting","livekit","planning","lifeguard","sdk","react"],"license":"MIT","_id":"@drytis/meeting-sdk@1.0.15","maintainers":[{"name":"drytis","email":"hikmat@drytis.com"}],"dist":{"shasum":"8c43133d6fafd576393908fd0131787095f597d6","tarball":"https://registry.npmjs.org/@drytis/meeting-sdk/-/meeting-sdk-1.0.15.tgz","fileCount":8,"integrity":"sha512-YZK8heq/0OVZLMVO2u/LDUo98S5+1ntGPqTamPAIIBBFuQZDgYQRImxNtmD4Tc9c1GIMMTGEpZk9hnk9KSc0Lw==","signatures":[{"sig":"MEUCIQC7v64IqEwppLBH5P5s9pZeBGHhALMnSdA7vwiWYKK60gIgWsOiH20ctVqSt80OX3KWP7AOQ8xmCo1IKc+FMDJiMJ8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":315875},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"gitHead":"886f282e3280dd397c8770ad61b024362a5a796c","scripts":{"dev":"tsup --watch","build":"tsup","prepare":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"drytis","email":"hikmat@drytis.com"},"repository":{"url":"https://git.drytis.ai/hikmat/meeting-sdk","type":"git"},"_npmVersion":"10.9.3","description":"React TypeScript SDK for Drytis Planning and Lifeguard meeting sessions","directories":{},"_nodeVersion":"22.20.0","dependencies":{"livekit-client":"^2.8.3"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","typescript":"^5","@types/react":"^19","@types/react-dom":"^19"},"peerDependencies":{"react":">=18","react-dom":">=18"},"_npmOperationalInternal":{"tmp":"tmp/meeting-sdk_1.0.15_1777897104273_0.5687991673232053","host":"s3://npm-registry-packages-npm-production"}},"1.0.16":{"name":"@drytis/meeting-sdk","version":"1.0.16","keywords":["drytis","meeting","livekit","planning","lifeguard","sdk","react"],"license":"MIT","_id":"@drytis/meeting-sdk@1.0.16","maintainers":[{"name":"drytis","email":"hikmat@drytis.com"}],"dist":{"shasum":"e60b49aaa93d2e2c6a0f7f629ad22a10270e1f63","tarball":"https://registry.npmjs.org/@drytis/meeting-sdk/-/meeting-sdk-1.0.16.tgz","fileCount":8,"integrity":"sha512-aEkU9yEk0aAL9QG0NdPh3Yr8QqV0k7WMbJR7PhMvvZPQz2W/pmbcVQVhSwNOwed+kgrDRzm9AVTnxD6pR9+Wag==","signatures":[{"sig":"MEQCIEWSJ5dlUyuXr+0TSFgS3hYvm9RkOj9TCIwQiNu0vrwKAiAk+zTE225qcv6ljqA1uGXHi2ZhnoLJUzqSIWqBVbuyEQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":347013},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"gitHead":"47b6acb91d41075c0bc0adaa9bb2e7e130af6ffe","scripts":{"dev":"tsup --watch","build":"tsup","prepare":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"drytis","email":"hikmat@drytis.com"},"repository":{"url":"https://git.drytis.ai/hikmat/meeting-sdk","type":"git"},"_npmVersion":"10.9.3","description":"React TypeScript SDK for Drytis Planning and Lifeguard meeting sessions","directories":{},"_nodeVersion":"22.20.0","dependencies":{"livekit-client":"^2.8.3"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","typescript":"^5","@types/react":"^19","@types/react-dom":"^19"},"peerDependencies":{"react":">=18","react-dom":">=18"},"_npmOperationalInternal":{"tmp":"tmp/meeting-sdk_1.0.16_1778095915342_0.6484983689503587","host":"s3://npm-registry-packages-npm-production"}},"1.0.17":{"name":"@drytis/meeting-sdk","version":"1.0.17","keywords":["drytis","meeting","livekit","planning","lifeguard","sdk","react"],"license":"MIT","_id":"@drytis/meeting-sdk@1.0.17","maintainers":[{"name":"drytis","email":"hikmat@drytis.com"}],"dist":{"shasum":"a05c12cf4743eeea5b5fc7b603562476ba52d8a0","tarball":"https://registry.npmjs.org/@drytis/meeting-sdk/-/meeting-sdk-1.0.17.tgz","fileCount":8,"integrity":"sha512-DaTLBQ/8giICAAFWHoADtJxmCaE6Dyv0CULWb7jMBYCLtHXRZXIr4W+4ScwEZeG5I4hFpiWME1w9TAlIs4WT3g==","signatures":[{"sig":"MEUCIQCORqBJpnNRS38Pxxi18MwMXbK+V1irArUHolovMwZ8pQIgCpEMhIy+L6GfORJvV+ssAVCVQWKOzGZMdSdA+tcicYc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":289109},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"gitHead":"2ad3d43588dbe4a6e8a1ad7c659cd8c9fb3dc779","scripts":{"dev":"tsup --watch","build":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"drytis","email":"hikmat@drytis.com"},"deprecated":"broken build - missing isRemoteScreenSharing","repository":{"url":"https://git.drytis.ai/hikmat/meeting-sdk","type":"git"},"_npmVersion":"11.12.1","description":"React TypeScript SDK for Drytis Planning and Lifeguard meeting sessions","directories":{},"_nodeVersion":"24.15.0","dependencies":{"livekit-client":"^2.8.3"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","typescript":"^5","@types/react":"^19","@types/react-dom":"^19"},"peerDependencies":{"react":">=18","react-dom":">=18"},"_npmOperationalInternal":{"tmp":"tmp/meeting-sdk_1.0.17_1780501242877_0.7176578384576573","host":"s3://npm-registry-packages-npm-production"}},"1.0.18":{"name":"@drytis/meeting-sdk","version":"1.0.18","description":"React TypeScript SDK for Drytis Planning and Lifeguard meeting sessions","main":"dist/index.js","module":"dist/index.mjs","types":"dist/index.d.ts","exports":{".":{"import":"./dist/index.mjs","require":"./dist/index.js","types":"./dist/index.d.ts"}},"scripts":{"build":"tsup","dev":"tsup --watch","typecheck":"tsc --noEmit"},"peerDependencies":{"react":">=18","react-dom":">=18"},"dependencies":{"livekit-client":"^2.8.3"},"devDependencies":{"@types/react":"^19","@types/react-dom":"^19","tsup":"^8","typescript":"^5"},"keywords":["drytis","meeting","livekit","planning","lifeguard","sdk","react"],"license":"MIT","repository":{"type":"git","url":"https://git.drytis.ai/hikmat/meeting-sdk"},"gitHead":"052fe12698b119a50ec3b9d343870c59fdc2696f","_id":"@drytis/meeting-sdk@1.0.18","_nodeVersion":"24.16.0","_npmVersion":"11.13.0","dist":{"integrity":"sha512-laW4+JookFiWNHaKVa3inVNHZVEhBM48SdVFfLweyCzIJ0YuRxEMg5EIn1BmyPvPeW0KYdW7JfZN2idX0F3T1A==","shasum":"94cb59521cecf9e30a84265384101553cab9f263","tarball":"https://registry.npmjs.org/@drytis/meeting-sdk/-/meeting-sdk-1.0.18.tgz","fileCount":8,"unpackedSize":294411,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEMCIGjB2WsvzCyAY7+1fk0oM0vXt7JpgCLx+r66s6KpAPjJAh80VYmydkZZHyfA5wcJEI+gpXpLItd68uarHRMRjJit"}]},"_npmUser":{"name":"drytis","email":"hikmat@drytis.com"},"directories":{},"maintainers":[{"name":"drytis","email":"hikmat@drytis.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/meeting-sdk_1.0.18_1781288748194_0.016398808794495068"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-08T20:11:52.731Z","modified":"2026-06-12T18:25:48.465Z","0.1.0":"2026-04-08T20:11:52.982Z","0.2.0":"2026-04-10T07:55:23.368Z","0.3.0":"2026-04-17T17:10:06.031Z","1.0.4":"2026-04-17T18:19:47.784Z","1.0.5":"2026-04-17T18:33:29.465Z","1.0.6":"2026-04-17T21:45:54.613Z","1.0.7":"2026-04-17T21:48:50.144Z","1.0.8":"2026-04-17T22:17:11.889Z","1.0.9":"2026-04-17T22:21:32.633Z","1.0.10":"2026-04-17T22:24:51.046Z","1.0.11":"2026-04-17T23:13:46.596Z","1.0.12":"2026-04-29T19:27:47.426Z","1.0.13":"2026-05-01T18:39:20.293Z","1.0.14":"2026-05-04T10:39:21.677Z","1.0.15":"2026-05-04T12:18:24.436Z","1.0.16":"2026-05-06T19:31:55.506Z","1.0.17":"2026-06-03T15:40:43.050Z","1.0.18":"2026-06-12T18:25:48.347Z"},"license":"MIT","keywords":["drytis","meeting","livekit","planning","lifeguard","sdk","react"],"repository":{"type":"git","url":"https://git.drytis.ai/hikmat/meeting-sdk"},"description":"React TypeScript SDK for Drytis Planning and Lifeguard meeting sessions","maintainers":[{"name":"drytis","email":"hikmat@drytis.com"}],"readme":"# @drytis/meeting-sdk\n\n[![npm version](https://img.shields.io/npm/v/@drytis/meeting-sdk)](https://www.npmjs.com/package/@drytis/meeting-sdk)\n[![license](https://img.shields.io/npm/l/@drytis/meeting-sdk)](./LICENSE)\n[![npm](https://img.shields.io/npm/dm/@drytis/meeting-sdk)](https://www.npmjs.com/package/@drytis/meeting-sdk)\n\nReact SDK for embedding Drytis real-time meeting sessions into your application. Handles Planning (FDE / AI agent), Lifeguard (human engineer), and Direct Meeting flows. Backed by [LiveKit](https://livekit.io).\n\n---\n\n## Table of Contents\n\n- [What is this SDK?](#what-is-this-sdk)\n- [How it fits into the stack](#how-it-fits-into-the-stack)\n- [Requirements](#requirements)\n- [Installation](#installation)\n- [API Keys](#api-keys)\n- [Core Concept — Pre-fetch, then Connect](#core-concept--pre-fetch-then-connect)\n- [Quick Start](#quick-start)\n- [DrytisCall Component](#drytisc all-component)\n- [Props Reference](#props-reference)\n- [Feature Flags](#feature-flags)\n- [The `metadata` Prop](#the-metadata-prop)\n- [Ref / Imperative API](#ref--imperative-api)\n- [Call Phase](#call-phase)\n- [Events / Callbacks](#events--callbacks)\n- [useDrytisCall Hook](#usedrytisc all-hook)\n- [ControlBar Component](#controlbar-component)\n- [Low-level API Utilities](#low-level-api-utilities)\n- [TypeScript Types](#typescript-types)\n- [Full Examples](#full-examples)\n\n---\n\n## What is this SDK?\n\nThe Drytis Meeting SDK embeds real-time audio/video calls inside your React application. You fetch a session from your backend, pass it to the SDK, and the SDK handles everything from there:\n\n- Connecting to a LiveKit room\n- Enabling the local microphone and camera\n- Playing remote audio from agents and participants\n- Streaming and deduplicating real-time transcripts\n- Providing mute, camera, and end-call controls\n\n**The SDK makes no API calls.** All session management (fetching tokens, managing queues, subscribing to SSE) is done by your application code. The SDK receives a `MeetingSession` object and connects.\n\n---\n\n## How it fits into the stack\n\n```\nYour Frontend (React)\n  │\n  ├── Your code fetches session ──→  Your Backend (Next.js API route)\n  │     (fetch, axios, etc.)             └── calls Drytis Meeting Server\n  │                                           └── returns LiveKit token\n  │\n  ├── Pass session to DrytisCall\n  │     session={{ roomName, token, serverUrl }}\n  │\n  └── DrytisCall connects to LiveKit ──→  LiveKit Cloud\n        (room audio / video / transcripts)\n```\n\nYour backend is the gatekeeper — it holds the secret key and creates sessions. The SDK only receives the result and connects.\n\n---\n\n## Requirements\n\n- React 18 or later\n- A running Drytis meeting server reachable from the browser\n- An API key pair from the Drytis admin panel\n\n---\n\n## Installation\n\n```bash\nnpm install @drytis/meeting-sdk\n# or\nyarn add @drytis/meeting-sdk\n# or\npnpm add @drytis/meeting-sdk\n```\n\n---\n\n## API Keys\n\n| Key | Prefix | Where it lives | Purpose |\n|-----|--------|----------------|---------|\n| **Public key** | `drytis_pk_live_…` | Client-side — safe to ship in browser code | Sent as `X-Drytis-Public-Key` on SDK requests. Identifies your project. |\n| **Secret key** | `drytis_sk_live_…` | **Server-side only** | Used by your backend to authenticate with the Drytis Meeting Server. |\n\n```bash\n# .env.local\nNEXT_PUBLIC_DRYTIS_PUBLIC_KEY=drytis_pk_live_xxxxx\nDRYTIS_SECRET_KEY=drytis_sk_live_xxxxx\nNEXT_PUBLIC_DRYTIS_MEETING_SERVER=https://meet.drytis.ai\n```\n\n> **Never** pass `DRYTIS_SECRET_KEY` to `DrytisCall` or any client-side code.\n\n---\n\n## Core Concept — Pre-fetch, then Connect\n\nThe SDK's only input is a `MeetingSession` — a plain object with three fields:\n\n```ts\ninterface MeetingSession {\n  roomName: string;   // LiveKit room name\n  token: string;      // LiveKit participant JWT\n  serverUrl: string;  // wss://your-livekit-instance.livekit.cloud\n}\n```\n\n**You fetch this from your backend. Then you pass it to the SDK.**\n\n```\n1. Your code  →  fetch('/api/fde/planning-session')\n2. Your code  →  get { roomName, token, serverUrl } back\n3. Your code  →  <DrytisCall session={...} />\n4. SDK        →  connects to LiveKit, handles audio/transcripts/controls\n```\n\nThis separation means the SDK never needs to know your server URL, your auth tokens, or your queue logic. Your app controls those; the SDK controls LiveKit.\n\n---\n\n## Quick Start\n\n### Step 1 — Your backend creates a session\n\n```ts\n// app/api/fde/planning-session/route.ts\nexport async function POST(req: Request) {\n  const body = await req.json();\n\n  const res = await fetch(`${process.env.DRYTIS_MEETING_SERVER}/api/fde/planning-session`, {\n    method: 'POST',\n    headers: {\n      'Content-Type': 'application/json',\n      Authorization: `Bearer ${process.env.DRYTIS_SECRET_KEY}`,\n    },\n    body: JSON.stringify(body),\n  });\n\n  return Response.json(await res.json(), { status: res.status });\n}\n```\n\n### Step 2 — Your frontend fetches the session and passes it to the SDK\n\n```tsx\nimport { useEffect, useRef, useState } from 'react';\nimport { DrytisCall } from '@drytis/meeting-sdk';\nimport type { DrytisCallHandle, MeetingSession } from '@drytis/meeting-sdk';\n\nexport function PlanningPage({ userToken }: { userToken: string }) {\n  const ref = useRef<DrytisCallHandle>(null);\n  const [session, setSession] = useState<MeetingSession | null>(null);\n\n  useEffect(() => {\n    fetch('/api/fde/planning-session', {\n      method: 'POST',\n      headers: { Authorization: `Bearer ${userToken}` },\n      body: JSON.stringify({ participantName: 'Alice' }),\n    })\n      .then(r => r.json())\n      .then(data => setSession({\n        roomName: data.roomName,\n        token: data.livekit_token,\n        serverUrl: data.serverUrl,\n      }));\n  }, [userToken]);\n\n  useEffect(() => {\n    if (session) ref.current?.requestSupport();\n  }, [session]);\n\n  return (\n    <DrytisCall\n      ref={ref}\n      session={session}\n      publicKey={process.env.NEXT_PUBLIC_DRYTIS_PUBLIC_KEY}\n      metadata={{ participantName: 'Alice' }}\n      features={{ renderControls: true, mute: true }}\n      onCallStarted={({ roomName }) => console.log('started in', roomName)}\n      onCallEnded={({ duration }) => console.log(`ended after ${duration}s`)}\n    />\n  );\n}\n```\n\n---\n\n## `DrytisCall` Component\n\nThe single component for all meeting types. It:\n\n1. Accepts a `session` prop with the LiveKit connection details\n2. Connects to the LiveKit room when `requestSupport()` is called (or when `autoConnect` is true)\n3. Manages audio, transcripts, and participant state internally\n4. Optionally renders a built-in `ControlBar` when `features.renderControls = true`\n5. Fires callbacks on every lifecycle event\n6. Exposes an imperative ref handle for external control\n\n```tsx\nimport { DrytisCall } from '@drytis/meeting-sdk';\n```\n\n---\n\n## Props Reference\n\n### Connection\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `session` | `MeetingSession \\| null` | — | The LiveKit session to connect to. Fetch this from your backend. Nothing connects until this is provided and `requestSupport()` is called. |\n| `publicKey` | `string` | — | Your public API key (`drytis_pk_live_…`). Identifies your project. |\n\n### Behaviour\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `autoConnect` | `boolean` | `false` | Automatically connect when `session` becomes non-null. Equivalent to calling `requestSupport()` in a `useEffect([session])`. |\n| `mode` | `'embedded' \\| 'redirect'` | `'embedded'` | `'embedded'` renders inline. `'redirect'` navigates the browser to `{session.serverUrl}/meeting/{session.roomName}`. |\n| `projectId` | `number \\| null` | — | Optional project reference — passed through to callbacks for context. |\n| `metadata` | `Record<string, unknown>` | — | Extra data — see [The `metadata` prop](#the-metadata-prop). |\n| `features` | `CallFeatures` | `{}` | Controls the built-in UI. See [Feature Flags](#feature-flags). |\n\n### Callbacks\n\n| Prop | Payload | When it fires |\n|------|---------|---------------|\n| `onCallStarted` | `CallStartedData` | Room connected, audio active. |\n| `onCallEnded` | `CallEndedData` | Room disconnected. |\n| `onParticipantJoined` | `ParticipantInfo` | Remote participant connects. |\n| `onParticipantLeft` | `ParticipantInfo` | Remote participant disconnects. |\n| `onUserSpeaking` | `UserSpeakingData` | New user transcript segment arrives. |\n| `onAIResponse` | `AIResponseData` | AI agent sends a message. |\n| `onTranscriptUpdate` | `TranscriptMessage[]` | Full transcript after any change. |\n| `onSessionResponse` | `SessionResponse` | Raw session response (for logging/context). |\n| `onError` | `CallError` | Connection failure or missing session. |\n| `onClose` | — | User clicked the × close button in the `ControlBar`. |\n| `onChat` | — | User clicked the chat icon in the `ControlBar`. No panel is rendered — handle this to show your own chat UI. |\n\n### Styling\n\n| Prop | Type | Description |\n|------|------|-------------|\n| `className` | `string` | CSS class on the wrapper `<div>`. |\n| `style` | `React.CSSProperties` | Inline styles on the wrapper `<div>`. |\n\n---\n\n## Feature Flags\n\n```ts\ninterface CallFeatures {\n  renderControls?: boolean;    // render built-in UI (default: false)\n  mute?: boolean;              // show mute button (default: true)\n  screenShare?: boolean;       // show screen-share button (default: true)\n  chat?: boolean;              // show chat icon button — fires onChat (default: true)\n  raiseHand?: boolean;         // reserved\n  noiseCancellation?: boolean; // reserved\n}\n```\n\nWhen `renderControls: true` the SDK renders:\n\n| Phase | Built-in UI |\n|-------|------------|\n| `idle` | \"Start Session\" button |\n| `connecting` | \"Connecting…\" banner |\n| `in_call` | `ControlBar` (timer, participants, mute, screen share, chat icon, more menu, close) |\n| `error` | Error message + \"Try Again\" button |\n\nSet `renderControls: false` (the default) to build your own UI using the ref or hook.\n\n---\n\n## The `metadata` Prop\n\nA free-form `Record<string, unknown>` for extra context. The SDK uses one field internally:\n\n| Field | Used by |\n|-------|---------|\n| `participantName` | `ControlBar` participants popup — shown as your display name |\n\nEverything else is available to you via callbacks (e.g. `onSessionResponse`) for your own logic.\n\n```tsx\nmetadata={{ participantName: 'Alice', userId: 42 }}\n```\n\n---\n\n## Ref / Imperative API\n\n`DrytisCall` forwards a ref of type `DrytisCallHandle`:\n\n```tsx\nimport { useRef } from 'react';\nimport { DrytisCall } from '@drytis/meeting-sdk';\nimport type { DrytisCallHandle } from '@drytis/meeting-sdk';\n\nconst ref = useRef<DrytisCallHandle>(null);\n\n<DrytisCall ref={ref} session={session} ... />\n\n// call from anywhere\nref.current?.requestSupport();   // connect to the session\nref.current?.mute();\nref.current?.endCall();\n```\n\n| Method | Description |\n|--------|-------------|\n| `requestSupport` | Connects to the session. Call this once `session` is ready. |\n| `cancelRequest` | Resets phase to `idle`. Use this if you want to abort before connecting. |\n| `mute` | Disables local microphone. |\n| `unmute` | Re-enables local microphone. |\n| `endCall` | Disconnects from the room, removes audio elements, resets to `idle`. |\n| `sendMessage` | Publishes a chat message over the LiveKit data channel. No-op if not connected. |\n| `getTranscript` | Returns a synchronous snapshot of the full transcript. |\n\n---\n\n## Call Phase\n\n```\nidle ──→ connecting ──→ in_call ──→ idle\nidle ──→ error\n```\n\n| Phase | Meaning |\n|-------|---------|\n| `idle` | No connection. Waiting for `requestSupport()` to be called. |\n| `connecting` | LiveKit room is being joined. |\n| `in_call` | Room connected. Audio is active. |\n| `error` | Connection failed. `errorMessage` has the reason. |\n\n> Queue management (waiting for an engineer, SSE, polling) is handled in your application code — not inside the SDK. See the [Lifeguard example](#lifeguard--human-engineer-queue).\n\n---\n\n## Events / Callbacks\n\nAll callbacks are optional. Pass them as props to `<DrytisCall>` or as options to `useDrytisCall`.\n\n```tsx\n<DrytisCall\n  session={session}\n  onCallStarted={...}\n  onCallEnded={...}\n  onParticipantJoined={...}\n  onParticipantLeft={...}\n  onUserSpeaking={...}\n  onAIResponse={...}\n  onTranscriptUpdate={...}\n  onSessionResponse={...}\n  onError={...}\n/>\n```\n\n---\n\n### `onCallStarted`\n\n```ts\nonCallStarted?: (data: CallStartedData) => void\n\ninterface CallStartedData {\n  roomName: string;   // the LiveKit room name that connected\n}\n```\n\n**When:** LiveKit room is connected and the local microphone is active. Audio from remote participants will begin playing immediately after this fires.\n\n**Use for:** starting a call timer, logging analytics, showing an \"in call\" indicator.\n\n```tsx\nonCallStarted={({ roomName }) => {\n  console.log('Call started in room:', roomName);\n  analytics.track('call_started', { roomName });\n}}\n```\n\n---\n\n### `onCallEnded`\n\n```ts\nonCallEnded?: (data: CallEndedData) => void\n\ninterface CallEndedData {\n  duration: number;   // call length in seconds (0 if ended before connecting)\n}\n```\n\n**When:** Room disconnects — either because `endCall()` was called, the remote side disconnected, or the network dropped.\n\n**Use for:** showing a post-call summary, logging duration, resetting UI state.\n\n```tsx\nonCallEnded={({ duration }) => {\n  console.log(`Call lasted ${duration} seconds`);\n  setCallActive(false);\n}}\n```\n\n---\n\n### `onParticipantJoined`\n\n```ts\nonParticipantJoined?: (participant: ParticipantInfo) => void\n\ninterface ParticipantInfo {\n  identity: string;   // LiveKit participant identity (unique, stable)\n  name: string;       // display name (falls back to identity if not set)\n}\n```\n\n**When:** Any remote participant connects to the room — including AI agents and human engineers. Fires once per participant.\n\n**Use for:** showing a \"participant joined\" notification, updating a participant list, detecting when an agent joins.\n\n```tsx\nonParticipantJoined={(p) => {\n  console.log(`${p.name} (${p.identity}) joined`);\n  setParticipants(prev => [...prev, p]);\n}}\n```\n\n---\n\n### `onParticipantLeft`\n\n```ts\nonParticipantLeft?: (participant: ParticipantInfo) => void\n\n// ParticipantInfo: same as above\n```\n\n**When:** A remote participant disconnects from the room.\n\n**Use for:** removing someone from a participant list, ending the session if the agent leaves.\n\n```tsx\nonParticipantLeft={(p) => {\n  console.log(`${p.name} left`);\n  setParticipants(prev => prev.filter(x => x.identity !== p.identity));\n}}\n```\n\n---\n\n### `onUserSpeaking`\n\n```ts\nonUserSpeaking?: (data: UserSpeakingData) => void\n\ninterface UserSpeakingData {\n  identity: string;   // LiveKit identity of the speaker\n  name: string;       // display name of the speaker\n  transcript: string; // the text they just spoke (may be interim)\n}\n```\n\n**When:** A new user transcript segment arrives for the first time (not on subsequent updates to the same segment). Fires once per new spoken utterance — not once per update.\n\n**Use for:** \"someone is speaking\" indicators, real-time activity feeds, keyword detection.\n\n```tsx\nonUserSpeaking={({ name, transcript }) => {\n  console.log(`${name} said: \"${transcript}\"`);\n  setSpeakerIndicator(name);\n}}\n```\n\n---\n\n### `onAIResponse`\n\n```ts\nonAIResponse?: (data: AIResponseData) => void\n\ninterface AIResponseData {\n  text: string;   // the agent's message text (may be interim)\n}\n```\n\n**When:** The AI agent publishes a transcript segment. Fires on every update — including interim (still being generated) messages.\n\n**Use for:** streaming AI responses into a chat UI, logging AI output, triggering side effects based on agent speech.\n\n```tsx\nonAIResponse={({ text }) => {\n  setAgentMessage(text);  // update in real time as agent speaks\n}}\n```\n\n---\n\n### `onTranscriptUpdate`\n\n```ts\nonTranscriptUpdate?: (messages: TranscriptMessage[]) => void\n\ninterface TranscriptMessage {\n  id: string;                                         // stable segment ID — same ID can arrive multiple times with updated text\n  sender: 'agent' | 'user' | 'system' | 'engineer';  // who sent this message\n  text: string;                                       // current text (may change on interim updates)\n  timestamp: number;                                  // ms since epoch\n  isFinal: boolean;                                   // false = still being spoken; true = complete\n  identity?: string;                                  // LiveKit participant identity of the sender\n}\n```\n\n**When:** Any change to the transcript — new message, interim text update, or final segment. Fires with the **complete, deduplicated** list every time.\n\n**Use for:** rendering a live transcript UI, persisting the full conversation, displaying captions.\n\n```tsx\nonTranscriptUpdate={(messages) => {\n  // messages is always the full list — render all of them\n  setTranscript(messages);\n}}\n\n// Rendering example\ntranscript.map(m => (\n  <div key={m.id} style={{ opacity: m.isFinal ? 1 : 0.5 }}>\n    <strong>{m.sender === 'agent' ? 'AI' : 'You'}:</strong> {m.text}\n  </div>\n))\n```\n\n**Sender values:**\n\n| Value | Who |\n|-------|-----|\n| `'user'` | The local user (you) |\n| `'agent'` | The AI agent in the room |\n| `'engineer'` | A human Lifeguard engineer |\n| `'system'` | System/platform messages |\n\n---\n\n### `onSessionResponse`\n\n```ts\nonSessionResponse?: (response: SessionResponse) => void\n\ninterface SessionResponse {\n  roomName: string;\n  participantName?: string;\n  participantIdentity?: string;\n  projectId?: string | number;\n  userId?: string | number;\n  agentName?: string;\n  [key: string]: any;   // any extra fields your backend returns\n}\n```\n\n**When:** The session has been resolved. Carries the raw server response for logging or display.\n\n**Use for:** debugging, logging session context, showing room info in a developer panel.\n\n```tsx\nonSessionResponse={(res) => {\n  console.log('Session:', res.roomName, 'Agent:', res.agentName);\n}}\n```\n\n---\n\n### `onError`\n\n```ts\nonError?: (error: CallError) => void\n\ninterface CallError {\n  code: string;     // machine-readable error code\n  message: string;  // human-readable description\n}\n```\n\n**When:** Something goes wrong. Phase moves to `'error'` at the same time.\n\n| `code` | Cause |\n|--------|-------|\n| `CONNECTION_FAILED` | `room.connect()` threw — bad token, server unreachable, network error. |\n| `CONFIG_ERROR` | `requestSupport()` was called but `session` prop is null or missing. |\n\n**Use for:** showing error toasts, logging to Sentry/Datadog, retrying with exponential backoff.\n\n```tsx\nonError={({ code, message }) => {\n  console.error(`[${code}] ${message}`);\n  Sentry.captureException(new Error(message), { extra: { code } });\n  toast.error('Could not connect to session. Please try again.');\n}}\n```\n\n---\n\n### All events at a glance\n\n| Event | Payload type | Fires when |\n|-------|-------------|------------|\n| `onCallStarted` | `CallStartedData` | Room connected, mic active |\n| `onCallEnded` | `CallEndedData` | Room disconnected |\n| `onParticipantJoined` | `ParticipantInfo` | Remote participant connects |\n| `onParticipantLeft` | `ParticipantInfo` | Remote participant disconnects |\n| `onUserSpeaking` | `UserSpeakingData` | New user speech segment arrives |\n| `onAIResponse` | `AIResponseData` | AI agent sends any message |\n| `onTranscriptUpdate` | `TranscriptMessage[]` | Any transcript change (full list) |\n| `onSessionResponse` | `SessionResponse` | Session resolved |\n| `onError` | `CallError` | Connection or config failure |\n| `onClose` | — | × button clicked in ControlBar |\n| `onChat` | — | Chat icon clicked in ControlBar |\n\n---\n\n## `useDrytisCall` Hook\n\nFor complete control over rendering, use the hook directly:\n\n```tsx\nimport { useDrytisCall } from '@drytis/meeting-sdk';\n\nfunction MyMeeting({ session }: { session: MeetingSession | null }) {\n  const {\n    phase,\n    isMuted,\n    isCameraOn,\n    transcript,\n    participants,\n    errorMessage,\n    requestSupport,\n    mute,\n    unmute,\n    toggleCamera,\n    endCall,\n  } = useDrytisCall({\n    session,\n    publicKey: process.env.NEXT_PUBLIC_DRYTIS_PUBLIC_KEY,\n    metadata: { participantName: 'Alice' },\n    onCallStarted: ({ roomName }) => console.log('started', roomName),\n    onTranscriptUpdate: (msgs) => console.log(msgs),\n  });\n\n  // Connect once session is ready\n  useEffect(() => {\n    if (session && phase === 'idle') requestSupport();\n  }, [session]);\n\n  if (phase === 'idle' || phase === 'connecting') return <p>{phase}…</p>;\n  if (phase === 'error') return <p>Error: {errorMessage}</p>;\n\n  return (\n    <div>\n      <ul>\n        {transcript.map(m => (\n          <li key={m.id} style={{ opacity: m.isFinal ? 1 : 0.5 }}>\n            <strong>{m.sender}:</strong> {m.text}\n          </li>\n        ))}\n      </ul>\n      <button onClick={isMuted ? unmute : mute}>{isMuted ? 'Unmute' : 'Mute'}</button>\n      <button onClick={toggleCamera}>{isCameraOn ? 'Camera Off' : 'Camera On'}</button>\n      <button onClick={endCall}>End</button>\n    </div>\n  );\n}\n```\n\n### Options (`UseDrytisCallOptions`)\n\nSame fields as `DrytisCallProps` minus `features`, `className`, `style`, and `autoConnect`.\n\n### Return (`UseDrytisCallReturn`)\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `phase` | `CallPhase` | Current state. |\n| `isMuted` | `boolean` | Local mic muted. |\n| `isCameraOn` | `boolean` | Local camera on. |\n| `transcript` | `TranscriptMessage[]` | Live transcript. |\n| `participants` | `ParticipantInfo[]` | Current remote participants. |\n| `errorMessage` | `string \\| null` | Set when `phase === 'error'`. |\n| `session` | `MeetingSession \\| null` | Active session. |\n| `requestSupport` / `startCall` | `() => void` | Connect to the session. |\n| `cancelRequest` | `() => void` | Reset to `idle`. |\n| `mute` / `unmute` | `() => void` | Mic controls. |\n| `toggleCamera` | `() => void` | Camera toggle. |\n| `isScreenSharing` | `boolean` | Whether local screen share is active. |\n| `toggleScreenShare` | `() => void` | Start / stop screen share. |\n| `endCall` | `() => void` | Disconnect and reset. |\n| `sendMessage` | `(text: string) => void` | Send a message over the LiveKit data channel. |\n| `getTranscript` | `() => TranscriptMessage[]` | Sync transcript snapshot. |\n\n---\n\n## `ControlBar` Component\n\nThe built-in control bar. You can use it standalone with `useDrytisCall`:\n\n```tsx\nimport { ControlBar } from '@drytis/meeting-sdk';\n\n<ControlBar\n  isMuted={isMuted}\n  isCameraOn={isCameraOn}\n  isScreenSharing={isScreenSharing}\n  onMute={mute}\n  onUnmute={unmute}\n  onToggleCamera={toggleCamera}\n  onToggleScreenShare={toggleScreenShare}\n  onEndCall={endCall}\n  onClose={() => setVisible(false)}\n  onChat={() => setChatOpen(true)}\n  features={{ mute: true, screenShare: true, chat: true }}\n  participantCount={participants.length + 1}\n  participants={participants}\n  localParticipantName=\"Alice\"\n/>\n```\n\n| Prop | Type | Description |\n|------|------|-------------|\n| `isMuted` | `boolean` | Mute button state. |\n| `isCameraOn` | `boolean` | Camera button state. |\n| `isScreenSharing` | `boolean` | Screen share button active state. |\n| `onMute` / `onUnmute` | `() => void` | Called on mute button press. |\n| `onToggleCamera` | `() => void` | Called on camera button press. |\n| `onToggleScreenShare` | `() => void` | Called on screen share button press. |\n| `onEndCall` | `() => void` | Called on end-call button press. |\n| `onClose` | `() => void` | Optional. Renders a × button that calls this when clicked. |\n| `onChat` | `() => void` | Optional. Renders a chat icon that fires this event — no panel is shown by the SDK. |\n| `features` | `CallFeatures` | Set individual flags to `false` to hide buttons. |\n| `participantCount` | `number` | Badge count (include yourself). |\n| `participants` | `ParticipantInfo[]` | Remote participants in the popup. |\n| `localParticipantName` | `string` | Your name in the participants popup. |\n\nThe bar renders: **mute** · **screen share** · **timer** · **participants** · **chat icon** · **more menu** · **close ×**\n\n---\n\n## Low-level API Utilities\n\nExported for app-level use — session fetching, queue management, and SSE event parsing. The SDK itself does not call these internally.\n\n```ts\nimport {\n  requestSession,\n  cancelQueueRequest,\n  pollQueueStatus,\n  resolveSessionFromEvent,\n} from '@drytis/meeting-sdk';\nimport type { ApiCredentials, SessionResult } from '@drytis/meeting-sdk';\n```\n\n### `requestSession`\n\n```ts\nrequestSession(\n  serverUrl: string,\n  sessionEndpoint: string,\n  credentials: ApiCredentials,  // { publicKey: string }\n  body?: Record<string, unknown>,\n  metadata?: Record<string, unknown>,\n): Promise<SessionResult>\n```\n\nCalls the session endpoint and returns:\n\n```ts\ninterface SessionResult {\n  session: MeetingSession | null;       // LiveKit token was returned immediately\n  queueId: string | null;               // request was queued\n  subscriptionUrl: string | null;       // SSE URL for queue events\n  subscriptionToken: string | null;     // JWT for SSE auth\n  rawResponse: SessionResponse | null;  // full raw server response\n}\n```\n\n### `cancelQueueRequest`\n\n```ts\ncancelQueueRequest(serverUrl: string, credentials: ApiCredentials, queueId: string, userId?: string | number): Promise<void>\n```\n\n### `pollQueueStatus`\n\n```ts\npollQueueStatus(serverUrl: string, queueId: string): Promise<Record<string, any>>\n```\n\n### `resolveSessionFromEvent`\n\n```ts\nresolveSessionFromEvent(eventData: any, fallbackServerUrl: string): MeetingSession | null\n```\n\nExtracts a `MeetingSession` from a Mercure/SSE acceptance event payload.\n\n---\n\n## TypeScript Types\n\n```ts\nimport type {\n  DrytisCallProps,\n  DrytisCallHandle,\n  CallHandle,\n  UseDrytisCallOptions,\n  UseDrytisCallReturn,\n  CallPhase,        // 'idle' | 'connecting' | 'in_call' | 'error'\n  EmbedMode,        // 'embedded' | 'redirect'\n  CallFeatures,\n  MeetingSession,\n  SessionResponse,\n  TranscriptMessage,\n  ParticipantInfo,\n  CallStartedData,\n  CallEndedData,\n  UserSpeakingData,\n  AIResponseData,\n  CallError,\n  ApiCredentials,\n  SessionResult,\n} from '@drytis/meeting-sdk';\n```\n\n---\n\n## Full Examples\n\n### Planning — AI agent session\n\n```tsx\n'use client';\nimport { useEffect, useRef, useState } from 'react';\nimport { DrytisCall } from '@drytis/meeting-sdk';\nimport type { DrytisCallHandle, MeetingSession } from '@drytis/meeting-sdk';\n\nconst PUBLIC_KEY = process.env.NEXT_PUBLIC_DRYTIS_PUBLIC_KEY!;\n\nexport function PlanningPanel({ userToken, userId, userName, projectId }: {\n  userToken: string; userId: string; userName: string; projectId?: number;\n}) {\n  const ref = useRef<DrytisCallHandle>(null);\n  const [session, setSession] = useState<MeetingSession | null>(null);\n  const hasJoined = useRef(false);\n\n  // Auto-connect once session is ready\n  useEffect(() => {\n    if (!session || hasJoined.current) return;\n    hasJoined.current = true;\n    setTimeout(() => ref.current?.requestSupport(), 0);\n  }, [session]);\n\n  // Fetch session from your backend\n  useEffect(() => {\n    const controller = new AbortController();\n    fetch('/api/fde/planning-session', {\n      method: 'POST',\n      signal: controller.signal,\n      headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${userToken}` },\n      body: JSON.stringify({ participantName: userName, userId, projectId }),\n    })\n      .then(r => r.json())\n      .then(data => {\n        const lkToken = data.livekit_token ?? data.token;\n        const roomName = data.roomName ?? data.room_name;\n        const serverUrl = data.serverUrl ?? data.livekit_server_url ?? '';\n        if (lkToken && roomName) setSession({ roomName, token: lkToken, serverUrl });\n      })\n      .catch(() => {});\n    return () => controller.abort();\n  }, [userToken, userId]);\n\n  if (!session) return <p>Fetching session…</p>;\n\n  return (\n    <DrytisCall\n      ref={ref}\n      session={session}\n      publicKey={PUBLIC_KEY}\n      metadata={{ participantName: userName }}\n      features={{ renderControls: true, mute: true }}\n      onCallStarted={(d) => console.log('started', d.roomName)}\n      onCallEnded={(d) => console.log('ended after', d.duration, 's')}\n      onTranscriptUpdate={(msgs) => console.log(msgs.length, 'messages')}\n    />\n  );\n}\n```\n\n### Lifeguard — human engineer queue\n\nYour app manages the queue flow. The SDK only connects once a session is available.\n\n```tsx\n'use client';\nimport { useEffect, useRef, useState } from 'react';\nimport { DrytisCall, pollQueueStatus, resolveSessionFromEvent } from '@drytis/meeting-sdk';\nimport type { DrytisCallHandle, MeetingSession } from '@drytis/meeting-sdk';\n\nconst SERVER = process.env.NEXT_PUBLIC_DRYTIS_MEETING_SERVER!;\nconst PUBLIC_KEY = process.env.NEXT_PUBLIC_DRYTIS_PUBLIC_KEY!;\n\nexport function LifeguardPanel({ userToken, userId, userName, projectId }: {\n  userToken: string; userId: string; userName: string; projectId?: number;\n}) {\n  const ref = useRef<DrytisCallHandle>(null);\n  const [session, setSession] = useState<MeetingSession | null>(null);\n  const [waiting, setWaiting] = useState(false);\n  const hasJoined = useRef(false);\n  const pollRef = useRef<ReturnType<typeof setInterval> | null>(null);\n  const esRef = useRef<EventSource | null>(null);\n  const sessionId = useRef(crypto.randomUUID()).current;\n\n  // Auto-connect once accepted\n  useEffect(() => {\n    if (!session || hasJoined.current) return;\n    hasJoined.current = true;\n    setTimeout(() => ref.current?.requestSupport(), 0);\n  }, [session]);\n\n  const stopListeners = () => {\n    esRef.current?.close(); esRef.current = null;\n    if (pollRef.current) { clearInterval(pollRef.current); pollRef.current = null; }\n  };\n\n  const handleAccepted = (eventData: unknown) => {\n    stopListeners();\n    const resolved = resolveSessionFromEvent(eventData, SERVER);\n    if (resolved) { setWaiting(false); setSession(resolved); }\n  };\n\n  const requestHelp = async () => {\n    setWaiting(true);\n    try {\n      const res = await fetch('/api/help-request', {\n        method: 'POST',\n        headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${userToken}` },\n        body: JSON.stringify({ userId, projectId, sessionId, participantName: userName }),\n      });\n      const data = await res.json();\n\n      // Queue was accepted immediately\n      const lkToken = data.queue?.livekit_token ?? data.livekit_token;\n      const roomName = data.room_name ?? data.roomName;\n      if (lkToken && roomName) {\n        setWaiting(false);\n        setSession({ roomName, token: lkToken, serverUrl: data.serverUrl ?? SERVER });\n        return;\n      }\n\n      const queueId: string | undefined = data.queue?.id?.toString();\n\n      // SSE subscription (primary)\n      const subUrl = data.subscription?.subscription_url;\n      const subToken = data.subscription?.jwt_token;\n      if (subUrl && subToken) {\n        const url = new URL(subUrl);\n        url.searchParams.set('authorization', subToken);\n        const es = new EventSource(url.toString());\n        esRef.current = es;\n        es.onmessage = (e) => {\n          try {\n            const ev = JSON.parse(e.data);\n            const type: string = ev.event ?? ev.type ?? '';\n            if (type === 'lifeguard.accepted' || type === 'fde.accepted') handleAccepted(ev);\n            else if (type === 'lifeguard.cancelled' || type === 'fde.cancelled') { stopListeners(); setWaiting(false); }\n          } catch { /* ignore */ }\n        };\n        es.onerror = () => { esRef.current?.close(); esRef.current = null; };\n      }\n\n      // Polling fallback every 5 s\n      if (queueId) {\n        pollRef.current = setInterval(async () => {\n          try {\n            const status = await pollQueueStatus(SERVER, queueId);\n            if (status.status === 'accepted') handleAccepted(status);\n            else if (status.status === 'cancelled') { stopListeners(); setWaiting(false); }\n          } catch { /* ignore */ }\n        }, 5000);\n      }\n    } catch (err) {\n      console.error('help request failed', err);\n      setWaiting(false);\n    }\n  };\n\n  const cancelHelp = () => {\n    stopListeners();\n    setWaiting(false);\n    hasJoined.current = false;\n    setSession(null);\n  };\n\n  return (\n    <div>\n      {!session && !waiting && (\n        <button onClick={() => void requestHelp()}>Get Help</button>\n      )}\n      {waiting && (\n        <>\n          <p>Waiting for an engineer…</p>\n          <button onClick={cancelHelp}>Cancel</button>\n        </>\n      )}\n      <DrytisCall\n        ref={ref}\n        session={session}\n        publicKey={PUBLIC_KEY}\n        metadata={{ participantName: userName }}\n        features={{ renderControls: true, mute: true }}\n        onCallStarted={() => console.log('engineer joined')}\n        onCallEnded={() => { hasJoined.current = false; setSession(null); }}\n      />\n    </div>\n  );\n}\n```\n\n### Redirect mode\n\n```tsx\n<DrytisCall\n  session={session}\n  publicKey={PUBLIC_KEY}\n  mode=\"redirect\"\n  autoConnect\n/>\n```\n\nSDK navigates the browser to `{session.serverUrl}/meeting/{session.roomName}` once connected.\n\n### Full custom UI with the hook\n\n```tsx\nimport { useDrytisCall, ControlBar } from '@drytis/meeting-sdk';\n\nfunction CustomUI({ session }: { session: MeetingSession | null }) {\n  const {\n    phase, isMuted, isCameraOn, transcript, participants,\n    requestSupport, mute, unmute, toggleCamera, endCall,\n  } = useDrytisCall({ session, publicKey: process.env.NEXT_PUBLIC_DRYTIS_PUBLIC_KEY });\n\n  useEffect(() => {\n    if (session && phase === 'idle') requestSupport();\n  }, [session]);\n\n  if (phase !== 'in_call') return <p>{phase}</p>;\n\n  return (\n    <div>\n      <ul>\n        {transcript.map(m => <li key={m.id}><b>{m.sender}:</b> {m.text}</li>)}\n      </ul>\n      <ControlBar\n        isMuted={isMuted} isCameraOn={isCameraOn}\n        onMute={mute} onUnmute={unmute}\n        onToggleCamera={toggleCamera} onEndCall={endCall}\n        features={{ mute: true }}\n        participantCount={participants.length + 1}\n        participants={participants}\n      />\n    </div>\n  );\n}\n```\n\n---\n\n## How It Works Internally\n\n1. **Connect** — When `requestSupport()` is called with a valid `session`, the SDK creates a LiveKit `Room` and calls `room.connect(session.serverUrl, session.token)`.\n\n2. **Remote audio** — On every `TrackSubscribed` event for audio tracks, the SDK calls `track.attach()` to create an `<audio>` element, appends it to `document.body` with `display: none`, and plays it. This is what makes the other side audible without a video element.\n\n3. **Transcript** — Two sources are merged into one array:\n   - **`TranscriptionReceived`** — LiveKit's native real-time transcription (interim + final segments, deduplicated by segment ID).\n   - **`DataReceived`** — JSON packets published over the reliable data channel with `type: \"transcript\"` or `type: \"chat\"`.\n\n4. **Cleanup** — On unmount or `endCall()`: `room.disconnect()` is called, all `<audio>` elements are removed from the DOM.\n\n---\n\n## License\n\nMIT\n","readmeFilename":"README.md"}