{
    "version": "https://jsonfeed.org/version/1",
    "title": "  Blog",
    "home_page_url": "https://www.plugnmeet.org/blog",
    "description": "  Blog",
    "items": [
        {
            "id": "https://www.plugnmeet.org/blog/self-hosted-open-source-webinar-software",
            "content_html": "<p>In an era dominated by digital communication, webinars and online meetings have become indispensable. Yet, many organizations find themselves trapped in the rigid, often expensive, ecosystems of proprietary software like Zoom or GoToWebinar. These tools, while convenient, often come with hidden costs, limited customization, and significant concerns about data privacy and ownership.</p>\n<p>What if you could host professional webinars and meetings with unparalleled control, complete data privacy, and the flexibility to tailor every aspect to your brand and workflow? This is the promise of <strong>open source webinar software</strong> and <strong>self-hosted meeting</strong> platforms. At plugNmeet, we believe in empowering you to reclaim ownership of your digital interactions.</p>\n<hr>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"the-power-of-open-source-webinar-software-more-than-just-a-tool\">The Power of Open Source Webinar Software: More Than Just a Tool<a href=\"https://www.plugnmeet.org/blog/self-hosted-open-source-webinar-software#the-power-of-open-source-webinar-software-more-than-just-a-tool\" class=\"hash-link\" aria-label=\"Direct link to The Power of Open Source Webinar Software: More Than Just a Tool\" title=\"Direct link to The Power of Open Source Webinar Software: More Than Just a Tool\" translate=\"no\">​</a></h2>\n<p>When you search for \"open source webinar software,\" you're looking for freedom. You want to own your brand, manage your audience, and create a unique, professional presentation without being locked into a vendor's ecosystem.</p>\n<p>Proprietary webinar tools often treat a webinar like a glorified meeting, lacking the specific features needed for a structured, engaging event. An open-source platform, however, allows for:</p>\n<ol>\n<li class=\"\"><strong>Clear Roles &amp; Moderation:</strong> Distinguish between presenters and attendees, with granular control over permissions. A dedicated admin can manage the audience, while the presenter focuses on content delivery, as detailed in our post on <a class=\"\" href=\"https://www.plugnmeet.org/blog/open-source-webinar-software-platform\">open source webinar software platforms</a>.</li>\n<li class=\"\"><strong>Structured Engagement:</strong> Move beyond chaotic chat. Implement live polls for Q&amp;A, quizzes, and use features like \"Raise Hand\" for organized interaction.</li>\n<li class=\"\"><strong>Complete Branding:</strong> Your webinar is your show. Open-source solutions, especially those built with an API-first approach like plugNmeet, allow for deep customization – from logos and colors to entirely rebuilding the layout to match your brand identity perfectly.</li>\n<li class=\"\"><strong>Content Ownership:</strong> Recordings are valuable assets. With open-source, you own your content, saving it to your servers and automating post-webinar workflows (e.g., uploading to YouTube, sending to attendees).</li>\n</ol>\n<p>Open source isn't just about cost; it's about community, transparency, and the ability to adapt the software to your exact needs, rather than adapting your needs to the software.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"why-self-hosted-meeting-solutions-are-non-negotiable-for-control--privacy\">Why Self-Hosted Meeting Solutions are Non-Negotiable for Control &amp; Privacy<a href=\"https://www.plugnmeet.org/blog/self-hosted-open-source-webinar-software#why-self-hosted-meeting-solutions-are-non-negotiable-for-control--privacy\" class=\"hash-link\" aria-label=\"Direct link to Why Self-Hosted Meeting Solutions are Non-Negotiable for Control &amp; Privacy\" title=\"Direct link to Why Self-Hosted Meeting Solutions are Non-Negotiable for Control &amp; Privacy\" translate=\"no\">​</a></h2>\n<p>The \"self-hosted meeting\" aspect takes control to the next level. When you host your own video conferencing infrastructure, you gain benefits that proprietary cloud services simply cannot offer:</p>\n<ul>\n<li class=\"\"><strong>Unmatched Data Ownership &amp; Privacy:</strong> Your conversations, recordings, and user data reside entirely within your infrastructure. This is critical for compliance with regulations like GDPR, HIPAA, and other industry-specific privacy standards. You are in command of your data, not a third-party vendor.</li>\n<li class=\"\"><strong>Cost-Effectiveness at Scale:</strong> Proprietary solutions often charge per-user or per-minute, leading to escalating costs. Self-hosting offers predictable infrastructure costs, making it a significantly more cost-effective solution for long-term, large-scale deployments. For strategies on <a class=\"\" href=\"https://www.plugnmeet.org/blog/hosting-large-scale-events-the-smart-way\">hosting large-scale events</a>, we recommend a broadcast studio model to maximize scalability and minimize costs.</li>\n<li class=\"\"><strong>Tailored Performance &amp; Reliability:</strong> By hosting on your own servers, you can optimize network and resources specifically for your users. This ensures a smoother, more reliable experience, especially when combined with smart streaming techniques like Simulcast and Dynacast, which adapt video quality to individual network conditions and pause off-screen video streams to save bandwidth, as explained in our article on <a class=\"\" href=\"https://www.plugnmeet.org/blog/smart-video-streaming\">smart video streaming</a>.</li>\n<li class=\"\"><strong>Deep Customization &amp; Integration:</strong> Beyond just branding, self-hosting allows for profound customization. You can integrate video conferencing directly into your existing applications and workflows, using powerful APIs to define feature sets for each room, embed web content, and even connect with traditional telephony via <a class=\"\" href=\"https://www.plugnmeet.org/blog/sip-dial-in-for-video-conferencing\">SIP Dial-In</a> for participants without internet access.</li>\n</ul>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"plugnmeet-your-foundation-for-open-source-self-hosted-excellence\">plugNmeet: Your Foundation for Open Source, Self-Hosted Excellence<a href=\"https://www.plugnmeet.org/blog/self-hosted-open-source-webinar-software#plugnmeet-your-foundation-for-open-source-self-hosted-excellence\" class=\"hash-link\" aria-label=\"Direct link to plugNmeet: Your Foundation for Open Source, Self-Hosted Excellence\" title=\"Direct link to plugNmeet: Your Foundation for Open Source, Self-Hosted Excellence\" translate=\"no\">​</a></h2>\n<p>plugNmeet is built from the ground up to deliver on these promises. As an open-source, WebRTC-based platform, it provides the flexible, powerful, and secure foundation you need to build a professional, branded, and automated webinar and meeting experience that is truly your own.</p>\n<p>Key features that highlight our commitment to open source and self-hosting include:</p>\n<ul>\n<li class=\"\"><strong>Adaptive Streaming (Simulcast &amp; Dynacast):</strong> Ensures smooth, uninterrupted calls even on challenging networks by intelligently managing video streams.</li>\n<li class=\"\"><strong>RTMP &amp; WHIP Ingress:</strong> Stream like a pro by bringing professional tools like OBS Studio directly into your meeting room, appearing as a participant, as detailed in our guide on <a class=\"\" href=\"https://www.plugnmeet.org/blog/obs-rtmp-whip-ingress\">OBS RTMP WHIP Ingress</a>.</li>\n<li class=\"\"><strong>SIP Dial-In:</strong> Bridge the gap for participants without internet access, allowing them to join audio via any standard telephone.</li>\n<li class=\"\"><strong>AI Meeting Agent:</strong> Enhance productivity with live spoken translations, automated summaries, and full transcriptions.</li>\n<li class=\"\"><strong>End-to-End Encryption (E2EE):</strong> For the <a class=\"\" href=\"https://www.plugnmeet.org/docs/security-overview\">highest level of privacy</a>, ensuring your conversations remain completely confidential.</li>\n<li class=\"\"><strong>Comprehensive APIs &amp; SDKs:</strong> For developers to build custom solutions and integrate deeply with existing systems.</li>\n<li class=\"\"><strong>CMS Plugins:</strong> Ready-to-use plugins for WordPress, Joomla, and Moodle for easy integration without coding. For example, you can <a class=\"\" href=\"https://www.plugnmeet.org/blog/no-code-video-conferencing-service-with-wordpress\">start your Webinar meeting with WordPress</a> in minutes.</li>\n</ul>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"take-control-of-your-communication-future\">Take Control of Your Communication Future<a href=\"https://www.plugnmeet.org/blog/self-hosted-open-source-webinar-software#take-control-of-your-communication-future\" class=\"hash-link\" aria-label=\"Direct link to Take Control of Your Communication Future\" title=\"Direct link to Take Control of Your Communication Future\" translate=\"no\">​</a></h2>\n<p>In a world where digital interactions are paramount, settling for generic, proprietary solutions means sacrificing control, privacy, and the ability to truly brand and tailor your user experience.</p>\n<p>Choose an <strong>open source webinar software</strong> and <strong>self-hosted meeting</strong> platform like plugNmeet to empower your organization with a secure, scalable, and fully customized video conferencing solution. Reclaim ownership of your communication and deliver an unparalleled experience to your users.</p>\n<hr>\n<p><strong>Ready to build your perfect webinar and meeting platform?</strong></p>\n<ul>\n<li class=\"\"><strong>Explore our <a href=\"https://demo.plugnmeet.com/landing.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Live Demo</a> to see the features in action.</strong></li>\n<li class=\"\"><strong>Visit our <a href=\"https://github.com/mynaparrot/plugNmeet-server\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">GitHub Repository</a> to get started with self-hosting.</strong></li>\n<li class=\"\"><strong>Dive into our <a class=\"\" href=\"https://www.plugnmeet.org/docs/api/intro\">API Documentation</a> to begin building your custom solution.</strong></li>\n</ul>",
            "url": "https://www.plugnmeet.org/blog/self-hosted-open-source-webinar-software",
            "title": "Unlock True Control: Why Self-Hosted, Open Source Webinar Software is Your Best Bet",
            "summary": "In an era dominated by digital communication, webinars and online meetings have become indispensable. Yet, many organizations find themselves trapped in the rigid, often expensive, ecosystems of proprietary software like Zoom or GoToWebinar. These tools, while convenient, often come with hidden costs, limited customization, and significant concerns about data privacy and ownership.",
            "date_modified": "2026-04-14T00:00:00.000Z",
            "author": {
                "name": "Chaboud Simon",
                "url": "https://github.com/saimonzone"
            },
            "tags": [
                "webinar",
                "open-source",
                "self-hosted",
                "video-conferencing",
                "platform",
                "privacy",
                "customization",
                "control",
                "scalability",
                "events"
            ]
        },
        {
            "id": "https://www.plugnmeet.org/blog/self-hosted-customizable-video-conferencing",
            "content_html": "<p>In today's interconnected world, video conferencing has become indispensable. But as businesses, educators, and healthcare professionals rely more heavily on these tools, a critical question arises: <strong>who truly controls your communication?</strong> Proprietary solutions often come with hidden costs, limited flexibility, and significant privacy concerns.</p>\n<p>Enter plugNmeet: an open-source, self-hosted WebRTC platform designed to give you ultimate control, unparalleled customization, and robust security, fundamentally outperforming generic, off-the-shelf alternatives.</p>\n<hr>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"the-power-of-self-hosting-unmatched-control--privacy\">The Power of Self-Hosting: Unmatched Control &amp; Privacy<a href=\"https://www.plugnmeet.org/blog/self-hosted-customizable-video-conferencing#the-power-of-self-hosting-unmatched-control--privacy\" class=\"hash-link\" aria-label=\"Direct link to The Power of Self-Hosting: Unmatched Control &amp; Privacy\" title=\"Direct link to The Power of Self-Hosting: Unmatched Control &amp; Privacy\" translate=\"no\">​</a></h2>\n<p>When you choose a proprietary video conferencing service, your data resides on their servers, subject to their policies, security practices, and potential data mining. Self-hosting with plugNmeet flips this script, putting you firmly in command.</p>\n<ul>\n<li class=\"\"><strong>Data Ownership &amp; Privacy:</strong> Your conversations, recordings, and user data remain entirely within your infrastructure. This is crucial for compliance with regulations like HIPAA, GDPR, and other industry-specific privacy standards. With plugNmeet, you own your data, ensuring maximum privacy and peace of mind. For a deeper dive into our security architecture, refer to our <a class=\"\" href=\"https://www.plugnmeet.org/docs/security-overview\">Security Overview</a>.</li>\n<li class=\"\"><strong>Cost-Effectiveness at Scale:</strong> Proprietary solutions often charge per-user or per-minute, leading to escalating costs as your needs grow. Self-hosting offers predictable infrastructure costs, making it a significantly more cost-effective solution for long-term, large-scale deployments.</li>\n<li class=\"\"><strong>Reliability &amp; Performance:</strong> By hosting on your own servers, you can optimize your network and resources specifically for your users, ensuring a smoother, more reliable experience tailored to your audience's geographical location and bandwidth.</li>\n</ul>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"beyond-branding-true-customization-for-your-unique-needs\">Beyond Branding: True Customization for Your Unique Needs<a href=\"https://www.plugnmeet.org/blog/self-hosted-customizable-video-conferencing#beyond-branding-true-customization-for-your-unique-needs\" class=\"hash-link\" aria-label=\"Direct link to Beyond Branding: True Customization for Your Unique Needs\" title=\"Direct link to Beyond Branding: True Customization for Your Unique Needs\" translate=\"no\">​</a></h2>\n<p>Many \"white-label\" solutions merely allow you to swap a logo. plugNmeet offers a level of customization that transforms the platform into an integral part of your ecosystem, not just an embedded tool.</p>\n<ul>\n<li class=\"\"><strong>Pixel-Perfect Branding:</strong> From custom URLs and logos to colors and layouts, plugNmeet allows you to tailor every visual aspect to match your brand identity seamlessly. For more on this, see our post on <a class=\"\" href=\"https://www.plugnmeet.org/blog/true-white-label-video-conferencing\">True White-Label Video Conferencing</a>. Our <code>getClientFiles</code> API even allows you to inject the client directly into your application for a truly native feel, as detailed in our <a class=\"\" href=\"https://www.plugnmeet.org/docs/developer-guide/design-customisation\">Design Customization Guide</a> and <a class=\"\" href=\"https://www.plugnmeet.org/docs/api/get-client-files\">getClientFiles API Documentation</a>.</li>\n<li class=\"\"><strong>Feature Control via API:</strong> Our powerful server-side API lets you define the exact feature set for each room. Need a minimalist one-to-one chat? Disable the whiteboard and polls. Building a virtual classroom? Enable breakout rooms, shared notepads, and advanced moderation tools. This granular control ensures your users only see what's relevant, creating a streamlined and focused experience.</li>\n<li class=\"\"><strong>Tailored Workflows:</strong> Whether you're building a telehealth platform, an online learning environment, or a secure corporate communication hub, plugNmeet's flexibility allows you to integrate video conferencing directly into your existing workflows and applications.</li>\n</ul>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"feature-spotlight-what-makes-plugnmeet-stand-out\">Feature Spotlight: What Makes plugNmeet Stand Out<a href=\"https://www.plugnmeet.org/blog/self-hosted-customizable-video-conferencing#feature-spotlight-what-makes-plugnmeet-stand-out\" class=\"hash-link\" aria-label=\"Direct link to Feature Spotlight: What Makes plugNmeet Stand Out\" title=\"Direct link to Feature Spotlight: What Makes plugNmeet Stand Out\" translate=\"no\">​</a></h2>\n<p>While control and customization are paramount, plugNmeet also boasts a rich set of features that rival, and often surpass, proprietary offerings.</p>\n<ul>\n<li class=\"\"><strong>AI Meeting Agent:</strong> Transform your meetings into actionable insights with live spoken translations, automated summaries, and full transcriptions. This intelligent agent enhances productivity and accessibility. Learn more about <a class=\"\" href=\"https://www.plugnmeet.org/blog/how-to-add-ai-meeting-assistant-features\">How to Add AI Features</a>.</li>\n<li class=\"\"><strong>End-to-End Encryption (E2EE):</strong> Beyond standard WebRTC security, plugNmeet adds an additional layer of true E2EE, ensuring that even the server cannot access the content of your private conversations. This is privacy by design. Explore our <a class=\"\" href=\"https://www.plugnmeet.org/blog/e2ee-key-models-guide\">E2EE Key Models Guide</a> and <a class=\"\" href=\"https://www.plugnmeet.org/blog/how-to-enable-end-to-end-encryption\">How to Enable End-to-End Encryption</a>.</li>\n<li class=\"\"><strong>Advanced Collaboration Tools:</strong> Boost productivity with interactive whiteboards, collaborative notepads, screen sharing, and breakout rooms for focused group discussions.</li>\n<li class=\"\"><strong>Scalability &amp; Performance:</strong> Built with modern technologies like Go, LiveKit, and NATS, plugNmeet is engineered for horizontal scaling, supporting hundreds of participants with optimal performance, even on challenging networks thanks to adaptive streaming. Discover how our <a class=\"\" href=\"https://www.plugnmeet.org/blog/scaling-architecture-saves-money\">Scaling Architecture Saves Money</a> and dive into our <a class=\"\" href=\"https://www.plugnmeet.org/blog/backend-architecture-deep-dive\">Backend Architecture Deep Dive</a>.</li>\n<li class=\"\"><strong>Recording &amp; Broadcasting:</strong> Capture sessions in HD as portable MP4 files to your own server, or stream live to platforms like YouTube and Facebook using built-in RTMP/WHIP support. You own your content, always. Read our <a class=\"\" href=\"https://www.plugnmeet.org/blog/guide-to-managing-meeting-recordings\">Guide to Managing Meeting Recordings</a> and learn about <a class=\"\" href=\"https://www.plugnmeet.org/blog/smart-scaling-ha-recorder\">Smart Scaling HA Recorder</a>.</li>\n</ul>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"seamless-integration--migration\">Seamless Integration &amp; Migration<a href=\"https://www.plugnmeet.org/blog/self-hosted-customizable-video-conferencing#seamless-integration--migration\" class=\"hash-link\" aria-label=\"Direct link to Seamless Integration &amp; Migration\" title=\"Direct link to Seamless Integration &amp; Migration\" translate=\"no\">​</a></h2>\n<p>Worried about switching from an existing platform? plugNmeet makes it easy.</p>\n<ul>\n<li class=\"\"><strong>Easy Migration:</strong> For users looking to move away from platforms like BigBlueButton, plugNmeet offers a modern, performant, and highly customizable alternative. Our architecture simplifies the transition, allowing you to leverage your existing knowledge while gaining superior control and features. See our guide on <a class=\"\" href=\"https://www.plugnmeet.org/docs/tutorials/migration-from-bbb\">Migration from BigBlueButton</a>.</li>\n<li class=\"\"><strong>CMS Plugins:</strong> Integrate plugNmeet effortlessly into popular content management systems like WordPress, Joomla, and Moodle with our official plugins, requiring no coding. Find out more in our guides for <a class=\"\" href=\"https://www.plugnmeet.org/blog/wordpress-video-conferencing-guide\">WordPress Video Conferencing</a>, <a class=\"\" href=\"https://www.plugnmeet.org/blog/joomla-video-conferencing-guide\">Joomla Video Conferencing</a>, and <a class=\"\" href=\"https://www.plugnmeet.org/blog/moodle-video-conferencing-guide\">Moodle Video Conferencing</a>.</li>\n<li class=\"\"><strong>Developer-Friendly SDKs:</strong> For custom applications, our comprehensive <a class=\"\" href=\"https://www.plugnmeet.org/docs/api/intro\">API Documentation</a> and SDKs (PHP, JavaScript, Python) provide the tools developers need for deep integration.</li>\n</ul>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"conclusion-choose-control-choose-customization-choose-plugnmeet\">Conclusion: Choose Control, Choose Customization, Choose plugNmeet<a href=\"https://www.plugnmeet.org/blog/self-hosted-customizable-video-conferencing#conclusion-choose-control-choose-customization-choose-plugnmeet\" class=\"hash-link\" aria-label=\"Direct link to Conclusion: Choose Control, Choose Customization, Choose plugNmeet\" title=\"Direct link to Conclusion: Choose Control, Choose Customization, Choose plugNmeet\" translate=\"no\">​</a></h2>\n<p>In a world increasingly reliant on digital communication, settling for generic, proprietary video conferencing solutions means sacrificing control, privacy, and the ability to truly brand and tailor your user experience.</p>\n<p>plugNmeet offers a powerful, open-source alternative that empowers you to build a secure, scalable, and fully customized video conferencing platform that is truly your own. Reclaim ownership of your communication and deliver an unparalleled experience to your users.</p>\n<p><strong>Ready to take control of your video conferencing?</strong></p>\n<ul>\n<li class=\"\"><strong>Explore our <a href=\"https://demo.plugnmeet.com/landing.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Live Demo</a> to see the features in action.</strong></li>\n<li class=\"\"><strong>Visit our <a href=\"https://github.com/mynaparrot/plugNmeet-server\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">GitHub Repository</a> to get started with self-hosting.</strong></li>\n<li class=\"\"><strong>Dive into our <a class=\"\" href=\"https://www.plugnmeet.org/docs/api/intro\">API Documentation</a> to begin building your custom solution.</strong></li>\n</ul>",
            "url": "https://www.plugnmeet.org/blog/self-hosted-customizable-video-conferencing",
            "title": "Why Self-Hosted, Customizable Video Conferencing with plugNmeet Outperforms Proprietary Solutions",
            "summary": "In today's interconnected world, video conferencing has become indispensable. But as businesses, educators, and healthcare professionals rely more heavily on these tools, a critical question arises: who truly controls your communication? Proprietary solutions often come with hidden costs, limited flexibility, and significant privacy concerns.",
            "date_modified": "2026-03-30T00:00:00.000Z",
            "author": {
                "name": "Chaboud Simon",
                "url": "https://github.com/saimonzone"
            },
            "tags": [
                "self-hosted",
                "open-source",
                "customization",
                "white-label",
                "security",
                "webrtc",
                "zoom-alternative",
                "video-conferencing",
                "platform"
            ]
        },
        {
            "id": "https://www.plugnmeet.org/blog/how-to-reduce-video-conferencing-costs-with-open-source",
            "content_html": "<p>Your organization relies on video conferencing. It's essential for team meetings, sales demos, and customer support. But as your company grows, your monthly bill from services like Zoom, Google Meet, or Microsoft Teams is growing with it. That predictable per-user, per-month fee is starting to look less like a convenience and more like a major operational expense.</p>\n<p>What if you could cut that bill by up to 80% while gaining more control and better branding?</p>\n<p>It's not a fantasy. By switching from a proprietary SaaS \"rental\" model to a self-hosted \"ownership\" model with an open-source platform like <strong>Plug-N-Meet</strong>, you can dramatically reduce your Total Cost of Ownership (TCO). This article will break down the numbers and show you how.</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"the-saas-rental-model-a-penalty-on-growth\">The SaaS \"Rental\" Model: A Penalty on Growth<a href=\"https://www.plugnmeet.org/blog/how-to-reduce-video-conferencing-costs-with-open-source#the-saas-rental-model-a-penalty-on-growth\" class=\"hash-link\" aria-label=\"Direct link to The SaaS &quot;Rental&quot; Model: A Penalty on Growth\" title=\"Direct link to The SaaS &quot;Rental&quot; Model: A Penalty on Growth\" translate=\"no\">​</a></h3>\n<p>The pricing model for most SaaS video platforms is simple and seductive: a flat fee per host, per month. It's easy to budget for when you have 10 employees. But what happens when you have 100?</p>\n<p>Let's look at a common scenario.</p>\n<p><strong>Scenario: A Company with 50 Hosts</strong></p>\n<ul>\n<li class=\"\"><strong>SaaS Provider Cost (e.g., Zoom Pro):</strong>\n<ul>\n<li class=\"\">Price per host: ~$15.99 / month</li>\n<li class=\"\">Total monthly cost: 50 hosts * $15.99 = <strong>$799.50 / month</strong></li>\n<li class=\"\">Total annual cost: <strong>$9,594 / year</strong></li>\n</ul>\n</li>\n</ul>\n<p>This is a significant, recurring operational expense (OpEx). And it gets worse. This model penalizes you for success. If your company grows to 100 hosts, your bill doubles to nearly <strong>$20,000 per year</strong>. You are paying more for the exact same service, simply because your team is bigger.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"the-self-hosted-ownership-model-predictable-and-scalable\">The Self-Hosted \"Ownership\" Model: Predictable and Scalable<a href=\"https://www.plugnmeet.org/blog/how-to-reduce-video-conferencing-costs-with-open-source#the-self-hosted-ownership-model-predictable-and-scalable\" class=\"hash-link\" aria-label=\"Direct link to The Self-Hosted &quot;Ownership&quot; Model: Predictable and Scalable\" title=\"Direct link to The Self-Hosted &quot;Ownership&quot; Model: Predictable and Scalable\" translate=\"no\">​</a></h3>\n<p>Now, let's compare this to a self-hosted model using the open-source Plug-N-Meet platform. With this model, you don't pay any license fees. Your only significant cost is the server hardware you run the software on.</p>\n<p><strong>Scenario: The Same Company with 50 Hosts (or 100, or 200)</strong></p>\n<ul>\n<li class=\"\"><strong>Self-Hosted Infrastructure Cost (e.g., Hetzner Cloud Server):</strong>\n<ul>\n<li class=\"\">Recommended server: A powerful cloud instance like the Hetzner CCX33 (8 vCPUs, 32 GB RAM).</li>\n<li class=\"\">Total monthly cost: <strong>~€55 / month (approx. $60 / month)</strong></li>\n<li class=\"\">Total annual cost: <strong>~$720 / year</strong></li>\n</ul>\n</li>\n</ul>\n<p>Let's compare the annual costs:</p>\n<ul>\n<li class=\"\"><strong>SaaS Model:</strong> <strong>$9,594</strong></li>\n<li class=\"\"><strong>Self-Hosted Model:</strong> <strong>$720</strong></li>\n</ul>\n<p>That's a cost reduction of <strong>over 92%</strong>.</p>\n<p>Even if you choose a more expensive cloud provider or a larger server for higher capacity, the savings are dramatic. A comparable server on DigitalOcean or AWS might cost $150-$250 per month, which still represents a <strong>cost saving of over 80%</strong> compared to the SaaS alternative.</p>\n<p>The best part? This cost is <strong>fixed</strong>. If your company grows from 50 to 100 hosts, your server cost doesn't change. You can accommodate more users for the same price, rewarding your growth instead of penalizing it.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"but-what-about-hidden-costs\">But What About \"Hidden\" Costs?<a href=\"https://www.plugnmeet.org/blog/how-to-reduce-video-conferencing-costs-with-open-source#but-what-about-hidden-costs\" class=\"hash-link\" aria-label=\"Direct link to But What About &quot;Hidden&quot; Costs?\" title=\"Direct link to But What About &quot;Hidden&quot; Costs?\" translate=\"no\">​</a></h3>\n<p>Skeptics of self-hosting often point to hidden costs like maintenance and setup. Let's address those directly.</p>\n<ul>\n<li class=\"\"><strong>Setup Complexity:</strong> This used to be a valid concern. Today, with automated tools, it's largely a solved problem. Plug-N-Meet provides a <strong><a class=\"\" href=\"https://www.plugnmeet.org/docs/installation\">one-line installation script</a></strong> that sets up a production-ready server in about 15 minutes. You don't need to be a systems administration expert.</li>\n<li class=\"\"><strong>Maintenance:</strong> The server requires minimal maintenance. Running a software update is as simple as executing another one-line script. For most organizations, this amounts to less than an hour of work per month.</li>\n<li class=\"\"><strong>Reliability:</strong> Modern cloud providers offer extremely high uptime (often 99.99%). Your self-hosted solution, running on reliable infrastructure, is just as dependable as a SaaS service, with the added benefit that you aren't affected by their widespread outages.</li>\n</ul>\n<p>When you weigh a few hours of optional maintenance per year against a potential saving of thousands of dollars, the financial case becomes overwhelmingly clear.</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"conclusion-stop-renting-start-owning\">Conclusion: Stop Renting, Start Owning<a href=\"https://www.plugnmeet.org/blog/how-to-reduce-video-conferencing-costs-with-open-source#conclusion-stop-renting-start-owning\" class=\"hash-link\" aria-label=\"Direct link to Conclusion: Stop Renting, Start Owning\" title=\"Direct link to Conclusion: Stop Renting, Start Owning\" translate=\"no\">​</a></h3>\n<p>The per-user, per-month SaaS model is a brilliant business strategy—for the provider. For the customer, it's a treadmill of escalating costs that actively works against your growth.</p>\n<p>By switching to a self-hosted, open-source platform like Plug-N-Meet, you move from being a tenant to an owner. You gain a predictable, low-cost infrastructure that scales with your success. You also get the invaluable strategic benefits of full brand control, deeper integration, and absolute data privacy.</p>\n<p>If your organization is feeling the pain of rising SaaS subscription costs, it's time to run the numbers. The move to self-hosting isn't just a technical decision; it's one of the smartest financial decisions you can make.</p>\n<hr>\n<p><strong>Ready to slash your video conferencing costs?</strong></p>\n<ul>\n<li class=\"\"><strong>See our detailed <a class=\"\" href=\"https://www.plugnmeet.org/blog/scaling-architecture-saves-money\">Server Recommendations</a>.</strong></li>\n<li class=\"\"><strong>Follow our simple <a class=\"\" href=\"https://www.plugnmeet.org/docs/installation\">Installation Guide</a> to get started.</strong></li>\n<li class=\"\"><strong>Read more about the <a class=\"\" href=\"https://www.plugnmeet.org/blog/own-vs-rent-video-platform\">Business Case for Self-Hosting</a>.</strong></li>\n</ul>",
            "url": "https://www.plugnmeet.org/blog/how-to-reduce-video-conferencing-costs-with-open-source",
            "title": "How to Reduce Video Conferencing Costs by up to 80% with Open-Source Software",
            "summary": "Your organization relies on video conferencing. It's essential for team meetings, sales demos, and customer support. But as your company grows, your monthly bill from services like Zoom, Google Meet, or Microsoft Teams is growing with it. That predictable per-user, per-month fee is starting to look less like a convenience and more like a major operational expense.",
            "date_modified": "2026-03-05T00:00:00.000Z",
            "author": {
                "name": "Chaboud Simon",
                "url": "https://github.com/saimonzone"
            },
            "tags": [
                "business",
                "cost-saving",
                "tco",
                "open-source",
                "saas-alternative",
                "pricing",
                "self-hosting"
            ]
        },
        {
            "id": "https://www.plugnmeet.org/blog/how-to-secure-your-online-classroom-for-teachers",
            "content_html": "<p>As an educator, your primary goal is to create a safe, respectful, and effective learning environment. In a physical classroom, you have established routines for this: you close the door to prevent interruptions, you set rules for speaking, and you manage the flow of the conversation.</p>\n<p>But how do you replicate this in a virtual classroom? An unsecured online meeting can quickly devolve into chaos, with uninvited guests, background noise, and constant interruptions.</p>\n<p>This guide will walk you through the simple but powerful tools available in Plug-N-Meet that allow you to take control of your digital classroom. Think of these features as your new classroom management toolkit, designed to ensure your online sessions are as safe, private, and productive as your in-person ones.</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"1-lock-the-door-the-waiting-room\">1. Lock the Door: The Waiting Room<a href=\"https://www.plugnmeet.org/blog/how-to-secure-your-online-classroom-for-teachers#1-lock-the-door-the-waiting-room\" class=\"hash-link\" aria-label=\"Direct link to 1. Lock the Door: The Waiting Room\" title=\"Direct link to 1. Lock the Door: The Waiting Room\" translate=\"no\">​</a></h3>\n<p>You wouldn't let just anyone walk into your classroom off the street. The same principle should apply online. The <strong>Waiting Room</strong> is your digital lobby, giving you complete control over who enters your session.</p>\n<ul>\n<li class=\"\"><strong>How it Works:</strong> When you enable the Waiting Room for your class, any student who clicks the join link is placed on a \"waiting\" screen. You, the teacher, will see a notification and a list of everyone waiting to get in. You can then admit them one by one or all at once.</li>\n<li class=\"\"><strong>Why it's Essential:</strong>\n<ul>\n<li class=\"\"><strong>Prevents \"Zoombombing\":</strong> This is the single most effective way to stop uninvited guests from disrupting your class. If you don't recognize a name, you don't have to let them in.</li>\n<li class=\"\"><strong>Control the Start Time:</strong> It allows you to prepare your lesson materials and admit all students at the same time, ensuring everyone starts on the same page.</li>\n</ul>\n</li>\n</ul>\n<p><strong>How to Enable it:</strong> You can enable the Waiting Room in your room settings when you create the class in your LMS (like <strong><a class=\"\" href=\"https://www.plugnmeet.org/blog/no-code-video-conferencing-moodle\">Moodle</a></strong>) or via the API.</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"2-set-the-rules-granular-lock-settings\">2. Set the Rules: Granular Lock Settings<a href=\"https://www.plugnmeet.org/blog/how-to-secure-your-online-classroom-for-teachers#2-set-the-rules-granular-lock-settings\" class=\"hash-link\" aria-label=\"Direct link to 2. Set the Rules: Granular Lock Settings\" title=\"Direct link to 2. Set the Rules: Granular Lock Settings\" translate=\"no\">​</a></h3>\n<p>Once your students are in the room, you need to set the ground rules for participation. Plug-N-Meet's <strong><a class=\"\" href=\"https://www.plugnmeet.org/blog/moderator-guide-room-lock-settings\">Room Lock Settings</a></strong> panel is your command center for this.</p>\n<ul>\n<li class=\"\"><strong>Lock Microphones (Your Most-Used Tool):</strong> This is the equivalent of asking the class to be quiet while you're speaking. It mutes all participants and prevents them from unmuting themselves. This is essential for lectures, presentations, or when a specific student is speaking.</li>\n<li class=\"\"><strong>Lock Webcams:</strong> In large classes, having dozens of webcams active can be distracting and consume a lot of bandwidth. Locking webcams helps focus attention on the speaker or the shared content.</li>\n<li class=\"\"><strong>Lock Public Chat:</strong> While chat is a great tool, it can sometimes become a source of distraction. You can temporarily lock the chat to bring focus back to the lesson, and then unlock it for a dedicated Q&amp;A session.</li>\n<li class=\"\"><strong>Lock Screen Sharing:</strong> Keep this locked by default to prevent students from accidentally taking over the presentation. You can unlock it when you want a student to share their work.</li>\n</ul>\n<p><strong>Pro Tip:</strong> Use these settings dynamically. Start your class with everything unlocked for a few minutes of social time, then lock microphones and chat when the formal lesson begins.</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"3-the-gold-standard-of-privacy-end-to-end-encryption-e2ee\">3. The Gold Standard of Privacy: End-to-End Encryption (E2EE)<a href=\"https://www.plugnmeet.org/blog/how-to-secure-your-online-classroom-for-teachers#3-the-gold-standard-of-privacy-end-to-end-encryption-e2ee\" class=\"hash-link\" aria-label=\"Direct link to 3. The Gold Standard of Privacy: End-to-End Encryption (E2EE)\" title=\"Direct link to 3. The Gold Standard of Privacy: End-to-End Encryption (E2EE)\" translate=\"no\">​</a></h3>\n<p>For sensitive conversations, such as a one-on-one meeting with a student or a parent, you may need the highest possible level of privacy. <strong>End-to-End Encryption (E2EE)</strong> provides a mathematical guarantee that no one—not even the server administrator—can access the audio or video of your conversation.</p>\n<ul>\n<li class=\"\"><strong>How it Works:</strong> When E2EE is enabled, the video and audio are encrypted on your device and only decrypted on the recipient's device.</li>\n<li class=\"\"><strong>When to Use It:</strong> This is ideal for any situation requiring absolute confidentiality, such as discussions about grades, student well-being, or any other private matter.</li>\n</ul>\n<p><strong>How to Enable it:</strong> E2EE can be enabled on a per-room basis. Our guide on <strong><a class=\"\" href=\"https://www.plugnmeet.org/blog/how-to-enable-end-to-end-encryption\">How to Enable E2EE</a></strong> explains the simple steps. It's important to note that when you enable the highest level of E2EE, features that require the server to \"see\" the media, like cloud recording, are automatically disabled for your protection.</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"conclusion-you-are-in-control\">Conclusion: You Are in Control<a href=\"https://www.plugnmeet.org/blog/how-to-secure-your-online-classroom-for-teachers#conclusion-you-are-in-control\" class=\"hash-link\" aria-label=\"Direct link to Conclusion: You Are in Control\" title=\"Direct link to Conclusion: You Are in Control\" translate=\"no\">​</a></h3>\n<p>A secure and productive online classroom doesn't happen by accident. It happens by design. By using these simple but powerful features, you can move from being a passive host to an active and effective classroom manager.</p>\n<p>Start with the Waiting Room to control who enters, use the Lock Settings to manage the flow of conversation, and deploy E2EE for sensitive discussions. With these tools at your fingertips, you can create a virtual learning environment that is just as safe, private, and conducive to learning as your physical classroom.</p>\n<hr>\n<p><strong>Ready to take control of your virtual classroom?</strong></p>\n<ul>\n<li class=\"\"><strong>Try these moderator features in our <a href=\"https://demo.plugnmeet.com/landing.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Live Demo</a>.</strong></li>\n<li class=\"\"><strong>Read our full <a class=\"\" href=\"https://www.plugnmeet.org/docs/user-guide/moderator\">Moderator Guide</a> for more tips.</strong></li>\n<li class=\"\"><strong>Learn more about our commitment to <a class=\"\" href=\"https://www.plugnmeet.org/blog/privacy-by-design-zero-trust\">Privacy by Design</a>.</strong></li>\n</ul>",
            "url": "https://www.plugnmeet.org/blog/how-to-secure-your-online-classroom-for-teachers",
            "title": "How to Secure Your Online Classroom: A Teacher's Guide to Privacy and Safety",
            "summary": "As an educator, your primary goal is to create a safe, respectful, and effective learning environment. In a physical classroom, you have established routines for this: you close the door to prevent interruptions, you set rules for speaking, and you manage the flow of the conversation.",
            "date_modified": "2026-02-28T00:00:00.000Z",
            "author": {
                "name": "Bob Teng",
                "url": "https://github.com/wbobteng"
            },
            "tags": [
                "tutorial",
                "how-to",
                "security",
                "privacy",
                "education",
                "teacher",
                "moderator",
                "virtual-classroom"
            ]
        },
        {
            "id": "https://www.plugnmeet.org/blog/why-your-business-needs-a-white-labeled-video-conferencing-solution",
            "content_html": "<p>Your brand is your most valuable asset. You've invested countless hours and resources into building a unique identity, a memorable customer experience, and a relationship of trust with your audience.</p>\n<p>So why, at the most critical moment of interaction—a sales demo, a customer support call, or a training session—do you send your customers to a third-party platform with someone else's logo on it?</p>\n<p>Using a generic, off-the-shelf video conferencing tool is like a Michelin-starred restaurant serving its food in a McDonald's box. It breaks the experience, erodes brand value, and misses a huge opportunity. This is why forward-thinking businesses are moving away from generic SaaS tools and embracing <strong>white-labeled video conferencing solutions</strong> like Plug-N-Meet.</p>\n<p>A white-label platform isn't just about changing the logo. It's about owning the entire customer journey and transforming a simple utility into a powerful strategic asset.</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"1-it-builds-brand-trust-and-professionalism\">1. It Builds Brand Trust and Professionalism<a href=\"https://www.plugnmeet.org/blog/why-your-business-needs-a-white-labeled-video-conferencing-solution#1-it-builds-brand-trust-and-professionalism\" class=\"hash-link\" aria-label=\"Direct link to 1. It Builds Brand Trust and Professionalism\" title=\"Direct link to 1. It Builds Brand Trust and Professionalism\" translate=\"no\">​</a></h3>\n<p>Imagine a new client is paying you for a premium consultation. You send them a meeting link. Which of these inspires more confidence?</p>\n<ul>\n<li class=\"\"><code>your-company.zoom.us</code></li>\n<li class=\"\"><code>meet.your-company.com</code></li>\n</ul>\n<p>The answer is obvious. When the entire experience—from the URL to the logo in the corner to the brand colors of the interface—is yours, it sends a powerful message: you are professional, you are established, and you have invested in your own platform. It removes the jarring feeling of being handed off to another company and keeps the focus entirely on your brand.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"2-it-creates-a-seamless-frictionless-customer-experience\">2. It Creates a Seamless, Frictionless Customer Experience<a href=\"https://www.plugnmeet.org/blog/why-your-business-needs-a-white-labeled-video-conferencing-solution#2-it-creates-a-seamless-frictionless-customer-experience\" class=\"hash-link\" aria-label=\"Direct link to 2. It Creates a Seamless, Frictionless Customer Experience\" title=\"Direct link to 2. It Creates a Seamless, Frictionless Customer Experience\" translate=\"no\">​</a></h3>\n<p>A white-labeled solution, especially one that can be deeply integrated, eliminates the \"context switching\" that plagues generic tools. Instead of your user having to open a separate app or a new tab that looks completely different from your website, the video experience feels like a natural extension of your own digital property.</p>\n<p>With a platform like Plug-N-Meet, you can use the <strong><a class=\"\" href=\"https://www.plugnmeet.org/blog/deep-integration-white-label-guide\"><code>getClientFiles</code> API</a></strong> to embed the video client directly into your own application or customer portal. This means you can:</p>\n<ul>\n<li class=\"\">Place the video call next to relevant customer data for your support agents.</li>\n<li class=\"\">Allow users to join a webinar directly from your community page without ever leaving your site.</li>\n<li class=\"\">Create a \"single pane of glass\" experience where the video is just one integrated component of a larger workflow.</li>\n</ul>\n<p>This seamlessness reduces friction, improves usability, and keeps your user engaged within your ecosystem.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"3-you-control-the-feature-set-and-user-journey\">3. You Control the Feature Set and User Journey<a href=\"https://www.plugnmeet.org/blog/why-your-business-needs-a-white-labeled-video-conferencing-solution#3-you-control-the-feature-set-and-user-journey\" class=\"hash-link\" aria-label=\"Direct link to 3. You Control the Feature Set and User Journey\" title=\"Direct link to 3. You Control the Feature Set and User Journey\" translate=\"no\">​</a></h3>\n<p>Not all conversations are the same. A one-on-one sales call has very different needs than a 100-person training webinar.</p>\n<p>Generic platforms offer a one-size-fits-all approach. A white-label, API-first platform gives you granular control to tailor the experience to the specific use case.</p>\n<ul>\n<li class=\"\"><strong>For a simple support call:</strong> You can disable all features except video, audio, and chat to create a clean, minimalist interface.</li>\n<li class=\"\"><strong>For an interactive workshop:</strong> You can enable the whiteboard, breakout rooms, and polling to foster collaboration.</li>\n<li class=\"\"><strong>For a formal presentation:</strong> You can lock down all participant controls to ensure the focus remains on the speaker.</li>\n</ul>\n<p>This ability to programmatically define the user journey means you can deliver the perfect, purpose-built tool for every interaction, which is a level of service generic platforms can't match.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"4-it-turns-a-cost-center-into-a-potential-revenue-stream\">4. It Turns a Cost Center into a Potential Revenue Stream<a href=\"https://www.plugnmeet.org/blog/why-your-business-needs-a-white-labeled-video-conferencing-solution#4-it-turns-a-cost-center-into-a-potential-revenue-stream\" class=\"hash-link\" aria-label=\"Direct link to 4. It Turns a Cost Center into a Potential Revenue Stream\" title=\"Direct link to 4. It Turns a Cost Center into a Potential Revenue Stream\" translate=\"no\">​</a></h3>\n<p>When you pay a per-user subscription for a generic SaaS tool, video conferencing is a cost center that grows as your team grows.</p>\n<p>When you own your platform, you flip the model. Because a self-hosted solution like Plug-N-Meet has no per-user fees, you can offer video conferencing services to your own customers. With our official plugins for <strong><a class=\"\" href=\"https://www.plugnmeet.org/blog/no-code-video-conferencing-service-with-wordpress\">WordPress</a></strong> and <strong><a class=\"\" href=\"https://www.plugnmeet.org/blog/no-code-video-conferencing-service-with-joomla\">Joomla</a></strong>, you can even use membership and e-commerce extensions to sell access to premium, branded meeting rooms—no coding required.</p>\n<p>You transform a monthly expense into a revenue-generating product.</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"conclusion-stop-advertising-for-other-brands\">Conclusion: Stop Advertising for Other Brands<a href=\"https://www.plugnmeet.org/blog/why-your-business-needs-a-white-labeled-video-conferencing-solution#conclusion-stop-advertising-for-other-brands\" class=\"hash-link\" aria-label=\"Direct link to Conclusion: Stop Advertising for Other Brands\" title=\"Direct link to Conclusion: Stop Advertising for Other Brands\" translate=\"no\">​</a></h3>\n<p>Every time a customer joins a meeting on a generic platform, you are paying to advertise for that platform.</p>\n<p>A white-labeled video conferencing solution is an investment in your own brand. It enhances trust, creates a superior customer experience, and gives you the control to build a communication tool that perfectly fits your business. In a competitive market, owning this critical touchpoint is not a luxury; it's a strategic necessity.</p>\n<hr>\n<p><strong>Ready to put your brand front and center?</strong></p>\n<ul>\n<li class=\"\"><strong>Learn more about our <a class=\"\" href=\"https://www.plugnmeet.org/blog/deep-integration-white-label-guide\">Deep Integration and White-Labeling Capabilities</a>.</strong></li>\n<li class=\"\"><strong>Try our <a href=\"https://demo.plugnmeet.com/landing.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Live Demo</a> and imagine it with your own branding.</strong></li>\n<li class=\"\"><strong>Follow our <a class=\"\" href=\"https://www.plugnmeet.org/docs/installation\">Installation Guide</a> to start building your own platform.</strong></li>\n</ul>",
            "url": "https://www.plugnmeet.org/blog/why-your-business-needs-a-white-labeled-video-conferencing-solution",
            "title": "Why Your Business Needs a White-Labeled Video Conferencing Solution",
            "summary": "Your brand is your most valuable asset. You've invested countless hours and resources into building a unique identity, a memorable customer experience, and a relationship of trust with your audience.",
            "date_modified": "2026-02-23T00:00:00.000Z",
            "author": {
                "name": "Chaboud Simon",
                "url": "https://github.com/saimonzone"
            },
            "tags": [
                "business",
                "branding",
                "white-label",
                "customer-experience",
                "marketing",
                "saas-alternative"
            ]
        },
        {
            "id": "https://www.plugnmeet.org/blog/joomla-video-conferencing-guide",
            "content_html": "<p>Joomla is a powerhouse for building robust, flexible websites, from community portals to sophisticated business sites. But when it's time to engage your audience with live video, you might find yourself sending them away from the beautiful site you've built to a generic, third-party conferencing tool. This breaks the user experience and undermines your brand.</p>\n<p>What if you could integrate a secure, fully-branded, and powerful video conferencing solution directly into your Joomla site, making it a native part of your user's journey?</p>\n<p>With the official <strong>Plug-N-Meet component for Joomla</strong>, you can. This guide will show you how to transform your Joomla website into a dynamic communication platform, perfect for community meetings, webinars, online training, and even monetized services.</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"why-a-native-joomla-integration-beats-external-tools\">Why a Native Joomla Integration Beats External Tools<a href=\"https://www.plugnmeet.org/blog/joomla-video-conferencing-guide#why-a-native-joomla-integration-beats-external-tools\" class=\"hash-link\" aria-label=\"Direct link to Why a Native Joomla Integration Beats External Tools\" title=\"Direct link to Why a Native Joomla Integration Beats External Tools\" translate=\"no\">​</a></h3>\n<p>Before we get into the setup, let's be clear about the strategic advantages of using a self-hosted, integrated solution like Plug-N-Meet instead of an external SaaS tool.</p>\n<ol>\n<li class=\"\"><strong>A Seamless, Professional Brand Experience:</strong> Your users join meetings on your own domain, with your logo and brand colors. This reinforces professionalism and trust, keeping the user experience consistent with the rest of your site.</li>\n<li class=\"\"><strong>Full Control Over User Data:</strong> All user data and meeting recordings are stored on your own server, not in a third-party cloud. This is critical for maintaining data sovereignty and complying with privacy regulations like GDPR.</li>\n<li class=\"\"><strong>Cost-Effective Scaling:</strong> You escape the expensive per-user, per-month subscription fees that penalize growth. With a self-hosted model, your primary cost is a predictable server fee, allowing you to serve more users without your bill spiraling out of control.</li>\n</ol>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"the-3-step-guide-to-your-first-joomla-meeting\">The 3-Step Guide to Your First Joomla Meeting<a href=\"https://www.plugnmeet.org/blog/joomla-video-conferencing-guide#the-3-step-guide-to-your-first-joomla-meeting\" class=\"hash-link\" aria-label=\"Direct link to The 3-Step Guide to Your First Joomla Meeting\" title=\"Direct link to The 3-Step Guide to Your First Joomla Meeting\" translate=\"no\">​</a></h3>\n<p>You can have a live video meeting room on your Joomla site in under 15 minutes.</p>\n<h4 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"step-1-get-your-video-conferencing-engine\">Step 1: Get Your Video Conferencing Engine<a href=\"https://www.plugnmeet.org/blog/joomla-video-conferencing-guide#step-1-get-your-video-conferencing-engine\" class=\"hash-link\" aria-label=\"Direct link to Step 1: Get Your Video Conferencing Engine\" title=\"Direct link to Step 1: Get Your Video Conferencing Engine\" translate=\"no\">​</a></h4>\n<p>First, you need the server that will power your video calls. You can either self-host the open-source version with our <strong><a class=\"\" href=\"https://www.plugnmeet.org/docs/installation\">simple installation script</a></strong> or get an instant, managed server from <strong><a href=\"https://www.plugnmeet.cloud/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">plugnmeet Cloud</a></strong>. Both will provide you with an <strong>API Key</strong> and <strong>API Secret</strong>.</p>\n<h4 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"step-2-install-the-free-plug-n-meet-component\">Step 2: Install the Free Plug-N-Meet Component<a href=\"https://www.plugnmeet.org/blog/joomla-video-conferencing-guide#step-2-install-the-free-plug-n-meet-component\" class=\"hash-link\" aria-label=\"Direct link to Step 2: Install the Free Plug-N-Meet Component\" title=\"Direct link to Step 2: Install the Free Plug-N-Meet Component\" translate=\"no\">​</a></h4>\n<p>From your Joomla administrator panel:</p>\n<ol>\n<li class=\"\">Go to <strong>System &gt; Install &gt; Extensions</strong>.</li>\n<li class=\"\">Install the Plug-N-Meet component, which you can download from the official <a href=\"https://extensions.joomla.org/extension/plugnmeet/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Joomla Extensions Directory</a>.</li>\n<li class=\"\">After installation, navigate to <strong>System &gt; Global Configuration</strong> and select <strong>plugNmeet</strong> from the components list.</li>\n<li class=\"\">Enter your <strong>API Key</strong> and <strong>API Secret</strong> and save the configuration.</li>\n</ol>\n<p>Your Joomla site is now fully connected to your video conferencing engine.</p>\n<h4 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"step-3-create-and-display-your-room\">Step 3: Create and Display Your Room<a href=\"https://www.plugnmeet.org/blog/joomla-video-conferencing-guide#step-3-create-and-display-your-room\" class=\"hash-link\" aria-label=\"Direct link to Step 3: Create and Display Your Room\" title=\"Direct link to Step 3: Create and Display Your Room\" translate=\"no\">​</a></h4>\n<ol>\n<li class=\"\">Go to <strong>Components &gt; Plug N Meet &gt; Manage Rooms</strong> and click <strong>+ New</strong>.</li>\n<li class=\"\">Configure your room's features, such as its name, participant limits, and a welcome message.</li>\n<li class=\"\">To display the room, go to <strong>Menus &gt; Main Menu</strong> and click <strong>+ Add New Menu Item</strong>.</li>\n<li class=\"\">For the <strong>Menu Item Type</strong>, select <strong>Plug N Meet &gt; Single room</strong>.</li>\n<li class=\"\">In the <strong>Select a room</strong> dropdown, choose the room you just created and save the menu item.</li>\n</ol>\n<p>That's it. A new link will appear on your site's main menu, leading directly to your fully functional, branded video conferencing room.</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"level-up-3-powerful-ways-to-use-video-on-your-joomla-site\">Level Up: 3 Powerful Ways to Use Video on Your Joomla Site<a href=\"https://www.plugnmeet.org/blog/joomla-video-conferencing-guide#level-up-3-powerful-ways-to-use-video-on-your-joomla-site\" class=\"hash-link\" aria-label=\"Direct link to Level Up: 3 Powerful Ways to Use Video on Your Joomla Site\" title=\"Direct link to Level Up: 3 Powerful Ways to Use Video on Your Joomla Site\" translate=\"no\">​</a></h3>\n<p>With the basics in place, you can now leverage Plug-N-Meet to build sophisticated video-based services.</p>\n<h4 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"use-case-1-the-private-client-portal\">Use Case 1: The Private Client Portal<a href=\"https://www.plugnmeet.org/blog/joomla-video-conferencing-guide#use-case-1-the-private-client-portal\" class=\"hash-link\" aria-label=\"Direct link to Use Case 1: The Private Client Portal\" title=\"Direct link to Use Case 1: The Private Client Portal\" translate=\"no\">​</a></h4>\n<p>If you're a consultant, coach, or provide any one-on-one service, you can create private, secure meeting rooms for your clients.</p>\n<ul>\n<li class=\"\"><strong>How to do it:</strong> Use Joomla's built-in Access Control Levels (ACL) to create a user group for each client. Create a dedicated menu item for their private meeting room and set its <strong>Access</strong> level to their specific user group. Now, only that logged-in client can see and access their private session link.</li>\n</ul>\n<h4 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"use-case-2-the-community-webinar-platform\">Use Case 2: The Community Webinar Platform<a href=\"https://www.plugnmeet.org/blog/joomla-video-conferencing-guide#use-case-2-the-community-webinar-platform\" class=\"hash-link\" aria-label=\"Direct link to Use Case 2: The Community Webinar Platform\" title=\"Direct link to Use Case 2: The Community Webinar Platform\" translate=\"no\">​</a></h4>\n<p>Host live webinars and events directly on your site to engage your community.</p>\n<ul>\n<li class=\"\"><strong>How to do it:</strong> Create a new room and configure it for a webinar. In the room settings, you can disable webcams for attendees, enable <strong>Cloud Recording</strong>, and activate features like <strong>Polling</strong> and <strong>Raise Hand</strong>. Set the menu item's access to \"Public\" or \"Registered\" and you have an instant webinar platform.</li>\n</ul>\n<h4 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"use-case-3-the-monetized-membership-service\">Use Case 3: The Monetized Membership Service<a href=\"https://www.plugnmeet.org/blog/joomla-video-conferencing-guide#use-case-3-the-monetized-membership-service\" class=\"hash-link\" aria-label=\"Direct link to Use Case 3: The Monetized Membership Service\" title=\"Direct link to Use Case 3: The Monetized Membership Service\" translate=\"no\">​</a></h4>\n<p>This is where you can turn your Joomla site into a revenue-generating business. By combining Plug-N-Meet with a Joomla membership or e-commerce extension, you can sell access to premium video content. For a detailed walkthrough, see our guide on <strong><a class=\"\" href=\"https://www.plugnmeet.org/blog/no-code-video-conferencing-service-with-joomla\">How to Launch Your Own No-Code Video Conferencing Service with Joomla</a></strong>.</p>\n<ul>\n<li class=\"\"><strong>How to do it:</strong>\n<ol>\n<li class=\"\">Use your membership extension to create a \"Premium Members\" user group that paying subscribers are automatically added to.</li>\n<li class=\"\">Create a new menu item that links to your premium Plug-N-Meet room.</li>\n<li class=\"\">Set the <strong>Access</strong> level for this menu item to \"Premium Members.\"</li>\n</ol>\n</li>\n</ul>\n<p>Now, only paying subscribers will be able to see the link and join your exclusive video sessions, creating an automated paywall.</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"conclusion-the-professional-video-solution-for-joomla\">Conclusion: The Professional Video Solution for Joomla<a href=\"https://www.plugnmeet.org/blog/joomla-video-conferencing-guide#conclusion-the-professional-video-solution-for-joomla\" class=\"hash-link\" aria-label=\"Direct link to Conclusion: The Professional Video Solution for Joomla\" title=\"Direct link to Conclusion: The Professional Video Solution for Joomla\" translate=\"no\">​</a></h3>\n<p>Don't let external tools dictate your user experience. With the Plug-N-Meet component, you can seamlessly integrate a secure, scalable, and fully-branded video conferencing platform into the Joomla ecosystem.</p>\n<p>It's a solution that's simple enough to get started in minutes but powerful enough to build a business on.</p>\n<hr>\n<p><strong>Ready to power up your Joomla site?</strong></p>\n<ul>\n<li class=\"\"><strong><a href=\"https://extensions.joomla.org/extension/plugnmeet/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Download the official Joomla Component</a></strong></li>\n<li class=\"\"><strong><a href=\"https://demo.plugnmeet.com/landing.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Try the Live Demo</a> to see all the features in action.</strong></li>\n<li class=\"\"><strong>Follow our <a class=\"\" href=\"https://www.plugnmeet.org/docs/installation\">Installation Guide</a> to get your server running.</strong></li>\n</ul>",
            "url": "https://www.plugnmeet.org/blog/joomla-video-conferencing-guide",
            "title": "The Ultimate Guide to Joomla Video Conferencing: Integrate Live Meetings & Webinars",
            "summary": "Joomla is a powerhouse for building robust, flexible websites, from community portals to sophisticated business sites. But when it's time to engage your audience with live video, you might find yourself sending them away from the beautiful site you've built to a generic, third-party conferencing tool. This breaks the user experience and undermines your brand.",
            "date_modified": "2026-02-18T00:00:00.000Z",
            "author": {
                "name": "Chaboud Simon",
                "url": "https://github.com/saimonzone"
            },
            "tags": [
                "joomla",
                "video-conferencing",
                "component",
                "no-code",
                "webinar",
                "membership",
                "joomla-video-conferencing"
            ]
        },
        {
            "id": "https://www.plugnmeet.org/blog/moodle-video-conferencing-guide",
            "content_html": "<p>As an educator or administrator on Moodle, your goal is to create a seamless and engaging learning environment. But when it's time for a live virtual class, many are forced to compromise, sending students to an external Zoom or Teams link. This breaks the learning flow, creates a disjointed user experience, and raises valid concerns about student data privacy.</p>\n<p>What if your virtual classroom was just another native Moodle activity, as easy to add as a quiz or a forum? What if it was a powerful, interactive space designed specifically for teaching, not just a generic meeting room?</p>\n<p>With the official <strong>Plug-N-Meet plugin for Moodle</strong>, this is not just possible; it's simple. This guide will show you how to transform your Moodle site into a world-class, interactive e-learning platform.</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"why-a-native-moodle-integration-is-a-game-changer\">Why a Native Moodle Integration is a Game-Changer<a href=\"https://www.plugnmeet.org/blog/moodle-video-conferencing-guide#why-a-native-moodle-integration-is-a-game-changer\" class=\"hash-link\" aria-label=\"Direct link to Why a Native Moodle Integration is a Game-Changer\" title=\"Direct link to Why a Native Moodle Integration is a Game-Changer\" translate=\"no\">​</a></h3>\n<p>Before we get to the \"how,\" let's focus on the \"why.\" Choosing a deeply integrated, self-hosted solution like Plug-N-Meet over a generic SaaS tool provides three critical advantages for any educational institution.</p>\n<ol>\n<li class=\"\"><strong>A Seamless Student Experience:</strong> Students never leave your Moodle site. They join the live class directly from their course page, with the same branding and interface they already know. This reduces confusion and keeps them focused on the learning, not the technology.</li>\n<li class=\"\"><strong>Absolute Control Over Student Data:</strong> In education, privacy is paramount. With a self-hosted platform, all student data and class recordings are stored on your own servers, not on a third-party cloud. This gives you full control to ensure compliance with privacy regulations like GDPR and FERPA.</li>\n<li class=\"\"><strong>Predictable, Scalable Costs:</strong> Instead of paying a costly per-host, per-month subscription that penalizes you for adding more teachers, you have a fixed, predictable cost for your server infrastructure. This is a far more sustainable and cost-effective model for schools and universities.</li>\n</ol>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"the-3-step-guide-to-your-first-moodle-virtual-classroom\">The 3-Step Guide to Your First Moodle Virtual Classroom<a href=\"https://www.plugnmeet.org/blog/moodle-video-conferencing-guide#the-3-step-guide-to-your-first-moodle-virtual-classroom\" class=\"hash-link\" aria-label=\"Direct link to The 3-Step Guide to Your First Moodle Virtual Classroom\" title=\"Direct link to The 3-Step Guide to Your First Moodle Virtual Classroom\" translate=\"no\">​</a></h3>\n<p>Getting started is incredibly fast. You can have your first meeting room live in under 15 minutes. For a more detailed walkthrough, you can also read our popular guide on <strong><a class=\"\" href=\"https://www.plugnmeet.org/blog/no-code-video-conferencing-moodle\">How to Add No-Code Video Conferencing to Your Moodle Courses</a></strong>.</p>\n<h4 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"step-1-get-your-video-conferencing-engine\">Step 1: Get Your Video Conferencing Engine<a href=\"https://www.plugnmeet.org/blog/moodle-video-conferencing-guide#step-1-get-your-video-conferencing-engine\" class=\"hash-link\" aria-label=\"Direct link to Step 1: Get Your Video Conferencing Engine\" title=\"Direct link to Step 1: Get Your Video Conferencing Engine\" translate=\"no\">​</a></h4>\n<p>First, you need the server that will power your video calls. You can either self-host the open-source version with our <strong><a class=\"\" href=\"https://www.plugnmeet.org/docs/installation\">simple installation script</a></strong> or get an instant, managed server from <strong><a href=\"https://www.plugnmeet.cloud/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">plugnmeet Cloud</a></strong>. Both will provide you with an <strong>API Key</strong> and <strong>API Secret</strong>.</p>\n<h4 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"step-2-install-the-free-plug-n-meet-plugin\">Step 2: Install the Free Plug-N-Meet Plugin<a href=\"https://www.plugnmeet.org/blog/moodle-video-conferencing-guide#step-2-install-the-free-plug-n-meet-plugin\" class=\"hash-link\" aria-label=\"Direct link to Step 2: Install the Free Plug-N-Meet Plugin\" title=\"Direct link to Step 2: Install the Free Plug-N-Meet Plugin\" translate=\"no\">​</a></h4>\n<p>From your Moodle Site Administration panel:</p>\n<ol>\n<li class=\"\">Go to <strong>Site administration &gt; Plugins &gt; Install plugins</strong>.</li>\n<li class=\"\">Install the Plug-N-Meet plugin, which you can download from the official <a href=\"https://moodle.org/plugins/mod_plugnmeet\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Moodle plugins directory</a>.</li>\n<li class=\"\">After installation, navigate to the plugin settings page (<strong>Site administration &gt; Plugins &gt; Activity modules &gt; PlugNmeet</strong>).</li>\n<li class=\"\">Enter your <strong>API Key</strong> and <strong>API Secret</strong> and save the changes.</li>\n</ol>\n<p>Your Moodle site is now fully integrated with your video conferencing engine.</p>\n<h4 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"step-3-add-the-plugnmeet-activity-to-your-course\">Step 3: Add the \"PlugNmeet\" Activity to Your Course<a href=\"https://www.plugnmeet.org/blog/moodle-video-conferencing-guide#step-3-add-the-plugnmeet-activity-to-your-course\" class=\"hash-link\" aria-label=\"Direct link to Step 3: Add the &quot;PlugNmeet&quot; Activity to Your Course\" title=\"Direct link to Step 3: Add the &quot;PlugNmeet&quot; Activity to Your Course\" translate=\"no\">​</a></h4>\n<ol>\n<li class=\"\">Navigate to the Moodle course where you want to add the live class.</li>\n<li class=\"\">Turn <strong>Edit mode</strong> on.</li>\n<li class=\"\">Click <strong>Add an activity or resource</strong> and select <strong>PlugNmeet</strong> from the list.</li>\n<li class=\"\">Configure your virtual classroom, giving it a name, a welcome message, and setting the schedule.</li>\n</ol>\n<p>That's it. Your students can now join a powerful, interactive virtual classroom directly from their Moodle course page.</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"level-up-3-powerful-ways-to-use-video-in-moodle\">Level Up: 3 Powerful Ways to Use Video in Moodle<a href=\"https://www.plugnmeet.org/blog/moodle-video-conferencing-guide#level-up-3-powerful-ways-to-use-video-in-moodle\" class=\"hash-link\" aria-label=\"Direct link to Level Up: 3 Powerful Ways to Use Video in Moodle\" title=\"Direct link to Level Up: 3 Powerful Ways to Use Video in Moodle\" translate=\"no\">​</a></h3>\n<p>Plug-N-Meet is more than just a lecture tool. It's a suite of features designed for modern pedagogy.</p>\n<h4 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"use-case-1-the-interactive-virtual-classroom\">Use Case 1: The Interactive Virtual Classroom<a href=\"https://www.plugnmeet.org/blog/moodle-video-conferencing-guide#use-case-1-the-interactive-virtual-classroom\" class=\"hash-link\" aria-label=\"Direct link to Use Case 1: The Interactive Virtual Classroom\" title=\"Direct link to Use Case 1: The Interactive Virtual Classroom\" translate=\"no\">​</a></h4>\n<p>Transform a passive lecture into an engaging, two-way experience.</p>\n<ul>\n<li class=\"\"><strong>How to do it:</strong> Use the <strong><a class=\"\" href=\"https://www.plugnmeet.org/blog/integrated-whiteboard-experience\">Interactive Whiteboard</a></strong> to upload your presentation slides and annotate them in real-time. Use the <strong>Live Polling</strong> feature to quickly check for student understanding or run a quick quiz. All class sessions can be recorded to the cloud and automatically made available back in the Moodle course for students who missed the live session.</li>\n</ul>\n<h4 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"use-case-2-the-collaborative-workshop-with-breakout-rooms\">Use Case 2: The Collaborative Workshop with Breakout Rooms<a href=\"https://www.plugnmeet.org/blog/moodle-video-conferencing-guide#use-case-2-the-collaborative-workshop-with-breakout-rooms\" class=\"hash-link\" aria-label=\"Direct link to Use Case 2: The Collaborative Workshop with Breakout Rooms\" title=\"Direct link to Use Case 2: The Collaborative Workshop with Breakout Rooms\" translate=\"no\">​</a></h4>\n<p>For project-based learning and group work, the <strong><a class=\"\" href=\"https://www.plugnmeet.org/blog/mastering-breakout-rooms\">Breakout Rooms</a></strong> feature is essential.</p>\n<ul>\n<li class=\"\"><strong>How to do it:</strong> From within the live session, the teacher can split the students into smaller groups. Each breakout room gets its own private, fully-functional whiteboard and shared notepad, allowing for true small-group collaboration. The teacher can broadcast messages to all rooms or drop in to a specific group to check on their progress.</li>\n</ul>\n<h4 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"use-case-3-the-secure-one-on-one-or-parent-teacher-conference\">Use Case 3: The Secure One-on-One or Parent-Teacher Conference<a href=\"https://www.plugnmeet.org/blog/moodle-video-conferencing-guide#use-case-3-the-secure-one-on-one-or-parent-teacher-conference\" class=\"hash-link\" aria-label=\"Direct link to Use Case 3: The Secure One-on-One or Parent-Teacher Conference\" title=\"Direct link to Use Case 3: The Secure One-on-One or Parent-Teacher Conference\" translate=\"no\">​</a></h4>\n<p>For private conversations, you need a higher level of security.</p>\n<ul>\n<li class=\"\"><strong>How to do it:</strong> Create a PlugNmeet activity and, in the room settings, enable <strong><a class=\"\" href=\"https://www.plugnmeet.org/blog/privacy-by-design-zero-trust\">End-to-End Encryption (E2EE)</a></strong>. This provides a mathematical guarantee that the conversation is completely private and that not even the server administrator can access the audio or video. This is ideal for one-on-one student support, counseling sessions, or parent-teacher conferences where confidentiality is critical.</li>\n</ul>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"conclusion-the-virtual-classroom-moodle-deserves\">Conclusion: The Virtual Classroom Moodle Deserves<a href=\"https://www.plugnmeet.org/blog/moodle-video-conferencing-guide#conclusion-the-virtual-classroom-moodle-deserves\" class=\"hash-link\" aria-label=\"Direct link to Conclusion: The Virtual Classroom Moodle Deserves\" title=\"Direct link to Conclusion: The Virtual Classroom Moodle Deserves\" translate=\"no\">​</a></h3>\n<p>Stop compromising with external tools that break the learning experience. By integrating Plug-N-Meet, you can offer your teachers and students a powerful, secure, and seamlessly integrated virtual classroom that lives right where it belongs: inside Moodle.</p>\n<p>It's a solution that's simple enough for any teacher to use but powerful enough to support the most innovative online teaching methods.</p>\n<hr>\n<p><strong>Ready to enhance your Moodle courses?</strong></p>\n<ul>\n<li class=\"\"><strong><a href=\"https://moodle.org/plugins/mod_plugnmeet\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Download the official Moodle Plugin</a></strong></li>\n<li class=\"\"><strong><a href=\"https://demo.plugnmeet.com/landing.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Try the Live Demo</a> to explore the teaching tools.</strong></li>\n<li class=\"\"><strong>Follow our <a class=\"\" href=\"https://www.plugnmeet.org/docs/installation\">Installation Guide</a> to get your server running.</strong></li>\n</ul>",
            "url": "https://www.plugnmeet.org/blog/moodle-video-conferencing-guide",
            "title": "The Ultimate Guide to Moodle Video Conferencing: From Virtual Classrooms to Collaborative Workshops",
            "summary": "As an educator or administrator on Moodle, your goal is to create a seamless and engaging learning environment. But when it's time for a live virtual class, many are forced to compromise, sending students to an external Zoom or Teams link. This breaks the learning flow, creates a disjointed user experience, and raises valid concerns about student data privacy.",
            "date_modified": "2026-02-14T00:00:00.000Z",
            "author": {
                "name": "Chaboud Simon",
                "url": "https://github.com/saimonzone"
            },
            "tags": [
                "moodle",
                "video-conferencing",
                "plugin",
                "no-code",
                "virtual-classroom",
                "e-learning",
                "moodle-plugin",
                "moodle-video-conferencing"
            ]
        },
        {
            "id": "https://www.plugnmeet.org/blog/wordpress-video-conferencing-guide",
            "content_html": "<p>WordPress has empowered millions to build anything from a simple blog to a complex e-commerce empire. But when it comes to one of the most critical forms of modern communication—live video—many site owners hit a wall. You're often left juggling clunky Zoom links, sending your users to third-party platforms, and sacrificing the professional, branded experience your website was designed to provide.</p>\n<p>What if you could integrate a powerful, secure, and fully-branded video conferencing solution directly into your WordPress site as easily as you add a new page?</p>\n<p>This is not just possible; it's simple. This guide will show you how to use the <strong>Plug-N-Meet plugin</strong> to transform your WordPress site into a true communication hub, whether you need a simple meeting room, a platform for webinars, or a full-fledged, monetized video service.</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"why-self-hosted-is-the-smart-choice-for-wordpress\">Why Self-Hosted is the Smart Choice for WordPress<a href=\"https://www.plugnmeet.org/blog/wordpress-video-conferencing-guide#why-self-hosted-is-the-smart-choice-for-wordpress\" class=\"hash-link\" aria-label=\"Direct link to Why Self-Hosted is the Smart Choice for WordPress\" title=\"Direct link to Why Self-Hosted is the Smart Choice for WordPress\" translate=\"no\">​</a></h3>\n<p>Before we dive into the \"how,\" let's cover the \"why.\" While embedding a Zoom meeting is an option, a self-hosted solution like Plug-N-Meet offers three transformative advantages for any serious WordPress site owner:</p>\n<ol>\n<li class=\"\"><strong>You Own Your Brand:</strong> Every meeting happens on your own domain, with your logo and your colors. You're not providing free advertising for a SaaS giant; you're reinforcing your own brand's professionalism and trust.</li>\n<li class=\"\"><strong>You Control Your Data:</strong> All data—from user information to recordings—stays on your own server. This gives you absolute control over privacy and is critical for compliance in sensitive industries.</li>\n<li class=\"\"><strong>You Escape Per-User Pricing:</strong> Instead of paying a fee for every host on your team, you have a predictable, fixed cost for your server. This model rewards your growth instead of penalizing it.</li>\n</ol>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"the-3-step-guide-to-your-first-wordpress-meeting\">The 3-Step Guide to Your First WordPress Meeting<a href=\"https://www.plugnmeet.org/blog/wordpress-video-conferencing-guide#the-3-step-guide-to-your-first-wordpress-meeting\" class=\"hash-link\" aria-label=\"Direct link to The 3-Step Guide to Your First WordPress Meeting\" title=\"Direct link to The 3-Step Guide to Your First WordPress Meeting\" translate=\"no\">​</a></h3>\n<p>Getting started is incredibly fast. You can have your first meeting room live in under 15 minutes.</p>\n<h4 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"step-1-install-the-engine-your-plug-n-meet-server\">Step 1: Install the Engine (Your Plug-N-Meet Server)<a href=\"https://www.plugnmeet.org/blog/wordpress-video-conferencing-guide#step-1-install-the-engine-your-plug-n-meet-server\" class=\"hash-link\" aria-label=\"Direct link to Step 1: Install the Engine (Your Plug-N-Meet Server)\" title=\"Direct link to Step 1: Install the Engine (Your Plug-N-Meet Server)\" translate=\"no\">​</a></h4>\n<p>First, you need the server that will power your video calls. You can either self-host the open-source version with our <strong><a class=\"\" href=\"https://www.plugnmeet.org/docs/installation\">simple installation script</a></strong> or get an instant, managed server from <strong><a href=\"https://www.plugnmeet.cloud/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">plugnmeet Cloud</a></strong>. Both will provide you with an <strong>API Key</strong> and <strong>API Secret</strong>.</p>\n<h4 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"step-2-install-the-free-plug-n-meet-plugin\">Step 2: Install the Free Plug-N-Meet Plugin<a href=\"https://www.plugnmeet.org/blog/wordpress-video-conferencing-guide#step-2-install-the-free-plug-n-meet-plugin\" class=\"hash-link\" aria-label=\"Direct link to Step 2: Install the Free Plug-N-Meet Plugin\" title=\"Direct link to Step 2: Install the Free Plug-N-Meet Plugin\" translate=\"no\">​</a></h4>\n<p>From your WordPress dashboard:</p>\n<ol>\n<li class=\"\">Go to <strong>Plugins &gt; Add New</strong>.</li>\n<li class=\"\">Search for <strong>\"plugnmeet\"</strong>.</li>\n<li class=\"\">Click <strong>Install Now</strong>, then <strong>Activate</strong>.</li>\n<li class=\"\">Navigate to the new <strong>Plug-N-Meet &gt; Settings</strong> menu.</li>\n<li class=\"\">Enter your <strong>API Key</strong> and <strong>API Secret</strong> and save.</li>\n</ol>\n<p>Your WordPress site is now fully integrated with your video conferencing engine.</p>\n<h4 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"step-3-create-and-embed-your-room\">Step 3: Create and Embed Your Room<a href=\"https://www.plugnmeet.org/blog/wordpress-video-conferencing-guide#step-3-create-and-embed-your-room\" class=\"hash-link\" aria-label=\"Direct link to Step 3: Create and Embed Your Room\" title=\"Direct link to Step 3: Create and Embed Your Room\" translate=\"no\">​</a></h4>\n<ol>\n<li class=\"\">Go to <strong>Plug-N-Meet &gt; Rooms</strong> and click <strong>Add New</strong>.</li>\n<li class=\"\">Configure your room's features (e.g., name, max participants, welcome message) and get the <strong>Room ID</strong>.</li>\n<li class=\"\">After saving, the plugin will generate a unique <strong>Shortcode</strong> for that room (e.g., <code>[plugnmeet_room_view id=\"your_room_id\"]</code>).</li>\n<li class=\"\">Create a new WordPress Page, paste the shortcode into the content, and click <strong>Publish</strong>.</li>\n</ol>\n<p>That's it. You now have a fully functional, branded video conferencing room running directly on your own website.</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"level-up-3-powerful-ways-to-use-video-on-your-wordpress-site\">Level Up: 3 Powerful Ways to Use Video on Your WordPress Site<a href=\"https://www.plugnmeet.org/blog/wordpress-video-conferencing-guide#level-up-3-powerful-ways-to-use-video-on-your-wordpress-site\" class=\"hash-link\" aria-label=\"Direct link to Level Up: 3 Powerful Ways to Use Video on Your WordPress Site\" title=\"Direct link to Level Up: 3 Powerful Ways to Use Video on Your WordPress Site\" translate=\"no\">​</a></h3>\n<p>Once you've mastered the basics, you can leverage Plug-N-Meet to power a wide range of professional services.</p>\n<h4 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"use-case-1-the-private-coaching-or-consultation-room\">Use Case 1: The Private Coaching or Consultation Room<a href=\"https://www.plugnmeet.org/blog/wordpress-video-conferencing-guide#use-case-1-the-private-coaching-or-consultation-room\" class=\"hash-link\" aria-label=\"Direct link to Use Case 1: The Private Coaching or Consultation Room\" title=\"Direct link to Use Case 1: The Private Coaching or Consultation Room\" translate=\"no\">​</a></h4>\n<p>Are you a coach, consultant, or therapist? You can easily create secure, private rooms for your client sessions.</p>\n<ul>\n<li class=\"\"><strong>How to do it:</strong> Create a separate, password-protected page for each client. Embed the shortcode for a unique meeting room on that page. Only the person with the password can access the session, ensuring complete privacy.</li>\n</ul>\n<h4 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"use-case-2-the-community-webinar-or-live-event\">Use Case 2: The Community Webinar or Live Event<a href=\"https://www.plugnmeet.org/blog/wordpress-video-conferencing-guide#use-case-2-the-community-webinar-or-live-event\" class=\"hash-link\" aria-label=\"Direct link to Use Case 2: The Community Webinar or Live Event\" title=\"Direct link to Use Case 2: The Community Webinar or Live Event\" translate=\"no\">​</a></h4>\n<p>Want to host a webinar for your audience? Plug-N-Meet is a powerful open-source webinar platform.</p>\n<ul>\n<li class=\"\"><strong>How to do it:</strong> Create a room and configure its features specifically for a webinar. In the room settings, you can disable webcams and microphones for attendees by default, enable <strong>Cloud Recording</strong>, and use features like <strong>Polling</strong> and <strong>Raise Hand</strong> to manage audience interaction. Embed the room's shortcode on your \"Events\" page and you're ready to go live.</li>\n</ul>\n<h4 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"use-case-3-the-monetized-membership-site\">Use Case 3: The Monetized Membership Site<a href=\"https://www.plugnmeet.org/blog/wordpress-video-conferencing-guide#use-case-3-the-monetized-membership-site\" class=\"hash-link\" aria-label=\"Direct link to Use Case 3: The Monetized Membership Site\" title=\"Direct link to Use Case 3: The Monetized Membership Site\" translate=\"no\">​</a></h4>\n<p>This is where you can turn your video feature into a revenue stream. By combining Plug-N-Meet with a popular membership plugin (like Paid Memberships Pro, MemberPress, or Restrict Content Pro), you can create premium video content exclusively for paying subscribers. For a detailed walkthrough of this business model, see our guide on <strong><a class=\"\" href=\"https://www.plugnmeet.org/blog/no-code-video-conferencing-service-with-wordpress\">How to Launch Your Own No-Code Video Conferencing Service with WordPress</a></strong>.</p>\n<ul>\n<li class=\"\"><strong>How to do it:</strong>\n<ol>\n<li class=\"\">Use your membership plugin to create a \"Premium\" subscription tier.</li>\n<li class=\"\">Create a new WordPress page for your premium content (e.g., \"Weekly Mastermind Call\").</li>\n<li class=\"\">Paste the Plug-N-Meet room shortcode onto this page.</li>\n<li class=\"\">Use your membership plugin's settings to <strong>restrict access</strong> to this page so that only users with an active \"Premium\" subscription can view it.</li>\n</ol>\n</li>\n</ul>\n<p>Now, you have a secure, automated paywall for your exclusive video content.</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"unlocking-advanced-features-ai-and-zero-trust-security\">Unlocking Advanced Features: AI and Zero-Trust Security<a href=\"https://www.plugnmeet.org/blog/wordpress-video-conferencing-guide#unlocking-advanced-features-ai-and-zero-trust-security\" class=\"hash-link\" aria-label=\"Direct link to Unlocking Advanced Features: AI and Zero-Trust Security\" title=\"Direct link to Unlocking Advanced Features: AI and Zero-Trust Security\" translate=\"no\">​</a></h3>\n<p>The Plug-N-Meet WordPress plugin isn't just for basic meetings. It gives you simple, one-click access to enterprise-grade features directly from your room settings.</p>\n<ul>\n<li class=\"\"><strong>AI-Powered Meeting Assistants:</strong> Imagine having every meeting automatically transcribed and summarized. With the AI features enabled, you can turn your conversations into actionable intelligence. Learn <strong><a class=\"\" href=\"https://www.plugnmeet.org/blog/how-to-add-ai-meeting-assistant-features\">how to add AI features</a></strong> with a simple toggle.</li>\n<li class=\"\"><strong>Zero-Trust Security:</strong> For telehealth, legal consultations, or any conversation requiring absolute confidentiality, you can enable End-to-End Encryption (E2EE). This ensures that not even your server can access the meeting's content. Discover more about our <strong><a class=\"\" href=\"https://www.plugnmeet.org/blog/privacy-by-design-zero-trust\">privacy-by-design philosophy</a></strong>.</li>\n</ul>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"conclusion-the-professional-video-solution-wordpress-deserves\">Conclusion: The Professional Video Solution WordPress Deserves<a href=\"https://www.plugnmeet.org/blog/wordpress-video-conferencing-guide#conclusion-the-professional-video-solution-wordpress-deserves\" class=\"hash-link\" aria-label=\"Direct link to Conclusion: The Professional Video Solution WordPress Deserves\" title=\"Direct link to Conclusion: The Professional Video Solution WordPress Deserves\" translate=\"no\">​</a></h3>\n<p>Stop treating live video as an afterthought. With Plug-N-Meet, you can seamlessly integrate a secure, scalable, and fully-branded video conferencing platform into the WordPress ecosystem you already know and love.</p>\n<p>It's a solution that's simple enough to start in minutes but powerful enough to grow with your biggest ambitions.</p>\n<hr>\n<p><strong>Ready to power up your WordPress site?</strong></p>\n<ul>\n<li class=\"\"><strong><a href=\"https://wordpress.org/plugins/plugnmeet/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Download the official WordPress Plugin</a></strong></li>\n<li class=\"\"><strong><a href=\"https://demo.plugnmeet.com/landing.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Try the Live Demo</a> to see all the features in action.</strong></li>\n<li class=\"\"><strong>Follow our <a class=\"\" href=\"https://www.plugnmeet.org/docs/installation\">Installation Guide</a> to get your server running.</strong></li>\n</ul>",
            "url": "https://www.plugnmeet.org/blog/wordpress-video-conferencing-guide",
            "title": "The Ultimate Guide to WordPress Video Conferencing",
            "summary": "WordPress has empowered millions to build anything from a simple blog to a complex e-commerce empire. But when it comes to one of the most critical forms of modern communication—live video—many site owners hit a wall. You're often left juggling clunky Zoom links, sending your users to third-party platforms, and sacrificing the professional, branded experience your website was designed to provide.",
            "date_modified": "2026-02-10T00:00:00.000Z",
            "author": {
                "name": "Chaboud Simon",
                "url": "https://github.com/saimonzone"
            },
            "tags": [
                "wordpress",
                "video-conferencing",
                "plugin",
                "no-code",
                "webinar",
                "coaching",
                "membership",
                "wordpress-video-conferencing"
            ]
        },
        {
            "id": "https://www.plugnmeet.org/blog/developer-guide-custom-video-chat-ui-headless-api",
            "content_html": "<p>You've successfully integrated Plug-N-Meet into your application. You can create rooms, generate join tokens, and embed the client in an <code>&lt;iframe&gt;</code>. It's fast and it works. But now you want to go deeper. You want to break out of the <code>&lt;iframe&gt;</code> box and build a truly seamless user experience where the video client feels like a native part of your application's UI.</p>\n<p>This is where Plug-N-Meet's \"headless\" integration mode comes in.</p>\n<p>This guide is for developers who want to move beyond basic embedding. We'll show you how to use the powerful <code>getClientFiles</code> API to render the Plug-N-Meet client directly into your own page's DOM, giving you ultimate control over layout, branding, and the user experience.</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"the-problem-with-iframes\">The Problem with Iframes<a href=\"https://www.plugnmeet.org/blog/developer-guide-custom-video-chat-ui-headless-api#the-problem-with-iframes\" class=\"hash-link\" aria-label=\"Direct link to The Problem with Iframes\" title=\"Direct link to The Problem with Iframes\" translate=\"no\">​</a></h3>\n<p>An <code>&lt;iframe&gt;</code> is a simple way to embed content, but it's essentially a \"window\" into another website. This creates several limitations for developers aiming for a professional, deeply integrated product:</p>\n<ul>\n<li class=\"\"><strong>The Black Box:</strong> The <code>&lt;iframe&gt;</code> creates a hard boundary between your parent application and the video client, making communication between them complex and limited.</li>\n<li class=\"\"><strong>Layout Restrictions:</strong> You are stuck with the layout provided by the <code>src</code> URL. You can't easily place your own application's components (like a custom header or a sidebar with user data) \"inside\" the video experience.</li>\n<li class=\"\"><strong>Styling and Branding:</strong> While you can pass some branding parameters, your ability to deeply customize the look and feel is limited.</li>\n</ul>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"the-solution-headless-integration-with-getclientfiles\">The Solution: Headless Integration with <code>getClientFiles</code><a href=\"https://www.plugnmeet.org/blog/developer-guide-custom-video-chat-ui-headless-api#the-solution-headless-integration-with-getclientfiles\" class=\"hash-link\" aria-label=\"Direct link to the-solution-headless-integration-with-getclientfiles\" title=\"Direct link to the-solution-headless-integration-with-getclientfiles\" translate=\"no\">​</a></h3>\n<p>The <code>getClientFiles</code> API is the key to unlocking a truly \"headless\" video integration. Instead of providing you with a URL to embed, it gives you a list of the exact CSS and JavaScript assets you need to render the client yourself.</p>\n<p>This approach transforms Plug-N-Meet from a separate application into a UI component library that you control.</p>\n<h4 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"the-three-step-workflow\">The Three-Step Workflow<a href=\"https://www.plugnmeet.org/blog/developer-guide-custom-video-chat-ui-headless-api#the-three-step-workflow\" class=\"hash-link\" aria-label=\"Direct link to The Three-Step Workflow\" title=\"Direct link to The Three-Step Workflow\" translate=\"no\">​</a></h4>\n<p>The logic is simple and can be implemented in any backend language (PHP, Node.js, Python, etc.).</p>\n<ol>\n<li class=\"\"><strong>Your Backend Fetches the Asset List:</strong> Your server makes a secure, server-to-server API call to the <code>/auth/getClientFiles</code> endpoint.</li>\n<li class=\"\"><strong>Plug-N-Meet Responds with a JSON Object:</strong> This object contains two arrays: <code>css</code> and <code>js</code>, listing the filenames of all the necessary assets.</li>\n<li class=\"\"><strong>Your Template Renders the <code>&lt;link&gt;</code> and <code>&lt;script&gt;</code> Tags:</strong> Your server-side templating engine loops through these arrays and dynamically injects the required tags into the HTML page that will be sent to the user's browser.</li>\n</ol>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"a-practical-example-nodejs-with-ejs\">A Practical Example (Node.js with EJS)<a href=\"https://www.plugnmeet.org/blog/developer-guide-custom-video-chat-ui-headless-api#a-practical-example-nodejs-with-ejs\" class=\"hash-link\" aria-label=\"Direct link to A Practical Example (Node.js with EJS)\" title=\"Direct link to A Practical Example (Node.js with EJS)\" translate=\"no\">​</a></h3>\n<p>Let's walk through a simplified example using Node.js and the EJS templating engine.</p>\n<h4 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"step-1-the-backend-route-eg-in-serverjs\">Step 1: The Backend Route (e.g., in <code>server.js</code>)<a href=\"https://www.plugnmeet.org/blog/developer-guide-custom-video-chat-ui-headless-api#step-1-the-backend-route-eg-in-serverjs\" class=\"hash-link\" aria-label=\"Direct link to step-1-the-backend-route-eg-in-serverjs\" title=\"Direct link to step-1-the-backend-route-eg-in-serverjs\" translate=\"no\">​</a></h4>\n<p>This route will handle the API calls and render the final page.</p>\n<div class=\"language-javascript codeBlockContainer_ibEU theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_osW6\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_lRqf thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_jDlT\"><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token keyword module\" style=\"color:#00009f\">import</span><span class=\"token plain\"> </span><span class=\"token imports\">express</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:#00009f\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'express'</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:#00009f\">import</span><span class=\"token plain\"> </span><span class=\"token imports punctuation\" style=\"color:#393A34\">{</span><span class=\"token imports\"> </span><span class=\"token imports maybe-class-name\">PlugNmeet</span><span class=\"token imports\"> </span><span class=\"token imports punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:#00009f\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'plugnmeet-sdk-js'</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> app </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">express</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">set</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token string\" style=\"color:#e3116c\">'view engine'</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'ejs'</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// Your Plug-N-Meet credentials (use environment variables in production)</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> </span><span class=\"token constant\" style=\"color:#36acaa\">API_KEY</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'plugnmeet'</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> </span><span class=\"token constant\" style=\"color:#36acaa\">API_SECRET</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'zumyyYWqv7KR2kUqvYdq4z4sXg7XTBD2ljT6'</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> </span><span class=\"token constant\" style=\"color:#36acaa\">PLUGNMEET_URL</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'https://demo.plugnmeet.com'</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> pnm </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">new</span><span class=\"token plain\"> </span><span class=\"token class-name\">PlugNmeet</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token constant\" style=\"color:#36acaa\">PLUGNMEET_URL</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> </span><span class=\"token constant\" style=\"color:#36acaa\">API_KEY</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> </span><span class=\"token constant\" style=\"color:#36acaa\">API_SECRET</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">get</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token string\" style=\"color:#e3116c\">'/meeting'</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:#393A34\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:#393A34\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:#00009f\">try</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// 1. Get the asset files</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> clientFiles </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:#00009f\">await</span><span class=\"token plain\"> pnm</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">getClientFiles</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// 2. Create a room and generate a join token for the user</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> room </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token literal-property property\" style=\"color:#36acaa\">room_id</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'my-custom-room'</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token literal-property property\" style=\"color:#36acaa\">metadata</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">        </span><span class=\"token literal-property property\" style=\"color:#36acaa\">room_title</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'My Custom UI Room'</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">        </span><span class=\"token literal-property property\" style=\"color:#36acaa\">room_features</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">          </span><span class=\"token literal-property property\" style=\"color:#36acaa\">allow_webcams</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:#36acaa\">true</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">          </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// ... other features</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token keyword control-flow\" style=\"color:#00009f\">await</span><span class=\"token plain\"> pnm</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">createRoom</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">room</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> joinToken </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:#00009f\">await</span><span class=\"token plain\"> pnm</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">getJoinToken</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token literal-property property\" style=\"color:#36acaa\">room_id</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'my-custom-room'</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token literal-property property\" style=\"color:#36acaa\">user_info</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">        </span><span class=\"token literal-property property\" style=\"color:#36acaa\">name</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'John Doe'</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">        </span><span class=\"token literal-property property\" style=\"color:#36acaa\">user_id</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'user-123'</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// 3. Define dynamic branding options</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> designOptions </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token literal-property property\" style=\"color:#36acaa\">primary_color</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'#004D90'</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token literal-property property\" style=\"color:#36acaa\">secondary_color</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'#24AEF7'</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token literal-property property\" style=\"color:#36acaa\">custom_logo</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'https://my-app.com/logo.png'</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// 4. Render the view, passing all the necessary data</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    res</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">render</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token string\" style=\"color:#e3116c\">'meeting'</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token literal-property property\" style=\"color:#36acaa\">plugnmeetUrl</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token constant\" style=\"color:#36acaa\">PLUGNMEET_URL</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token literal-property property\" style=\"color:#36acaa\">clientFiles</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> clientFiles</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">res</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token literal-property property\" style=\"color:#36acaa\">accessToken</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> joinToken</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">res</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">token</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token literal-property property\" style=\"color:#36acaa\">designOptions</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token known-class-name class-name\">JSON</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">stringify</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">designOptions</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:#00009f\">catch</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">error</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token console class-name\">console</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">error</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">error</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    res</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">status</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token number\" style=\"color:#36acaa\">500</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">send</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token string\" style=\"color:#e3116c\">'Error setting up meeting'</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">listen</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token number\" style=\"color:#36acaa\">3000</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:#393A34\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token console class-name\">console</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">log</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token string\" style=\"color:#e3116c\">'Server started on port 3000'</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><br></div></code></pre></div></div>\n<h4 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"step-2-the-frontend-template-eg-in-viewsmeetingejs\">Step 2: The Frontend Template (e.g., in <code>views/meeting.ejs</code>)<a href=\"https://www.plugnmeet.org/blog/developer-guide-custom-video-chat-ui-headless-api#step-2-the-frontend-template-eg-in-viewsmeetingejs\" class=\"hash-link\" aria-label=\"Direct link to step-2-the-frontend-template-eg-in-viewsmeetingejs\" title=\"Direct link to step-2-the-frontend-template-eg-in-viewsmeetingejs\" translate=\"no\">​</a></h4>\n<p>This EJS file will take the data from the backend and construct the HTML page.</p>\n<div class=\"language-html codeBlockContainer_ibEU theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_osW6\"><pre tabindex=\"0\" class=\"prism-code language-html codeBlock_lRqf thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_jDlT\"><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token doctype punctuation\" style=\"color:#393A34;font-style:italic\">&lt;!</span><span class=\"token doctype doctype-tag\" style=\"color:#999988;font-style:italic\">DOCTYPE</span><span class=\"token doctype\" style=\"color:#999988;font-style:italic\"> </span><span class=\"token doctype name\" style=\"color:#999988;font-style:italic\">html</span><span class=\"token doctype punctuation\" style=\"color:#393A34;font-style:italic\">&gt;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">html</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag attr-name\" style=\"color:#00a4db\">lang</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:#393A34\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag attr-value\" style=\"color:#e3116c\">en</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">head</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">title</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\">My Custom Video App</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">title</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">&lt;!-- Dynamically inject CSS files --&gt;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  &lt;% clientFiles.css.forEach(file =&gt; { %&gt;</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">link</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag attr-name\" style=\"color:#00a4db\">href</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:#393A34\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag attr-value\" style=\"color:#e3116c\">&lt;%= plugnmeetUrl %&gt;/assets/css/&lt;%= file %&gt;</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag attr-name\" style=\"color:#00a4db\">rel</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:#393A34\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag attr-value\" style=\"color:#e3116c\">stylesheet</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag punctuation\" style=\"color:#393A34\">/&gt;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  &lt;% }); %&gt;</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">&lt;!-- Inject essential window variables BEFORE loading scripts --&gt;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">script</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag attr-name\" style=\"color:#00a4db\">type</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:#393A34\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag attr-value\" style=\"color:#e3116c\">text/javascript</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token script language-javascript\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">    </span><span class=\"token script language-javascript dom variable\" style=\"color:#36acaa\">window</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">.</span><span class=\"token script language-javascript property-access\">plugNmeetConfig</span><span class=\"token script language-javascript\"> </span><span class=\"token script language-javascript operator\" style=\"color:#393A34\">=</span><span class=\"token script language-javascript\"> </span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">{</span><span class=\"token script language-javascript\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">      </span><span class=\"token script language-javascript comment\" style=\"color:#999988;font-style:italic\">// Required: The URL of your plugNmeet server.</span><span class=\"token script language-javascript\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">      </span><span class=\"token script language-javascript literal-property property\" style=\"color:#36acaa\">serverUrl</span><span class=\"token script language-javascript operator\" style=\"color:#393A34\">:</span><span class=\"token script language-javascript\"> </span><span class=\"token script language-javascript string\" style=\"color:#e3116c\">\"&lt;%= plugnmeetUrl %&gt;\"</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">,</span><span class=\"token script language-javascript\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">      </span><span class=\"token script language-javascript comment\" style=\"color:#999988;font-style:italic\">// Required: The public path to the assets directory.</span><span class=\"token script language-javascript\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">      </span><span class=\"token script language-javascript literal-property property\" style=\"color:#36acaa\">staticAssetsPath</span><span class=\"token script language-javascript operator\" style=\"color:#393A34\">:</span><span class=\"token script language-javascript\"> </span><span class=\"token script language-javascript string\" style=\"color:#e3116c\">\"&lt;%= plugnmeetUrl %&gt;/assets\"</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">,</span><span class=\"token script language-javascript\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">      </span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">      </span><span class=\"token script language-javascript comment\" style=\"color:#999988;font-style:italic\">// Required: Pass the design customization options.</span><span class=\"token script language-javascript\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">      </span><span class=\"token script language-javascript literal-property property\" style=\"color:#36acaa\">designCustomization</span><span class=\"token script language-javascript operator\" style=\"color:#393A34\">:</span><span class=\"token script language-javascript\"> </span><span class=\"token script language-javascript operator\" style=\"color:#393A34\">&lt;</span><span class=\"token script language-javascript operator\" style=\"color:#393A34\">%</span><span class=\"token script language-javascript operator\" style=\"color:#393A34\">-</span><span class=\"token script language-javascript\"> designOptions </span><span class=\"token script language-javascript operator\" style=\"color:#393A34\">%</span><span class=\"token script language-javascript operator\" style=\"color:#393A34\">&gt;</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">,</span><span class=\"token script language-javascript\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">      </span><span class=\"token script language-javascript comment\" style=\"color:#999988;font-style:italic\">// Optional: Add any other custom configurations.</span><span class=\"token script language-javascript\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">      </span><span class=\"token script language-javascript comment\" style=\"color:#999988;font-style:italic\">// See: https://github.com/mynaparrot/plugNmeet-client/blob/main/src/assets/config_sample.js</span><span class=\"token script language-javascript\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">      </span><span class=\"token script language-javascript literal-property property\" style=\"color:#36acaa\">enableSimulcast</span><span class=\"token script language-javascript operator\" style=\"color:#393A34\">:</span><span class=\"token script language-javascript\"> </span><span class=\"token script language-javascript boolean\" style=\"color:#36acaa\">true</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">,</span><span class=\"token script language-javascript\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">      </span><span class=\"token script language-javascript literal-property property\" style=\"color:#36acaa\">enableDynacast</span><span class=\"token script language-javascript operator\" style=\"color:#393A34\">:</span><span class=\"token script language-javascript\"> </span><span class=\"token script language-javascript boolean\" style=\"color:#36acaa\">true</span><span class=\"token script language-javascript\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">    </span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">}</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">;</span><span class=\"token script language-javascript\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">  </span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">script</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">head</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">body</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">&lt;!-- Your Custom Application UI --&gt;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">header</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag special-attr attr-name\" style=\"color:#00a4db\">style</span><span class=\"token tag special-attr attr-value punctuation attr-equals\" style=\"color:#393A34\">=</span><span class=\"token tag special-attr attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag special-attr attr-value value css language-css property\" style=\"color:#36acaa\">background</span><span class=\"token tag special-attr attr-value value css language-css punctuation\" style=\"color:#393A34\">:</span><span class=\"token tag special-attr attr-value value css language-css\" style=\"color:#e3116c\"> </span><span class=\"token tag special-attr attr-value value css language-css hexcode color\" style=\"color:#e3116c\">#0A1929</span><span class=\"token tag special-attr attr-value value css language-css punctuation\" style=\"color:#393A34\">;</span><span class=\"token tag special-attr attr-value value css language-css\" style=\"color:#e3116c\"> </span><span class=\"token tag special-attr attr-value value css language-css property\" style=\"color:#36acaa\">color</span><span class=\"token tag special-attr attr-value value css language-css punctuation\" style=\"color:#393A34\">:</span><span class=\"token tag special-attr attr-value value css language-css\" style=\"color:#e3116c\"> </span><span class=\"token tag special-attr attr-value value css language-css color\" style=\"color:#e3116c\">white</span><span class=\"token tag special-attr attr-value value css language-css punctuation\" style=\"color:#393A34\">;</span><span class=\"token tag special-attr attr-value value css language-css\" style=\"color:#e3116c\"> </span><span class=\"token tag special-attr attr-value value css language-css property\" style=\"color:#36acaa\">padding</span><span class=\"token tag special-attr attr-value value css language-css punctuation\" style=\"color:#393A34\">:</span><span class=\"token tag special-attr attr-value value css language-css\" style=\"color:#e3116c\"> </span><span class=\"token tag special-attr attr-value value css language-css number\" style=\"color:#36acaa\">1</span><span class=\"token tag special-attr attr-value value css language-css unit\" style=\"color:#e3116c\">rem</span><span class=\"token tag special-attr attr-value value css language-css punctuation\" style=\"color:#393A34\">;</span><span class=\"token tag special-attr attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">h1</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\">My Application Header</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">h1</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">header</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag special-attr attr-name\" style=\"color:#00a4db\">style</span><span class=\"token tag special-attr attr-value punctuation attr-equals\" style=\"color:#393A34\">=</span><span class=\"token tag special-attr attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag special-attr attr-value value css language-css property\" style=\"color:#36acaa\">display</span><span class=\"token tag special-attr attr-value value css language-css punctuation\" style=\"color:#393A34\">:</span><span class=\"token tag special-attr attr-value value css language-css\" style=\"color:#e3116c\"> flex</span><span class=\"token tag special-attr attr-value value css language-css punctuation\" style=\"color:#393A34\">;</span><span class=\"token tag special-attr attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">&lt;!-- The Plug-N-Meet client will mount itself in this div --&gt;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag attr-name\" style=\"color:#00a4db\">id</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:#393A34\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag attr-value\" style=\"color:#e3116c\">plugNmeet-app</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag special-attr attr-name\" style=\"color:#00a4db\">style</span><span class=\"token tag special-attr attr-value punctuation attr-equals\" style=\"color:#393A34\">=</span><span class=\"token tag special-attr attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag special-attr attr-value value css language-css property\" style=\"color:#36acaa\">flex-grow</span><span class=\"token tag special-attr attr-value value css language-css punctuation\" style=\"color:#393A34\">:</span><span class=\"token tag special-attr attr-value value css language-css\" style=\"color:#e3116c\"> </span><span class=\"token tag special-attr attr-value value css language-css number\" style=\"color:#36acaa\">1</span><span class=\"token tag special-attr attr-value value css language-css punctuation\" style=\"color:#393A34\">;</span><span class=\"token tag special-attr attr-value value css language-css\" style=\"color:#e3116c\"> </span><span class=\"token tag special-attr attr-value value css language-css property\" style=\"color:#36acaa\">height</span><span class=\"token tag special-attr attr-value value css language-css punctuation\" style=\"color:#393A34\">:</span><span class=\"token tag special-attr attr-value value css language-css\" style=\"color:#e3116c\"> </span><span class=\"token tag special-attr attr-value value css language-css number\" style=\"color:#36acaa\">90</span><span class=\"token tag special-attr attr-value value css language-css unit\" style=\"color:#e3116c\">vh</span><span class=\"token tag special-attr attr-value value css language-css punctuation\" style=\"color:#393A34\">;</span><span class=\"token tag special-attr attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">&lt;!-- Your Custom Application Sidebar --&gt;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">aside</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag special-attr attr-name\" style=\"color:#00a4db\">style</span><span class=\"token tag special-attr attr-value punctuation attr-equals\" style=\"color:#393A34\">=</span><span class=\"token tag special-attr attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag special-attr attr-value value css language-css property\" style=\"color:#36acaa\">width</span><span class=\"token tag special-attr attr-value value css language-css punctuation\" style=\"color:#393A34\">:</span><span class=\"token tag special-attr attr-value value css language-css\" style=\"color:#e3116c\"> </span><span class=\"token tag special-attr attr-value value css language-css number\" style=\"color:#36acaa\">250</span><span class=\"token tag special-attr attr-value value css language-css unit\" style=\"color:#e3116c\">px</span><span class=\"token tag special-attr attr-value value css language-css punctuation\" style=\"color:#393A34\">;</span><span class=\"token tag special-attr attr-value value css language-css\" style=\"color:#e3116c\"> </span><span class=\"token tag special-attr attr-value value css language-css property\" style=\"color:#36acaa\">background</span><span class=\"token tag special-attr attr-value value css language-css punctuation\" style=\"color:#393A34\">:</span><span class=\"token tag special-attr attr-value value css language-css\" style=\"color:#e3116c\"> </span><span class=\"token tag special-attr attr-value value css language-css hexcode color\" style=\"color:#e3116c\">#f4f4f4</span><span class=\"token tag special-attr attr-value value css language-css punctuation\" style=\"color:#393A34\">;</span><span class=\"token tag special-attr attr-value value css language-css\" style=\"color:#e3116c\"> </span><span class=\"token tag special-attr attr-value value css language-css property\" style=\"color:#36acaa\">padding</span><span class=\"token tag special-attr attr-value value css language-css punctuation\" style=\"color:#393A34\">:</span><span class=\"token tag special-attr attr-value value css language-css\" style=\"color:#e3116c\"> </span><span class=\"token tag special-attr attr-value value css language-css number\" style=\"color:#36acaa\">1</span><span class=\"token tag special-attr attr-value value css language-css unit\" style=\"color:#e3116c\">rem</span><span class=\"token tag special-attr attr-value value css language-css punctuation\" style=\"color:#393A34\">;</span><span class=\"token tag special-attr attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">h2</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\">Meeting Notes</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">h2</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">p</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\">This sidebar is part of my parent application!</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">p</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">aside</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">div</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">&lt;!-- Dynamically inject JavaScript files --&gt;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  &lt;% clientFiles.js.forEach(file =&gt; { %&gt;</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    &lt;% if (file.startsWith('main-module.')) { %&gt;</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">script</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag attr-name\" style=\"color:#00a4db\">src</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:#393A34\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag attr-value\" style=\"color:#e3116c\">&lt;%= plugnmeetUrl %&gt;/assets/js/&lt;%= file %&gt;</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag attr-name\" style=\"color:#00a4db\">type</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:#393A34\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag attr-value\" style=\"color:#e3116c\">module</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">script</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    &lt;% } else { %&gt;</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">script</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag attr-name\" style=\"color:#00a4db\">src</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:#393A34\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag attr-value\" style=\"color:#e3116c\">&lt;%= plugnmeetUrl %&gt;/assets/js/&lt;%= file %&gt;</span><span class=\"token tag attr-value punctuation\" style=\"color:#393A34\">\"</span><span class=\"token tag\" style=\"color:#00009f\"> </span><span class=\"token tag attr-name\" style=\"color:#00a4db\">defer</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">script</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    &lt;% } %&gt;</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  &lt;% }); %&gt;</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;</span><span class=\"token tag\" style=\"color:#00009f\">script</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token script language-javascript\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">    </span><span class=\"token script language-javascript comment\" style=\"color:#999988;font-style:italic\">// Add the access token to the URL without reloading the page</span><span class=\"token script language-javascript\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">    </span><span class=\"token script language-javascript keyword\" style=\"color:#00009f\">const</span><span class=\"token script language-javascript\"> url </span><span class=\"token script language-javascript operator\" style=\"color:#393A34\">=</span><span class=\"token script language-javascript\"> </span><span class=\"token script language-javascript keyword\" style=\"color:#00009f\">new</span><span class=\"token script language-javascript\"> </span><span class=\"token script language-javascript class-name\">URL</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">(</span><span class=\"token script language-javascript dom variable\" style=\"color:#36acaa\">window</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">.</span><span class=\"token script language-javascript property-access\">location</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">)</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">;</span><span class=\"token script language-javascript\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">    url</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">.</span><span class=\"token script language-javascript property-access\">searchParams</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">.</span><span class=\"token script language-javascript method function property-access\" style=\"color:#d73a49\">set</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">(</span><span class=\"token script language-javascript string\" style=\"color:#e3116c\">'access_token'</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">,</span><span class=\"token script language-javascript\"> </span><span class=\"token script language-javascript string\" style=\"color:#e3116c\">'&lt;%= accessToken %&gt;'</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">)</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">;</span><span class=\"token script language-javascript\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">    </span><span class=\"token script language-javascript dom variable\" style=\"color:#36acaa\">window</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">.</span><span class=\"token script language-javascript property-access\">history</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">.</span><span class=\"token script language-javascript method function property-access\" style=\"color:#d73a49\">pushState</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">(</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">{</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">}</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">,</span><span class=\"token script language-javascript\"> </span><span class=\"token script language-javascript string\" style=\"color:#e3116c\">''</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">,</span><span class=\"token script language-javascript\"> url</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">)</span><span class=\"token script language-javascript punctuation\" style=\"color:#393A34\">;</span><span class=\"token script language-javascript\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token script language-javascript\">  </span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">script</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">body</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token tag punctuation\" style=\"color:#393A34\">&lt;/</span><span class=\"token tag\" style=\"color:#00009f\">html</span><span class=\"token tag punctuation\" style=\"color:#393A34\">&gt;</span><br></div></code></pre></div></div>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"the-result-a-truly-integrated-experience\">The Result: A Truly Integrated Experience<a href=\"https://www.plugnmeet.org/blog/developer-guide-custom-video-chat-ui-headless-api#the-result-a-truly-integrated-experience\" class=\"hash-link\" aria-label=\"Direct link to The Result: A Truly Integrated Experience\" title=\"Direct link to The Result: A Truly Integrated Experience\" translate=\"no\">​</a></h3>\n<p>When a user visits <code>/meeting</code>, they will see your application's custom header and sidebar, with the Plug-N-Meet video client rendered seamlessly in the middle. The client will be styled with the dynamic brand colors you passed from the backend.</p>\n<p>You have successfully broken out of the <code>&lt;iframe&gt;</code> box.</p>\n<p>From here, the possibilities are endless. You can use your own application's state to interact with the meeting, build custom layouts that respond to meeting events, and create a user experience that is completely unique to your product.</p>\n<p>This is the true power of a headless, API-first platform. It gives you the building blocks not just to add a feature, but to build a deeply integrated and professional product.</p>\n<hr>\n<p><strong>Ready to build your custom UI?</strong></p>\n<ul>\n<li class=\"\"><strong>Review the full <a class=\"\" href=\"https://www.plugnmeet.org/docs/api/get-client-files\"><code>getClientFiles</code> API Documentation</a>.</strong></li>\n<li class=\"\"><strong>Explore our <a class=\"\" href=\"https://www.plugnmeet.org/docs/developer-guide/design-customisation\">Design Customization Guide</a> for more branding options.</strong></li>\n<li class=\"\"><strong>Check out our official <a href=\"https://github.com/mynaparrot/plugNmeet-sdk-js\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">JavaScript SDK</a> to simplify your backend code.</strong></li>\n</ul>",
            "url": "https://www.plugnmeet.org/blog/developer-guide-custom-video-chat-ui-headless-api",
            "title": "A Developer's Guide to Building a Custom Video Chat UI with Plug-N-Meet's Headless API",
            "summary": "You've successfully integrated Plug-N-Meet into your application. You can create rooms, generate join tokens, and embed the client in an `. It's fast and it works. But now you want to go deeper. You want to break out of the ` box and build a truly seamless user experience where the video client feels like a native part of your application's UI.",
            "date_modified": "2026-02-07T00:00:00.000Z",
            "author": {
                "name": "Jibon L. Costa",
                "url": "https://github.com/jibon57"
            },
            "tags": [
                "developer",
                "tutorial",
                "how-to",
                "api",
                "headless",
                "getClientFiles",
                "white-label",
                "customization",
                "javascript"
            ]
        },
        {
            "id": "https://www.plugnmeet.org/blog/scaling-architecture-saves-money",
            "content_html": "<p>Your video service is a hit. User numbers are climbing, and engagement is high. But as you celebrate your success, a new problem emerges: your server bill is exploding. To keep up with demand, you keep buying bigger and bigger servers, paying a premium for resources that sit idle most of the day.</p>\n<p>What went wrong? You've fallen into the monolith trap.</p>\n<p>This post explains how Plug-N-Meet's modern, decoupled architecture is designed to prevent this exact problem, enabling you to scale your service efficiently and control your costs. For a business, this isn't just a technical detail—it's a core financial advantage.</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"the-monolith-trap-paying-for-empty-seats\">The Monolith Trap: Paying for Empty Seats<a href=\"https://www.plugnmeet.org/blog/scaling-architecture-saves-money#the-monolith-trap-paying-for-empty-seats\" class=\"hash-link\" aria-label=\"Direct link to The Monolith Trap: Paying for Empty Seats\" title=\"Direct link to The Monolith Trap: Paying for Empty Seats\" translate=\"no\">​</a></h3>\n<p>Many traditional video conferencing platforms are built as <strong>monoliths</strong>. This means the core application, the media processing, and the recording services are all bundled into a single, massive application.</p>\n<p>This creates a huge problem when it's time to scale:</p>\n<ul>\n<li class=\"\"><strong>If you need to support more concurrent users</strong>, you have to upgrade the <em>entire</em> server, even if your recording service is barely being used.</li>\n<li class=\"\"><strong>If a few large recording jobs are using all the CPU</strong>, your live meetings will lag and stutter, forcing you to buy a bigger server just to handle a temporary workload.</li>\n</ul>\n<p>A monolithic server is like renting a giant bus for a single passenger. You pay for all the empty seats, and if one part of the bus breaks down, the whole trip is canceled.</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"the-plug-n-meet-way-a-decoupled-intelligent-architecture\">The Plug-N-Meet Way: A Decoupled, Intelligent Architecture<a href=\"https://www.plugnmeet.org/blog/scaling-architecture-saves-money#the-plug-n-meet-way-a-decoupled-intelligent-architecture\" class=\"hash-link\" aria-label=\"Direct link to The Plug-N-Meet Way: A Decoupled, Intelligent Architecture\" title=\"Direct link to The Plug-N-Meet Way: A Decoupled, Intelligent Architecture\" translate=\"no\">​</a></h3>\n<p>Plug-N-Meet was designed from the ground up to avoid this trap. It is not one giant application; it is a set of specialized, independent services that work together.</p>\n<p>The three core components are:</p>\n<ol>\n<li class=\"\"><strong><code>plugnmeet-server</code> (The Brain):</strong> A lightweight Go application that handles all the API calls, authentication, and business logic.</li>\n<li class=\"\"><strong><code>LiveKit</code> (The Media Highway):</strong> A high-performance, open-source media server that manages all the real-time audio and video traffic.</li>\n<li class=\"\"><strong><code>plugnmeet-recorder</code> (The Factory):</strong> A dedicated Go application that handles CPU-intensive tasks like MP4 recording and RTMP broadcasting.</li>\n</ol>\n<p>These components are designed to be scaled <strong>independently</strong>. This is the key to cost-effective growth.</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"benefit-1-scale-only-what-you-need-elastic-scaling\">Benefit 1: Scale Only What You Need (Elastic Scaling)<a href=\"https://www.plugnmeet.org/blog/scaling-architecture-saves-money#benefit-1-scale-only-what-you-need-elastic-scaling\" class=\"hash-link\" aria-label=\"Direct link to Benefit 1: Scale Only What You Need (Elastic Scaling)\" title=\"Direct link to Benefit 1: Scale Only What You Need (Elastic Scaling)\" translate=\"no\">​</a></h3>\n<p>With a decoupled architecture, you can scale each component based on your actual needs.</p>\n<ul>\n<li class=\"\"><strong>Expecting a huge webinar?</strong> You don't need to touch your main application server. Simply spin up a few extra <code>LiveKit</code> media servers for the duration of the event, then spin them down afterward.</li>\n<li class=\"\"><strong>Seeing more users in a specific region?</strong> Deploy media servers in that geographic area to reduce latency, without having to replicate your entire backend.</li>\n</ul>\n<p>This is like adding more cashiers to a store only during the holiday rush. You get the performance you need when you need it, and you don't pay for idle resources during quiet periods.</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"benefit-2-isolate-your-workloads-improved-quality-of-service\">Benefit 2: Isolate Your Workloads (Improved Quality of Service)<a href=\"https://www.plugnmeet.org/blog/scaling-architecture-saves-money#benefit-2-isolate-your-workloads-improved-quality-of-service\" class=\"hash-link\" aria-label=\"Direct link to Benefit 2: Isolate Your Workloads (Improved Quality of Service)\" title=\"Direct link to Benefit 2: Isolate Your Workloads (Improved Quality of Service)\" translate=\"no\">​</a></h3>\n<p>In a monolith, a resource-hungry task can degrade the entire user experience. If someone starts a large recording, everyone's live video call could suffer.</p>\n<p>Plug-N-Meet's architecture prevents this. Because the <code>plugnmeet-recorder</code> is a separate service, you can run it on entirely different machines from your live media servers.</p>\n<ul>\n<li class=\"\"><strong>The Result:</strong> A CPU-intensive MP4 transcoding job will have <strong>zero impact</strong> on the quality and stability of your live meetings.</li>\n<li class=\"\"><strong>The Benefit:</strong> Your users always get a smooth, high-quality experience, which is critical for customer satisfaction and retention.</li>\n</ul>\n<hr>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"conclusion-a-lower-total-cost-of-ownership-tco\">Conclusion: A Lower Total Cost of Ownership (TCO)<a href=\"https://www.plugnmeet.org/blog/scaling-architecture-saves-money#conclusion-a-lower-total-cost-of-ownership-tco\" class=\"hash-link\" aria-label=\"Direct link to Conclusion: A Lower Total Cost of Ownership (TCO)\" title=\"Direct link to Conclusion: A Lower Total Cost of Ownership (TCO)\" translate=\"no\">​</a></h2>\n<p>\"Scalable\" isn't just a technical buzzword; it's a business feature. The ability to scale components independently and isolate workloads means you are not constantly over-provisioning your hardware. You pay only for the resources you actually use.</p>\n<p>As your platform grows, this intelligent design leads to a dramatically <strong>lower Total Cost of Ownership (TCO)</strong> compared to a monolithic solution. You can grow your user base sustainably without your infrastructure costs spiraling out of control.</p>\n<p>Plug-N-Meet's architecture isn't just a technical implementation detail; it's a strategic advantage designed to help your business succeed at scale.</p>\n<hr>\n<p><strong>Ready to learn more about scaling?</strong></p>\n<ul>\n<li class=\"\"><strong>Read our detailed <a class=\"\" href=\"https://www.plugnmeet.org/docs/developer-guide/scalable-setup\">Scalable Deployment Guide</a></strong></li>\n<li class=\"\"><strong>Dive deep into our <a class=\"\" href=\"https://www.plugnmeet.org/blog/backend-architecture-deep-dive\">Backend Architecture</a></strong></li>\n<li class=\"\"><strong>Get started with the <a class=\"\" href=\"https://www.plugnmeet.org/docs/installation\">Installation Guide</a></strong></li>\n</ul>",
            "url": "https://www.plugnmeet.org/blog/scaling-architecture-saves-money",
            "title": "Scaling Your Video Service: How Plug-N-Meet's Architecture Saves You Money",
            "summary": "Your video service is a hit. User numbers are climbing, and engagement is high. But as you celebrate your success, a new problem emerges: your server bill is exploding. To keep up with demand, you keep buying bigger and bigger servers, paying a premium for resources that sit idle most of the day.",
            "date_modified": "2026-01-31T00:00:00.000Z",
            "author": {
                "name": "Chaboud Simon",
                "url": "https://github.com/saimonzone"
            },
            "tags": [
                "scaling",
                "architecture",
                "tco",
                "cost-saving",
                "self-hosting",
                "business",
                "developer"
            ]
        },
        {
            "id": "https://www.plugnmeet.org/blog/guide-to-managing-meeting-recordings",
            "content_html": "<p>For many platforms, the \"record\" button is the end of the story. You get an MP4 file, and that's it. But what if your recordings could be more? What if they could be a structured, searchable, and accessible content library for your students, your team, or your customers?</p>\n<p>With Plug-N-Meet's powerful API, your recordings aren't just video files; they are programmable assets.</p>\n<p>This guide will show you how to move beyond simply recording a meeting and start building a true content management system using the Recordings API. We'll cover how to find your recordings, enrich them with metadata, and even attach subtitles.</p>\n<hr>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"the-goal\">The Goal<a href=\"https://www.plugnmeet.org/blog/guide-to-managing-meeting-recordings#the-goal\" class=\"hash-link\" aria-label=\"Direct link to The Goal\" title=\"Direct link to The Goal\" translate=\"no\">​</a></h2>\n<p>By the end of this guide, you will be able to programmatically manage your entire recording lifecycle, turning a simple list of video files into a rich, queryable content library.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"prerequisites\">Prerequisites<a href=\"https://www.plugnmeet.org/blog/guide-to-managing-meeting-recordings#prerequisites\" class=\"hash-link\" aria-label=\"Direct link to Prerequisites\" title=\"Direct link to Prerequisites\" translate=\"no\">​</a></h2>\n<ul>\n<li class=\"\">A running Plug-N-Meet server.</li>\n<li class=\"\">Basic familiarity with making API calls (we'll use <code>cURL</code> for the examples).</li>\n<li class=\"\">Your Plug-N-Meet <code>API_KEY</code> and <code>API_SECRET</code>.</li>\n</ul>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"step-1-enable-cloud-recording\">Step 1: Enable Cloud Recording<a href=\"https://www.plugnmeet.org/blog/guide-to-managing-meeting-recordings#step-1-enable-cloud-recording\" class=\"hash-link\" aria-label=\"Direct link to Step 1: Enable Cloud Recording\" title=\"Direct link to Step 1: Enable Cloud Recording\" translate=\"no\">​</a></h3>\n<p>First, you need to ensure your meetings are being recorded. You can do this on a per-room basis when you create it. In your <code>createRoom</code> API call, include the <code>recording_features</code> block and set <code>is_allow_cloud</code> to <code>true</code>.</p>\n<p>For a \"record by default\" experience, you can also enable <code>enable_auto_cloud_recording</code>.</p>\n<div class=\"language-json codeBlockContainer_ibEU theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_osW6\"><pre tabindex=\"0\" class=\"prism-code language-json codeBlock_lRqf thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_jDlT\"><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token property\" style=\"color:#36acaa\">\"room_id\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"my-recorded-session\"</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token property\" style=\"color:#36acaa\">\"metadata\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token property\" style=\"color:#36acaa\">\"room_features\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// ... other features</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token property\" style=\"color:#36acaa\">\"recording_features\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">        </span><span class=\"token property\" style=\"color:#36acaa\">\"is_allow\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:#36acaa\">true</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">        </span><span class=\"token property\" style=\"color:#36acaa\">\"is_allow_cloud\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:#36acaa\">true</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">        </span><span class=\"token property\" style=\"color:#36acaa\">\"enable_auto_cloud_recording\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:#36acaa\">true</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><br></div></code></pre></div></div>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"step-2-find-your-recordings\">Step 2: Find Your Recordings<a href=\"https://www.plugnmeet.org/blog/guide-to-managing-meeting-recordings#step-2-find-your-recordings\" class=\"hash-link\" aria-label=\"Direct link to Step 2: Find Your Recordings\" title=\"Direct link to Step 2: Find Your Recordings\" translate=\"no\">​</a></h3>\n<p>After your meetings are over, the first step is to get a list of your available recordings. You can do this using the <code>recording/fetch</code> endpoint. You can fetch recordings for one or more specific rooms by providing their IDs in the <code>room_ids</code> array.</p>\n<p>This call will return a paginated list of all recordings for the specified room(s).</p>\n<div class=\"language-bash codeBlockContainer_ibEU theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_osW6\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_lRqf thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_jDlT\"><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"># The raw JSON body as a string</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">BODY='{\"room_ids\":[\"room01\"],\"from\":0,\"limit\":20,\"order_by\":\"DESC\"}'</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"># Your API credentials</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">API_KEY=\"YOUR_API_KEY\"</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">SECRET=\"YOUR_API_SECRET\"</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"># Generate the HMAC-SHA256 signature</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">SIGNATURE=$(echo -n \"$BODY\" | openssl dgst -sha256 -mac HMAC -macopt key:\"$SECRET\" | awk '{print $2}')</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"># Make the POST request</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">curl -X POST https://YOUR_SERVER_URL/auth/recording/fetch \\</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  -H \"Content-Type: application/json\" \\</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  -H \"API-KEY: $API_KEY\" \\</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  -H \"HASH-SIGNATURE: $SIGNATURE\" \\</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  -d \"$BODY\"</span><br></div></code></pre></div></div>\n<p>The response will give you a list that includes the <code>record_id</code>, <code>room_id</code>, <code>file_size</code>, and <code>creation_time</code> for each recording—the essential building blocks for your content library.</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"step-3-make-your-recordings-discoverable-with-metadata\">Step 3: Make Your Recordings Discoverable with Metadata<a href=\"https://www.plugnmeet.org/blog/guide-to-managing-meeting-recordings#step-3-make-your-recordings-discoverable-with-metadata\" class=\"hash-link\" aria-label=\"Direct link to Step 3: Make Your Recordings Discoverable with Metadata\" title=\"Direct link to Step 3: Make Your Recordings Discoverable with Metadata\" translate=\"no\">​</a></h3>\n<p>A filename like <code>rec_123.mp4</code> isn't very helpful. To make your recordings useful, you need to add context. The <code>recording/update-metadata</code> endpoint allows you to add a descriptive title, a detailed description, and even your own custom data.</p>\n<p>Let's say you just finished a \"Q4 Marketing Sync\" and you want to tag it appropriately.</p>\n<div class=\"language-bash codeBlockContainer_ibEU theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_osW6\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_lRqf thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_jDlT\"><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"># The raw JSON body as a string</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">BODY='{\"record_id\":\"f031a270-20ab-433d-96e9-aed34db97c0a-1761814595173\",\"metadata\":{\"title\":\"Q4 Marketing Sync - Final Strategy\",\"description\":\"A detailed review of the Q4 marketing plan, including budget allocation and final campaign targets.\",\"extra_data\":{\"department\":\"Marketing\",\"quarter\":\"Q4-2025\",\"is_public\":\"true\"}}}'</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"># Your API credentials</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">API_KEY=\"YOUR_API_KEY\"</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">SECRET=\"YOUR_API_SECRET\"</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"># Generate the HMAC-SHA256 signature</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">SIGNATURE=$(echo -n \"$BODY\" | openssl dgst -sha256 -mac HMAC -macopt key:\"$SECRET\" | awk '{print $2}')</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"># Make the POST request</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">curl -X POST https://YOUR_SERVER_URL/auth/recording/update-metadata \\</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  -H \"Content-Type: application/json\" \\</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  -H \"API-KEY: $API_KEY\" \\</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  -H \"HASH-SIGNATURE: $SIGNATURE\" \\</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  -d \"$BODY\"</span><br></div></code></pre></div></div>\n<p>Now, when you fetch this recording, it will come with rich, structured data. You can use the <code>extra_data</code> field to build powerful filtering and search features in your own application (e.g., \"show me all recordings from the Marketing department\").</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"step-4-bonus-attach-subtitles-and-summaries\">Step 4 (Bonus): Attach Subtitles and Summaries<a href=\"https://www.plugnmeet.org/blog/guide-to-managing-meeting-recordings#step-4-bonus-attach-subtitles-and-summaries\" class=\"hash-link\" aria-label=\"Direct link to Step 4 (Bonus): Attach Subtitles and Summaries\" title=\"Direct link to Step 4 (Bonus): Attach Subtitles and Summaries\" translate=\"no\">​</a></h3>\n<p>If you used the <strong>AI Meeting Assistant</strong> during your meeting, you can link the generated transcription file directly to your recording's metadata.</p>\n<ol>\n<li class=\"\">First, use the <code>artifacts/fetch</code> API to get the URL of the VTT subtitle file.</li>\n<li class=\"\">Then, call <code>recording/update-metadata</code> again, this time adding a <code>subtitles</code> map.</li>\n</ol>\n<div class=\"language-bash codeBlockContainer_ibEU theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_osW6\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_lRqf thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_jDlT\"><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"># The raw JSON body as a string</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">BODY='{\"record_id\":\"f031a270-20ab-433d-96e9-aed34db97c0a-1761814595173\",\"metadata\":{\"subtitles\":{\"en\":{\"label\":\"English\",\"url\":\"https://YOUR_SERVER_URL/path/to/your/transcript.vtt\"}}}}'</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"># Your API credentials</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">API_KEY=\"YOUR_API_KEY\"</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">SECRET=\"YOUR_API_SECRET\"</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"># Generate the HMAC-SHA256 signature</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">SIGNATURE=$(echo -n \"$BODY\" | openssl dgst -sha256 -mac HMAC -macopt key:\"$SECRET\" | awk '{print $2}')</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"># Make the POST request</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">curl -X POST https://YOUR_SERVER_URL/auth/recording/update-metadata \\</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  -H \"Content-Type: application/json\" \\</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  -H \"API-KEY: $API_KEY\" \\</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  -H \"HASH-SIGNATURE: $SIGNATURE\" \\</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  -d \"$BODY\"</span><br></div></code></pre></div></div>\n<p>By linking these assets together, you create a far more valuable piece of content. Your users can now watch the recording with closed captions, improving accessibility and comprehension.</p>\n<hr>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"conclusion\">Conclusion<a href=\"https://www.plugnmeet.org/blog/guide-to-managing-meeting-recordings#conclusion\" class=\"hash-link\" aria-label=\"Direct link to Conclusion\" title=\"Direct link to Conclusion\" translate=\"no\">​</a></h2>\n<p>Stop thinking of your recordings as a simple archive. With Plug-N-Meet's API-first approach, you have the tools to build a sophisticated, searchable, and accessible content management system.</p>\n<p>By programmatically fetching, enriching, and linking your recordings with other assets, you can transform a simple folder of MP4 files into a powerful knowledge base for your organization.</p>\n<hr>\n<p><strong>Ready to build your content library?</strong></p>\n<ul>\n<li class=\"\"><strong>Explore the full <a class=\"\" href=\"https://www.plugnmeet.org/docs/api/recording/fetch\">Recording API Documentation</a></strong></li>\n<li class=\"\"><strong>Learn how to generate summaries with our <a class=\"\" href=\"https://www.plugnmeet.org/blog/how-to-add-ai-meeting-assistant-features\">AI Features Guide</a></strong></li>\n<li class=\"\"><strong>Automate this entire process with <a class=\"\" href=\"https://www.plugnmeet.org/blog/how-to-build-your-first-webhook-workflow\">Webhooks</a></strong></li>\n</ul>",
            "url": "https://www.plugnmeet.org/blog/guide-to-managing-meeting-recordings",
            "title": "Beyond the Record Button: A Guide to Managing and Using Your Meeting Recordings",
            "summary": "For many platforms, the \"record\" button is the end of the story. You get an MP4 file, and that's it. But what if your recordings could be more? What if they could be a structured, searchable, and accessible content library for your students, your team, or your customers?",
            "date_modified": "2026-01-24T00:00:00.000Z",
            "author": {
                "name": "Bob Teng",
                "url": "https://github.com/wbobteng"
            },
            "tags": [
                "tutorial",
                "how-to",
                "recordings",
                "api",
                "content-management",
                "developer"
            ]
        },
        {
            "id": "https://www.plugnmeet.org/blog/sip-dial-in-for-video-conferencing",
            "content_html": "<p>We've all been there: a crucial meeting is about to start, but a key participant has a spotty internet connection, is traveling, or simply doesn't have access to a computer. In a world increasingly reliant on video calls, how do you ensure everyone can still participate, even if they can't join online?</p>\n<p>At Plug-N-Meet, we believe in inclusivity. That's why we're excited to introduce <strong>SIP Dial-In</strong>, a powerful feature that allows participants to join the audio of your video meetings using any standard telephone. No internet, no problem.</p>\n<p>This feature seamlessly bridges the gap between your online conference and the traditional telephone network, ensuring that every voice can be heard, regardless of their connectivity.</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"the-problem-when-internet-isnt-an-option\">The Problem: When Internet Isn't an Option<a href=\"https://www.plugnmeet.org/blog/sip-dial-in-for-video-conferencing#the-problem-when-internet-isnt-an-option\" class=\"hash-link\" aria-label=\"Direct link to The Problem: When Internet Isn't an Option\" title=\"Direct link to The Problem: When Internet Isn't an Option\" translate=\"no\">​</a></h3>\n<p>While WebRTC-based video conferencing is incredibly powerful, it relies on a stable internet connection. This can be a barrier for:</p>\n<ul>\n<li class=\"\"><strong>Travelers:</strong> On the road, in a car, or in areas with poor Wi-Fi.</li>\n<li class=\"\"><strong>Remote Locations:</strong> Participants in regions with limited internet infrastructure.</li>\n<li class=\"\"><strong>Accessibility Needs:</strong> Individuals who prefer or require telephone access.</li>\n<li class=\"\"><strong>Simple Audio-Only Participation:</strong> Sometimes, all you need is to listen and speak, without the video.</li>\n</ul>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"the-solution-plug-n-meets-sip-dial-in\">The Solution: Plug-N-Meet's SIP Dial-In<a href=\"https://www.plugnmeet.org/blog/sip-dial-in-for-video-conferencing#the-solution-plug-n-meets-sip-dial-in\" class=\"hash-link\" aria-label=\"Direct link to The Solution: Plug-N-Meet's SIP Dial-In\" title=\"Direct link to The Solution: Plug-N-Meet's SIP Dial-In\" translate=\"no\">​</a></h3>\n<p>Our SIP Dial-In feature integrates your Plug-N-Meet meetings with the global telephone network. It works by setting up a <strong>SIP Gateway</strong> that acts as a bridge, allowing traditional phone calls to connect directly into your online conference.</p>\n<h4 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"how-it-works-for-participants-simple--familiar\">How It Works for Participants (Simple &amp; Familiar)<a href=\"https://www.plugnmeet.org/blog/sip-dial-in-for-video-conferencing#how-it-works-for-participants-simple--familiar\" class=\"hash-link\" aria-label=\"Direct link to How It Works for Participants (Simple &amp; Familiar)\" title=\"Direct link to How It Works for Participants (Simple &amp; Familiar)\" translate=\"no\">​</a></h4>\n<ol>\n<li class=\"\">A participant receives a standard phone number and a unique PIN for the meeting.</li>\n<li class=\"\">They dial the number from any phone (mobile or landline).</li>\n<li class=\"\">They enter the PIN when prompted.</li>\n<li class=\"\">They are instantly connected to the meeting's audio, able to hear and speak with everyone in the online session.</li>\n</ol>\n<h4 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"how-it-works-for-moderators-easy-control\">How It Works for Moderators (Easy Control)<a href=\"https://www.plugnmeet.org/blog/sip-dial-in-for-video-conferencing#how-it-works-for-moderators-easy-control\" class=\"hash-link\" aria-label=\"Direct link to How It Works for Moderators (Easy Control)\" title=\"Direct link to How It Works for Moderators (Easy Control)\" translate=\"no\">​</a></h4>\n<p>Moderators have full control over the SIP dial-in service from within the meeting:</p>\n<ul>\n<li class=\"\"><strong>Enable/Disable:</strong> Start or stop the dial-in service for the current session.</li>\n<li class=\"\"><strong>View Information:</strong> See the active dial-in number(s) and PIN.</li>\n<li class=\"\"><strong>Share Instantly:</strong> With a single click, post the dial-in details directly into the public chat for all online participants.</li>\n<li class=\"\"><strong>Privacy Options:</strong> Choose to mask the dial-in number in the participant list, showing only the last four digits for added privacy.</li>\n</ul>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"technical-deep-dive-powering-the-connection\">Technical Deep Dive: Powering the Connection<a href=\"https://www.plugnmeet.org/blog/sip-dial-in-for-video-conferencing#technical-deep-dive-powering-the-connection\" class=\"hash-link\" aria-label=\"Direct link to Technical Deep Dive: Powering the Connection\" title=\"Direct link to Technical Deep Dive: Powering the Connection\" translate=\"no\">​</a></h3>\n<p>Under the hood, Plug-N-Meet leverages the robust <strong><a href=\"https://github.com/livekit/sip\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">livekit/sip</a></strong> project as its SIP gateway. This component is responsible for managing the connection between your meeting and an external SIP trunking provider.</p>\n<h4 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"for-administrators-server-setup-is-key\">For Administrators: Server Setup is Key<a href=\"https://www.plugnmeet.org/blog/sip-dial-in-for-video-conferencing#for-administrators-server-setup-is-key\" class=\"hash-link\" aria-label=\"Direct link to For Administrators: Server Setup is Key\" title=\"Direct link to For Administrators: Server Setup is Key\" translate=\"no\">​</a></h4>\n<p>To enable SIP Dial-In, your system administrator must perform a one-time server-side configuration:</p>\n<ol>\n<li class=\"\"><strong><code>config.yaml</code> Setup:</strong> The <code>livekit_sip_info</code> section in your Plug-N-Meet server's <code>config.yaml</code> file must be configured with your SIP provider's details.</li>\n<li class=\"\"><strong>External SIP Provider:</strong> You will need an account with a SIP trunking provider (e.g., Twilio, SignalWire, etc.) to handle the actual phone calls to and from the public telephone network (PSTN).</li>\n</ol>\n<h4 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"security-considerations-encrypting-the-phone-leg\">Security Considerations: Encrypting the Phone Leg<a href=\"https://www.plugnmeet.org/blog/sip-dial-in-for-video-conferencing#security-considerations-encrypting-the-phone-leg\" class=\"hash-link\" aria-label=\"Direct link to Security Considerations: Encrypting the Phone Leg\" title=\"Direct link to Security Considerations: Encrypting the Phone Leg\" translate=\"no\">​</a></h4>\n<p>While the SIP audio stream itself cannot be end-to-end encrypted (as it must traverse the traditional phone network), the connection between your SIP gateway and your SIP provider <em>can</em> be secured. The <code>livekit/sip</code> service supports <code>SIPMediaEncryption</code> (SRTP), which encrypts the audio stream over the internet. It is the administrator's responsibility to enable this and ensure their chosen SIP provider supports it.</p>\n<h4 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"e2ee-incompatibility\">E2EE Incompatibility<a href=\"https://www.plugnmeet.org/blog/sip-dial-in-for-video-conferencing#e2ee-incompatibility\" class=\"hash-link\" aria-label=\"Direct link to E2EE Incompatibility\" title=\"Direct link to E2EE Incompatibility\" translate=\"no\">​</a></h4>\n<p>It's important to note that the SIP gateway must process unencrypted audio to bridge the call. Because of this, <strong>SIP Dial-In is automatically disabled for any room where End-to-End Encryption (E2EE) is active</strong>. This ensures that the integrity of your E2EE sessions is never compromised.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"conclusion-expanding-your-reach\">Conclusion: Expanding Your Reach<a href=\"https://www.plugnmeet.org/blog/sip-dial-in-for-video-conferencing#conclusion-expanding-your-reach\" class=\"hash-link\" aria-label=\"Direct link to Conclusion: Expanding Your Reach\" title=\"Direct link to Conclusion: Expanding Your Reach\" translate=\"no\">​</a></h3>\n<p>Plug-N-Meet's SIP Dial-In feature is more than just a convenience; it's a powerful tool for inclusivity and accessibility. It ensures that your meetings can reach a wider audience, providing a reliable audio connection even when internet access is a challenge.</p>\n<p>By bridging the gap between online and traditional telephony, Plug-N-Meet empowers you to connect with everyone, everywhere.</p>\n<hr>\n<p><strong>Ready to expand your meeting's reach?</strong></p>\n<ul>\n<li class=\"\"><strong>Learn how to enable SIP Dial-In when creating a room via the <a class=\"\" href=\"https://www.plugnmeet.org/docs/api/room/create\">Create Room API</a>.</strong></li>\n<li class=\"\"><strong>Understand the security implications in our <a class=\"\" href=\"https://www.plugnmeet.org/docs/security-overview\">Security Overview</a>.</strong></li>\n<li class=\"\"><strong>Explore the <a href=\"https://github.com/livekit/sip\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">livekit/sip GitHub repository</a> for detailed setup instructions.</strong></li>\n<li class=\"\"><strong><a href=\"https://demo.plugnmeet.com/landing.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Try the Live Demo</a> to experience Plug-N-Meet's features.</strong></li>\n</ul>",
            "url": "https://www.plugnmeet.org/blog/sip-dial-in-for-video-conferencing",
            "title": "Bridging the Gap: How SIP Dial-In Connects Your Meetings to Any Phone",
            "summary": "We've all been there: a crucial meeting is about to start, but a key participant has a spotty internet connection, is traveling, or simply doesn't have access to a computer. In a world increasingly reliant on video calls, how do you ensure everyone can still participate, even if they can't join online?",
            "date_modified": "2026-01-21T00:00:00.000Z",
            "author": {
                "name": "Bob Teng",
                "url": "https://github.com/wbobteng"
            },
            "tags": [
                "sip",
                "voip",
                "telephony",
                "integration",
                "accessibility",
                "feature",
                "moderator",
                "admin"
            ]
        },
        {
            "id": "https://www.plugnmeet.org/blog/bigbluebutton-vs-plugnmeet-alternative",
            "content_html": "<p>If you're in the world of open-source education or communication, you owe a debt of gratitude to BigBlueButton. It was a pioneering platform that showed the world what was possible. However, as the web has evolved, the demands for scalability, developer experience, and cost-effective performance have grown exponentially.</p>\n<p>Many long-time BigBlueButton users are now looking for a next-generation solution, one designed from the ground up to meet the new demands for scalability and flexibility that the modern web requires. This is where Plug-N-Meet comes in.</p>\n<p>This article provides a direct, head-to-head comparison to help you understand the key differences in philosophy and technology, so you can make an informed decision about which platform is right for you.</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"at-a-glance-a-head-to-head-comparison\">At a Glance: A Head-to-Head Comparison<a href=\"https://www.plugnmeet.org/blog/bigbluebutton-vs-plugnmeet-alternative#at-a-glance-a-head-to-head-comparison\" class=\"hash-link\" aria-label=\"Direct link to At a Glance: A Head-to-Head Comparison\" title=\"Direct link to At a Glance: A Head-to-Head Comparison\" translate=\"no\">​</a></h3>\n<table><thead><tr><th style=\"text-align:left\">Feature / Aspect</th><th style=\"text-align:left\">BigBlueButton (BBB)</th><th style=\"text-align:left\">Plug-N-Meet</th><th style=\"text-align:left\">Why It Matters</th></tr></thead><tbody><tr><td style=\"text-align:left\"><strong>Core Architecture</strong></td><td style=\"text-align:left\"><strong>Monolithic:</strong> Tightly coupled services (web, media, recording).</td><td style=\"text-align:left\"><strong>Decoupled:</strong> Independent microservices for the app, media, and recorder.</td><td style=\"text-align:left\">You can scale, update, or maintain one part of Plug-N-Meet without impacting the others, leading to higher stability and lower costs.</td></tr><tr><td style=\"text-align:left\"><strong>Technology Stack</strong></td><td style=\"text-align:left\"><strong>Complex Mix:</strong> A combination of Scala, Java, JavaScript, Ruby, etc.</td><td style=\"text-align:left\"><strong>Unified &amp; Modern:</strong> Go for the entire backend, TypeScript/React for the frontend.</td><td style=\"text-align:left\">A simpler, unified stack is far easier to maintain, debug, and contribute to, resulting in faster development cycles.</td></tr><tr><td style=\"text-align:left\"><strong>Media Server</strong></td><td style=\"text-align:left\"><strong>FreeSWITCH &amp; mediasoup</strong> (previously Kurento)</td><td style=\"text-align:left\"><strong>LiveKit</strong> (a modern, high-performance SFU)</td><td style=\"text-align:left\">LiveKit is purpose-built for scalable WebRTC, offering better performance, adaptive streaming (Simulcast/Dynacast), and lower resource usage out of the box.</td></tr><tr><td style=\"text-align:left\"><strong>Security &amp; E2EE</strong></td><td style=\"text-align:left\">Basic encryption. E2EE is not a native, fully integrated feature.</td><td style=\"text-align:left\"><strong>Native End-to-End Encryption (E2EE):</strong> A core, API-controlled feature with multiple key management models.</td><td style=\"text-align:left\">Plug-N-Meet provides true zero-trust security, ensuring not even the server can access meeting content. This is critical for privacy-sensitive applications.</td></tr><tr><td style=\"text-align:left\"><strong>Recording</strong></td><td style=\"text-align:left\">Reconstructs a presentation from separately recorded raw streams.</td><td style=\"text-align:left\"><strong>High-Fidelity Capture:</strong> A headless browser records the final rendered output to a single MP4.</td><td style=\"text-align:left\">Plug-N-Meet's method produces a perfect, \"what you see is what you get\" replica of the live session, ensuring perfect synchronization.</td></tr><tr><td style=\"text-align:left\"><strong>Customization</strong></td><td style=\"text-align:left\"><strong>Static Theming:</strong> Requires complex server-side configuration and often code modification.</td><td style=\"text-align:left\"><strong>Multi-Layered &amp; Dynamic:</strong> Offers four levels of customization, from simple branding to a \"headless\" native integration.</td><td style=\"text-align:left\">Plug-N-Meet's API-driven approach enables true multi-tenant branding and a vastly superior white-label experience.</td></tr><tr><td style=\"text-align:left\"><strong>Installation</strong></td><td style=\"text-align:left\">Scripted, but with heavy, specific OS dependencies.</td><td style=\"text-align:left\"><strong>Automated script using Docker containers.</strong></td><td style=\"text-align:left\">Plug-N-Meet's containerized setup is faster and avoids conflicts with other services on the host machine.</td></tr><tr><td style=\"text-align:left\"><strong>Upgrades &amp; Maintenance</strong></td><td style=\"text-align:left\"><strong>Full Rebuild Required:</strong> OS upgrades require a new server and manual data migration.</td><td style=\"text-align:left\"><strong>Simple &amp; In-Place:</strong> Docker abstracts the OS, allowing safe, independent updates.</td><td style=\"text-align:left\">You avoid the operational cost and risk of rebuilding your server for every major OS update.</td></tr><tr><td style=\"text-align:left\"><strong>Multi-Tenancy</strong></td><td style=\"text-align:left\"><strong>Domain-Coupled:</strong> Difficult to serve multiple domains from one instance.</td><td style=\"text-align:left\"><strong>Domain-Agnostic:</strong> A single server can easily serve unlimited domains via a reverse proxy.</td><td style=\"text-align:left\">Greatly simplifies offering a white-labeled service to multiple clients from a single, cost-effective server instance.</td></tr></tbody></table>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"key-difference-1-the-architectural-philosophy\">Key Difference 1: The Architectural Philosophy<a href=\"https://www.plugnmeet.org/blog/bigbluebutton-vs-plugnmeet-alternative#key-difference-1-the-architectural-philosophy\" class=\"hash-link\" aria-label=\"Direct link to Key Difference 1: The Architectural Philosophy\" title=\"Direct link to Key Difference 1: The Architectural Philosophy\" translate=\"no\">​</a></h3>\n<p>The most fundamental difference is in the design philosophy.</p>\n<p><strong>BigBlueButton</strong> is a <strong>monolith</strong>. Its core components are tightly interwoven. This means if your recording service is consuming all the CPU, it directly impacts the performance of your live meetings.</p>\n<p><strong>Plug-N-Meet</strong> is built on a <strong>decoupled, microservices-based architecture</strong>. The application server, the LiveKit media server, and the recorder are all independent services. This allows you to isolate workloads and scale intelligently, leading to a significantly lower Total Cost of Ownership (TCO).</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"key-difference-2-a-multi-layered-approach-to-customization\">Key Difference 2: A Multi-Layered Approach to Customization<a href=\"https://www.plugnmeet.org/blog/bigbluebutton-vs-plugnmeet-alternative#key-difference-2-a-multi-layered-approach-to-customization\" class=\"hash-link\" aria-label=\"Direct link to Key Difference 2: A Multi-Layered Approach to Customization\" title=\"Direct link to Key Difference 2: A Multi-Layered Approach to Customization\" translate=\"no\">​</a></h3>\n<p>This is a game-changer for anyone building a white-label or multi-tenant service.</p>\n<p><strong>BigBlueButton</strong> uses a <strong>static theming</strong> approach. Customizing the look and feel is a complex, server-wide change that often requires modifying configuration files and restarting services.</p>\n<p><strong>Plug-N-Meet</strong> is <strong>API-first and dynamic</strong>, offering a <strong><a class=\"\" href=\"https://www.plugnmeet.org/blog/true-white-label-video-conferencing\">multi-layered approach to white-labeling</a></strong> that allows you to choose the level of customization that fits your needs:</p>\n<ul>\n<li class=\"\"><strong>Level 1: Quick Rebranding:</strong> Instantly change logos, colors, and backgrounds via a simple <strong><a class=\"\" href=\"https://www.plugnmeet.org/docs/developer-guide/design-customisation\">configuration object</a></strong>.</li>\n<li class=\"\"><strong>Level 2: API-Driven Feature Control:</strong> Programmatically enable or disable features (like the whiteboard or breakout rooms) to create purpose-built experiences for different use cases.</li>\n<li class=\"\"><strong>Level 3: Deep Styling with Custom CSS:</strong> Provide a URL to your own stylesheet for pixel-perfect control over any UI element.</li>\n<li class=\"\"><strong>Level 4: True Native Integration:</strong> Use the <strong><a class=\"\" href=\"https://www.plugnmeet.org/docs/api/get-client-files\"><code>getClientFiles</code> API</a></strong> to render the client \"headless\" directly within your own application, giving you complete control over the layout and user experience.</li>\n</ul>\n<p>This layered flexibility makes it easy to get started but provides a path to a truly unique and deeply integrated product.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"key-difference-3-security-by-design---native-end-to-end-encryption\">Key Difference 3: Security by Design - Native End-to-End Encryption<a href=\"https://www.plugnmeet.org/blog/bigbluebutton-vs-plugnmeet-alternative#key-difference-3-security-by-design---native-end-to-end-encryption\" class=\"hash-link\" aria-label=\"Direct link to Key Difference 3: Security by Design - Native End-to-End Encryption\" title=\"Direct link to Key Difference 3: Security by Design - Native End-to-End Encryption\" translate=\"no\">​</a></h3>\n<p><strong>BigBlueButton</strong>'s architecture requires the server to have access to unencrypted media streams for features like recording. This architectural choice is incompatible with a zero-trust E2EE model.</p>\n<p><strong>Plug-N-Meet</strong> was built with a <strong>\"privacy by design\"</strong> philosophy. E2EE is a <strong>core, native feature</strong> of the platform. As detailed in our <strong><a class=\"\" href=\"https://www.plugnmeet.org/docs/security-overview\">Security Overview</a></strong>, we provide multiple API-controlled models, including a zero-trust option where the encryption key never touches the server, providing a mathematical guarantee of privacy.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"key-difference-4-the-recording-philosophy---perfect-fidelity-vs-complex-reconstruction\">Key Difference 4: The Recording Philosophy - Perfect Fidelity vs. Complex Reconstruction<a href=\"https://www.plugnmeet.org/blog/bigbluebutton-vs-plugnmeet-alternative#key-difference-4-the-recording-philosophy---perfect-fidelity-vs-complex-reconstruction\" class=\"hash-link\" aria-label=\"Direct link to Key Difference 4: The Recording Philosophy - Perfect Fidelity vs. Complex Reconstruction\" title=\"Direct link to Key Difference 4: The Recording Philosophy - Perfect Fidelity vs. Complex Reconstruction\" translate=\"no\">​</a></h3>\n<p><strong>BigBlueButton</strong> records individual streams and attempts to reassemble them in post-processing, which can be a complex task.</p>\n<p><strong>Plug-N-Meet</strong>, as detailed in our <strong><a class=\"\" href=\"https://www.plugnmeet.org/blog/recording-philosophy\">Recording Philosophy</a></strong>, uses a headless browser to capture the final, rendered output. This produces a \"what you see is what you get\" MP4 file that is a perfect, high-fidelity replica of the live experience, ensuring all elements are perfectly synchronized.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"key-difference-5-long-term-maintainability-and-upgrades\">Key Difference 5: Long-Term Maintainability and Upgrades<a href=\"https://www.plugnmeet.org/blog/bigbluebutton-vs-plugnmeet-alternative#key-difference-5-long-term-maintainability-and-upgrades\" class=\"hash-link\" aria-label=\"Direct link to Key Difference 5: Long-Term Maintainability and Upgrades\" title=\"Direct link to Key Difference 5: Long-Term Maintainability and Upgrades\" translate=\"no\">​</a></h3>\n<p>This is a significant consideration for any administrator.</p>\n<p><strong>BigBlueButton</strong> is <strong>tightly coupled to a specific OS version</strong>. The official upgrade path for a new OS often requires provisioning a brand new server and manually migrating all data and recordings. This can be a time-consuming and complex undertaking.</p>\n<p><strong>Plug-N-Meet</strong> completely avoids this problem by using <strong>Docker</strong>. The entire application runs in isolated containers, abstracting it from the host OS. You can upgrade your server's OS without breaking the application, and updating Plug-N-Meet is as simple as pulling a new Docker image. This makes long-term maintenance dramatically simpler and safer.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"the-migration-path-try-without-the-risk\">The Migration Path: Try Without the Risk<a href=\"https://www.plugnmeet.org/blog/bigbluebutton-vs-plugnmeet-alternative#the-migration-path-try-without-the-risk\" class=\"hash-link\" aria-label=\"Direct link to The Migration Path: Try Without the Risk\" title=\"Direct link to The Migration Path: Try Without the Risk\" translate=\"no\">​</a></h3>\n<p>We understand that migrating from a deeply integrated platform is a big decision. That's why we made it painless.</p>\n<p>Plug-N-Meet includes a <strong>BBB-compatible API layer</strong>. This means you can point your existing Greenlight, Moodle, or custom application to your Plug-N-Meet server, and it will work <strong>without any front-end code changes</strong>. This allows you to test the performance and stability of Plug-N-Meet in your own environment with zero risk.</p>\n<p>Ready to take the next step? Follow our simple, step-by-step <strong><a class=\"\" href=\"https://www.plugnmeet.org/docs/tutorials/migration-from-bbb\">Migration from BigBlueButton Tutorial</a></strong> to see how easy it is.</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"conclusion-the-right-tool-for-the-modern-web\">Conclusion: The Right Tool for the Modern Web<a href=\"https://www.plugnmeet.org/blog/bigbluebutton-vs-plugnmeet-alternative#conclusion-the-right-tool-for-the-modern-web\" class=\"hash-link\" aria-label=\"Direct link to Conclusion: The Right Tool for the Modern Web\" title=\"Direct link to Conclusion: The Right Tool for the Modern Web\" translate=\"no\">​</a></h3>\n<p>BigBlueButton laid the foundation for open-source communication. It proved what was possible. As we detailed in our <strong><a class=\"\" href=\"https://www.plugnmeet.org/blog/why-we-built-plugnmeet\">founder's story, \"Why We Built Plug-N-Meet,\"</a></strong> our platform is the next step in that evolution, built from the ground up to meet the modern web's demands for elastic scalability, developer agility, and long-term maintainability.</p>\n<p>If you are feeling the pain points of a monolithic architecture and are looking for a more flexible, performant, and cost-effective solution, Plug-N-Meet is the modern alternative you've been waiting for.</p>\n<hr>\n<p><strong>Ready to see the difference for yourself?</strong></p>\n<ul>\n<li class=\"\"><strong><a href=\"https://demo.plugnmeet.com/landing.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Try the Live Demo</a> to experience the modern interface.</strong></li>\n<li class=\"\"><strong><a class=\"\" href=\"https://www.plugnmeet.org/docs/installation\">Follow our simple Installation Guide</a> to get your own server running in minutes.</strong></li>\n</ul>",
            "url": "https://www.plugnmeet.org/blog/bigbluebutton-vs-plugnmeet-alternative",
            "title": "BigBlueButton vs. Plug-N-Meet: A Modern Alternative for Scalable Video Conferencing",
            "summary": "If you're in the world of open-source education or communication, you owe a debt of gratitude to BigBlueButton. It was a pioneering platform that showed the world what was possible. However, as the web has evolved, the demands for scalability, developer experience, and cost-effective performance have grown exponentially.",
            "date_modified": "2026-01-18T00:00:00.000Z",
            "author": {
                "name": "Chaboud Simon",
                "url": "https://github.com/saimonzone"
            },
            "tags": [
                "bigbluebutton",
                "bbb-alternative",
                "plugnmeet-vs-bbb",
                "open-source",
                "video-conferencing",
                "scalability",
                "comparison"
            ]
        },
        {
            "id": "https://www.plugnmeet.org/blog/how-to-build-your-first-webhook-workflow",
            "content_html": "<p>You've set up your video conferencing service, but now you want to make it smarter. What if you could automatically log the duration of every meeting for billing? Or get a Slack notification when a VIP joins a call? This is where webhooks come in.</p>\n<p>This guide will walk you through building your very first automated workflow. We'll create a simple but powerful service that listens for a meeting to end and logs its duration to your console—a perfect starting point for a real-world billing or analytics system.</p>\n<p>For more high-level ideas on what's possible, check out our companion post on <a class=\"\" href=\"https://www.plugnmeet.org/blog/powerful-workflows-with-webhooks\">5 Powerful Workflows You Can Build with Webhooks</a>.</p>\n<hr>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"the-goal\">The Goal<a href=\"https://www.plugnmeet.org/blog/how-to-build-your-first-webhook-workflow#the-goal\" class=\"hash-link\" aria-label=\"Direct link to The Goal\" title=\"Direct link to The Goal\" translate=\"no\">​</a></h2>\n<p>By the end of this tutorial, you will have a simple webhook listener that can:</p>\n<ol>\n<li class=\"\">Securely validate and receive events from your Plug-N-Meet server.</li>\n<li class=\"\">Calculate the duration of a meeting when it ends.</li>\n<li class=\"\">Differentiate between different types of AI-generated artifacts.</li>\n</ol>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"prerequisites\">Prerequisites<a href=\"https://www.plugnmeet.org/blog/how-to-build-your-first-webhook-workflow#prerequisites\" class=\"hash-link\" aria-label=\"Direct link to Prerequisites\" title=\"Direct link to Prerequisites\" translate=\"no\">​</a></h2>\n<ul>\n<li class=\"\">A running Plug-N-Meet server with your <code>API_KEY</code> and <code>API_SECRET</code>.</li>\n<li class=\"\">An application endpoint (URL) ready to receive <code>POST</code> requests.</li>\n<li class=\"\">Node.js and a basic understanding of Express.</li>\n</ul>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"step-1-setting-up-your-endpoint\">Step 1: Setting Up Your Endpoint<a href=\"https://www.plugnmeet.org/blog/how-to-build-your-first-webhook-workflow#step-1-setting-up-your-endpoint\" class=\"hash-link\" aria-label=\"Direct link to Step 1: Setting Up Your Endpoint\" title=\"Direct link to Step 1: Setting Up Your Endpoint\" translate=\"no\">​</a></h3>\n<p>First, tell Plug-N-Meet where to send the events. Open your <code>config.yaml</code> file on your server and add the <code>webhook_conf</code> section.</p>\n<div class=\"language-yaml codeBlockContainer_ibEU theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_osW6\"><pre tabindex=\"0\" class=\"prism-code language-yaml codeBlock_lRqf thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_jDlT\"><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token key atrule\" style=\"color:#00a4db\">webhook_conf</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\"># Set to true to enable webhooks globally.</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token key atrule\" style=\"color:#00a4db\">enable</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token boolean important\" style=\"color:#36acaa\">true</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\"># The primary URL where all events will be sent.</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token key atrule\" style=\"color:#00a4db\">url</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"https://yourapp.com/webhook-listener\"</span><br></div></code></pre></div></div>\n<p>Save the file and <strong>restart your PlugNmeet server</strong>. Now, all events will be sent to your specified URL.</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"step-2-secure-your-endpoint-with-signature-validation\">Step 2: Secure Your Endpoint with Signature Validation<a href=\"https://www.plugnmeet.org/blog/how-to-build-your-first-webhook-workflow#step-2-secure-your-endpoint-with-signature-validation\" class=\"hash-link\" aria-label=\"Direct link to Step 2: Secure Your Endpoint with Signature Validation\" title=\"Direct link to Step 2: Secure Your Endpoint with Signature Validation\" translate=\"no\">​</a></h3>\n<p>Before processing any data, you <strong>must</strong> verify that the webhook request is genuinely from your Plug-N-Meet server. An unprotected endpoint can be exploited by anyone on the internet.</p>\n<p>Plug-N-Meet uses a JWT-based signature in the <code>Authorization</code> header to secure webhooks. The process is:</p>\n<ol>\n<li class=\"\">Verify the JWT token using your <code>API_SECRET</code>.</li>\n<li class=\"\">Calculate a SHA256 hash of the raw request body.</li>\n<li class=\"\">Compare your calculated hash with the <code>sha256</code> claim inside the verified token.</li>\n</ol>\n<p>Here’s how to implement this in a Node.js/Express middleware.</p>\n<div class=\"language-javascript codeBlockContainer_ibEU theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_osW6\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_lRqf thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_jDlT\"><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// You'll need these packages: npm install express body-parser jsonwebtoken crypto</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:#00009f\">import</span><span class=\"token plain\"> </span><span class=\"token imports\">express</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:#00009f\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'express'</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:#00009f\">import</span><span class=\"token plain\"> </span><span class=\"token imports\">bodyParser</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:#00009f\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'body-parser'</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:#00009f\">import</span><span class=\"token plain\"> </span><span class=\"token imports\">jwt</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:#00009f\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'jsonwebtoken'</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:#00009f\">import</span><span class=\"token plain\"> </span><span class=\"token imports\">crypto</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:#00009f\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'crypto'</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> app </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">express</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> </span><span class=\"token constant\" style=\"color:#36acaa\">API_SECRET</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'YOUR_PLUGNMEET_API_SECRET'</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"> </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// Use environment variables in production!</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// We need the raw body for hash comparison, so we use a custom verify function.</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">use</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">bodyParser</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">json</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token literal-property property\" style=\"color:#36acaa\">type</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'application/webhook+json'</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token function-variable function\" style=\"color:#d73a49\">verify</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:#393A34\">,</span><span class=\"token parameter\"> res</span><span class=\"token parameter punctuation\" style=\"color:#393A34\">,</span><span class=\"token parameter\"> buf</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:#393A34\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    req</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">rawBody</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> buf</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">toString</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// The validation middleware</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">function</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">validateRequest</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:#393A34\">,</span><span class=\"token parameter\"> res</span><span class=\"token parameter punctuation\" style=\"color:#393A34\">,</span><span class=\"token parameter\"> next</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> token </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">header</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token string\" style=\"color:#e3116c\">'Authorization'</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:#00009f\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token operator\" style=\"color:#393A34\">!</span><span class=\"token plain\">token</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token keyword control-flow\" style=\"color:#00009f\">return</span><span class=\"token plain\"> res</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">status</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token number\" style=\"color:#36acaa\">401</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">send</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token string\" style=\"color:#e3116c\">'Authorization header missing'</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:#00009f\">try</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// 1. Verify the JWT</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> decoded </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> jwt</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">verify</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">token</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> </span><span class=\"token constant\" style=\"color:#36acaa\">API_SECRET</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// 2. Hash the raw request body</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> sha256 </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> crypto</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">createHash</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token string\" style=\"color:#e3116c\">'sha256'</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    sha256</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">update</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">req</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">rawBody</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> hash </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> sha256</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">digest</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token string\" style=\"color:#e3116c\">'hex'</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// 3. Compare hashes</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token keyword control-flow\" style=\"color:#00009f\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">decoded</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">sha256</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">!==</span><span class=\"token plain\"> hash</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token keyword control-flow\" style=\"color:#00009f\">throw</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">new</span><span class=\"token plain\"> </span><span class=\"token class-name\">Error</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token string\" style=\"color:#e3116c\">'Signature hash does not match'</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// Success! Proceed to the main handler.</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token function\" style=\"color:#d73a49\">next</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:#00009f\">catch</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">err</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token console class-name\">console</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">error</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token string\" style=\"color:#e3116c\">'Webhook validation failed:'</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> err</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">message</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token keyword control-flow\" style=\"color:#00009f\">return</span><span class=\"token plain\"> res</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">status</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token number\" style=\"color:#36acaa\">403</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">send</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token string\" style=\"color:#e3116c\">'Invalid signature'</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// Apply the middleware to your listener route.</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">post</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token string\" style=\"color:#e3116c\">'/webhook-listener'</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> validateRequest</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:#393A34\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:#393A34\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token console class-name\">console</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">log</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token string\" style=\"color:#e3116c\">'Webhook received and validated!'</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token function\" style=\"color:#d73a49\">handleWebhook</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">req</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  res</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">status</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token number\" style=\"color:#36acaa\">200</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">send</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token string\" style=\"color:#e3116c\">'OK'</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">listen</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token number\" style=\"color:#36acaa\">3000</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:#393A34\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token console class-name\">console</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">log</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token string\" style=\"color:#e3116c\">'Webhook listener started on port 3000'</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// We'll define handleWebhook in the next step.</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">function</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">handleWebhook</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token parameter\">payload</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// ...</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><br></div></code></pre></div></div>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"step-3-handle-different-events\">Step 3: Handle Different Events<a href=\"https://www.plugnmeet.org/blog/how-to-build-your-first-webhook-workflow#step-3-handle-different-events\" class=\"hash-link\" aria-label=\"Direct link to Step 3: Handle Different Events\" title=\"Direct link to Step 3: Handle Different Events\" translate=\"no\">​</a></h3>\n<p>Now that our endpoint is secure, we can safely process the incoming data. We'll create our <code>handleWebhook</code> function to inspect the <code>event</code> property and act accordingly.</p>\n<h4 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"use-case-1-basic-billing--analytics\">Use Case 1: Basic Billing &amp; Analytics<a href=\"https://www.plugnmeet.org/blog/how-to-build-your-first-webhook-workflow#use-case-1-basic-billing--analytics\" class=\"hash-link\" aria-label=\"Direct link to Use Case 1: Basic Billing &amp; Analytics\" title=\"Direct link to Use Case 1: Basic Billing &amp; Analytics\" translate=\"no\">​</a></h4>\n<p>The <code>session_ended</code> event is perfect for this. It fires when the last participant leaves and includes <code>creation_time</code> and <code>ended_time</code> timestamps.</p>\n<div class=\"language-javascript codeBlockContainer_ibEU theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_osW6\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_lRqf thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_jDlT\"><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token keyword\" style=\"color:#00009f\">function</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">handleWebhook</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token parameter\">payload</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:#00009f\">switch</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">payload</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">event</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:#00009f\">case</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'session_ended'</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> room </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> payload</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">room</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> durationInSeconds </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> room</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">ended_time</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">-</span><span class=\"token plain\"> room</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">creation_time</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> durationInMinutes </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token known-class-name class-name\">Math</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">ceil</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">durationInSeconds </span><span class=\"token operator\" style=\"color:#393A34\">/</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">60</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token console class-name\">console</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">log</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token template-string template-punctuation string\" style=\"color:#e3116c\">`</span><span class=\"token template-string string\" style=\"color:#e3116c\">SUCCESS: Room '</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:#393A34\">${</span><span class=\"token template-string interpolation\">room</span><span class=\"token template-string interpolation punctuation\" style=\"color:#393A34\">.</span><span class=\"token template-string interpolation property-access\">room_id</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:#393A34\">}</span><span class=\"token template-string string\" style=\"color:#e3116c\">' lasted for </span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:#393A34\">${</span><span class=\"token template-string interpolation\">durationInMinutes</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:#393A34\">}</span><span class=\"token template-string string\" style=\"color:#e3116c\"> minutes.</span><span class=\"token template-string template-punctuation string\" style=\"color:#e3116c\">`</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// In a real application, you would save this to your database.</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token keyword control-flow\" style=\"color:#00009f\">break</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:#00009f\">case</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'artifact_created'</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token function\" style=\"color:#d73a49\">handleArtifact</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">payload</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">room_artifact</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token keyword control-flow\" style=\"color:#00009f\">break</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><br></div></code></pre></div></div>\n<h4 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"use-case-2-advanced-ai-cost--usage-tracking\">Use Case 2: Advanced AI Cost &amp; Usage Tracking<a href=\"https://www.plugnmeet.org/blog/how-to-build-your-first-webhook-workflow#use-case-2-advanced-ai-cost--usage-tracking\" class=\"hash-link\" aria-label=\"Direct link to Use Case 2: Advanced AI Cost &amp; Usage Tracking\" title=\"Direct link to Use Case 2: Advanced AI Cost &amp; Usage Tracking\" translate=\"no\">​</a></h4>\n<p>The <code>artifact_created</code> event fires whenever an AI service generates a result. The key is to inspect the <code>room_artifact.type</code> and the nested <code>metadata</code> object.</p>\n<div class=\"language-javascript codeBlockContainer_ibEU theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_osW6\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_lRqf thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_jDlT\"><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// A mapping from the enum number to a human-readable string.</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">ArtifactType</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token number\" style=\"color:#36acaa\">2</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'MEETING_SUMMARY'</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token number\" style=\"color:#36acaa\">5</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'SPEECH_TRANSCRIPTION_USAGE'</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token number\" style=\"color:#36acaa\">8</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'AI_TEXT_CHAT_INTERACTION_USAGE'</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token number\" style=\"color:#36acaa\">7</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'CHAT_TRANSLATION_USAGE'</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:#00009f\">function</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:#d73a49\">handleArtifact</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token parameter\">artifact</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> artifactType </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">ArtifactType</span><span class=\"token punctuation\" style=\"color:#393A34\">[</span><span class=\"token plain\">artifact</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">type</span><span class=\"token punctuation\" style=\"color:#393A34\">]</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">||</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'UNKNOWN'</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token console class-name\">console</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">log</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token template-string template-punctuation string\" style=\"color:#e3116c\">`</span><span class=\"token template-string string\" style=\"color:#e3116c\">Processing artifact of type: </span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:#393A34\">${</span><span class=\"token template-string interpolation\">artifactType</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:#393A34\">}</span><span class=\"token template-string template-punctuation string\" style=\"color:#e3116c\">`</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:#00009f\">switch</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">artifactType</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:#00009f\">case</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'MEETING_SUMMARY'</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token keyword control-flow\" style=\"color:#00009f\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">artifact</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">metadata</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">&amp;&amp;</span><span class=\"token plain\"> artifact</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">metadata</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">file_info</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">        </span><span class=\"token console class-name\">console</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">log</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token template-string template-punctuation string\" style=\"color:#e3116c\">`</span><span class=\"token template-string string\" style=\"color:#e3116c\">New summary available at: </span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:#393A34\">${</span><span class=\"token template-string interpolation\">artifact</span><span class=\"token template-string interpolation punctuation\" style=\"color:#393A34\">.</span><span class=\"token template-string interpolation property-access\">metadata</span><span class=\"token template-string interpolation punctuation\" style=\"color:#393A34\">.</span><span class=\"token template-string interpolation property-access\">file_info</span><span class=\"token template-string interpolation punctuation\" style=\"color:#393A34\">.</span><span class=\"token template-string interpolation property-access\">file_path</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:#393A34\">}</span><span class=\"token template-string template-punctuation string\" style=\"color:#e3116c\">`</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token keyword control-flow\" style=\"color:#00009f\">break</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:#00009f\">case</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'SPEECH_TRANSCRIPTION_USAGE'</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token keyword control-flow\" style=\"color:#00009f\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">artifact</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">metadata</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">&amp;&amp;</span><span class=\"token plain\"> artifact</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">metadata</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">duration_usage</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">        </span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> usage </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> artifact</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">metadata</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">duration_usage</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">        </span><span class=\"token console class-name\">console</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">log</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token template-string template-punctuation string\" style=\"color:#e3116c\">`</span><span class=\"token template-string string\" style=\"color:#e3116c\">Transcription usage: </span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:#393A34\">${</span><span class=\"token template-string interpolation\">usage</span><span class=\"token template-string interpolation punctuation\" style=\"color:#393A34\">.</span><span class=\"token template-string interpolation property-access\">duration_sec</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:#393A34\">}</span><span class=\"token template-string string\" style=\"color:#e3116c\"> seconds.</span><span class=\"token template-string template-punctuation string\" style=\"color:#e3116c\">`</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">        </span><span class=\"token console class-name\">console</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">log</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token template-string template-punctuation string\" style=\"color:#e3116c\">`</span><span class=\"token template-string string\" style=\"color:#e3116c\">Estimated Cost: $</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:#393A34\">${</span><span class=\"token template-string interpolation\">usage</span><span class=\"token template-string interpolation punctuation\" style=\"color:#393A34\">.</span><span class=\"token template-string interpolation property-access\">duration_sec_estimated_cost</span><span class=\"token template-string interpolation punctuation\" style=\"color:#393A34\">.</span><span class=\"token template-string interpolation method function property-access\" style=\"color:#d73a49\">toFixed</span><span class=\"token template-string interpolation punctuation\" style=\"color:#393A34\">(</span><span class=\"token template-string interpolation number\" style=\"color:#36acaa\">4</span><span class=\"token template-string interpolation punctuation\" style=\"color:#393A34\">)</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:#393A34\">}</span><span class=\"token template-string template-punctuation string\" style=\"color:#e3116c\">`</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token keyword control-flow\" style=\"color:#00009f\">break</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:#00009f\">case</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">'AI_TEXT_CHAT_INTERACTION_USAGE'</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token keyword control-flow\" style=\"color:#00009f\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">artifact</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">metadata</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">&amp;&amp;</span><span class=\"token plain\"> artifact</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">metadata</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">token_usage</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">        </span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> usage </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> artifact</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">metadata</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token property-access\">token_usage</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">        </span><span class=\"token console class-name\">console</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">log</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token template-string template-punctuation string\" style=\"color:#e3116c\">`</span><span class=\"token template-string string\" style=\"color:#e3116c\">AI Chat usage: </span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:#393A34\">${</span><span class=\"token template-string interpolation\">usage</span><span class=\"token template-string interpolation punctuation\" style=\"color:#393A34\">.</span><span class=\"token template-string interpolation property-access\">total_tokens</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:#393A34\">}</span><span class=\"token template-string string\" style=\"color:#e3116c\"> tokens.</span><span class=\"token template-string template-punctuation string\" style=\"color:#e3116c\">`</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">        </span><span class=\"token console class-name\">console</span><span class=\"token punctuation\" style=\"color:#393A34\">.</span><span class=\"token method function property-access\" style=\"color:#d73a49\">log</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token template-string template-punctuation string\" style=\"color:#e3116c\">`</span><span class=\"token template-string string\" style=\"color:#e3116c\">Estimated Cost: $</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:#393A34\">${</span><span class=\"token template-string interpolation\">usage</span><span class=\"token template-string interpolation punctuation\" style=\"color:#393A34\">.</span><span class=\"token template-string interpolation property-access\">total_tokens_estimated_cost</span><span class=\"token template-string interpolation punctuation\" style=\"color:#393A34\">.</span><span class=\"token template-string interpolation method function property-access\" style=\"color:#d73a49\">toFixed</span><span class=\"token template-string interpolation punctuation\" style=\"color:#393A34\">(</span><span class=\"token template-string interpolation number\" style=\"color:#36acaa\">4</span><span class=\"token template-string interpolation punctuation\" style=\"color:#393A34\">)</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:#393A34\">}</span><span class=\"token template-string template-punctuation string\" style=\"color:#e3116c\">`</span><span class=\"token punctuation\" style=\"color:#393A34\">)</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token keyword control-flow\" style=\"color:#00009f\">break</span><span class=\"token punctuation\" style=\"color:#393A34\">;</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><br></div></code></pre></div></div>\n<hr>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"conclusion\">Conclusion<a href=\"https://www.plugnmeet.org/blog/how-to-build-your-first-webhook-workflow#conclusion\" class=\"hash-link\" aria-label=\"Direct link to Conclusion\" title=\"Direct link to Conclusion\" translate=\"no\">​</a></h2>\n<p>That's it! You've just built a <strong>secure</strong> webhook handler that can intelligently process different types of events. Security is not optional when handling webhooks, and by validating the signature on every request, you ensure your application is safe from bad actors.</p>\n<p>From here, you can easily extend this logic to build a sophisticated, real-time billing and analytics system, knowing your endpoint is properly protected.</p>\n<hr>\n<p><strong>Ready to start automating?</strong></p>\n<ul>\n<li class=\"\"><strong>Explore the complete <a class=\"\" href=\"https://www.plugnmeet.org/docs/others/webhooks\">Webhooks Documentation</a></strong></li>\n<li class=\"\"><strong>Review the full <a class=\"\" href=\"https://www.plugnmeet.org/docs/api/intro\">API Introduction</a></strong></li>\n<li class=\"\"><strong>Get more ideas from our post on <a class=\"\" href=\"https://www.plugnmeet.org/blog/powerful-workflows-with-webhooks\">5 Powerful Workflows</a></strong></li>\n</ul>",
            "url": "https://www.plugnmeet.org/blog/how-to-build-your-first-webhook-workflow",
            "title": "A Developer's Guide: How to Build Your First Automated Workflow with Webhooks",
            "summary": "You've set up your video conferencing service, but now you want to make it smarter. What if you could automatically log the duration of every meeting for billing? Or get a Slack notification when a VIP joins a call? This is where webhooks come in.",
            "date_modified": "2026-01-17T00:00:00.000Z",
            "author": {
                "name": "Bob Teng",
                "url": "https://github.com/wbobteng"
            },
            "tags": [
                "tutorial",
                "how-to",
                "webhooks",
                "automation",
                "developer",
                "api",
                "integration",
                "security"
            ]
        },
        {
            "id": "https://www.plugnmeet.org/blog/how-to-build-frictionless-browser-based-video-platform",
            "content_html": "<p>You sent a meeting link. Five minutes later, your most important client messages you: <em>\"It's asking me to install something.\"</em> The momentum is lost.</p>\n<p>This is a scenario every business, educator, and community manager dreads. In today's fast-paced world, friction is the enemy of engagement. The alternative? A world where users click a link and are instantly in the meeting, ready to collaborate.</p>\n<p>This is the promise of a truly browser-based platform, and this guide shows you how to deliver that experience with Plug-N-Meet—without compromising on features.</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"the-no-installation-philosophy\">The \"No Installation\" Philosophy<a href=\"https://www.plugnmeet.org/blog/how-to-build-frictionless-browser-based-video-platform#the-no-installation-philosophy\" class=\"hash-link\" aria-label=\"Direct link to The &quot;No Installation&quot; Philosophy\" title=\"Direct link to The &quot;No Installation&quot; Philosophy\" translate=\"no\">​</a></h3>\n<p>Before we dive into the \"how,\" let's understand the \"why.\" A browser-first approach is a strategic choice that prioritizes:</p>\n<ul>\n<li class=\"\"><strong>Accessibility:</strong> Works for anyone on any modern device (desktop or mobile) without barriers.</li>\n<li class=\"\"><strong>Security:</strong> Users aren't asked to trust and run unknown executables on their systems.</li>\n<li class=\"\"><strong>Speed:</strong> The time from click-to-conversation is measured in seconds, not minutes.</li>\n</ul>\n<p>Here’s how you can leverage Plug-N-Meet's browser-native architecture to build a complete video service that rivals desktop applications.</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"step-1-deliver-instant-access-with-a-full-feature-set\">Step 1: Deliver Instant Access with a Full Feature Set<a href=\"https://www.plugnmeet.org/blog/how-to-build-frictionless-browser-based-video-platform#step-1-deliver-instant-access-with-a-full-feature-set\" class=\"hash-link\" aria-label=\"Direct link to Step 1: Deliver Instant Access with a Full Feature Set\" title=\"Direct link to Step 1: Deliver Instant Access with a Full Feature Set\" translate=\"no\">​</a></h3>\n<p>Your first step is to provide a core experience that feels complete. A browser doesn't mean \"basic.\" With a simple <code>createRoom</code> API call, you can enable a rich, desktop-class feature set that runs entirely in the browser.</p>\n<p><strong>What to Enable in Your API Call:</strong>\nIn your <code>createRoom</code> metadata, ensure these features are set to <code>true</code>:</p>\n<ul>\n<li class=\"\"><strong>HD Video &amp; Screensharing:</strong> Enable <code>allow_webcams</code> and <code>allow_screen_share</code>. Modern browsers handle this natively and securely.</li>\n<li class=\"\"><strong>Rich Collaboration Tools:</strong> Set <code>whiteboard_features.is_allow</code>, <code>shared_note_pad_features.is_allow</code>, and <code>chat_features.is_allow</code> to <code>true</code>. These tools are built-in and require no plugins.</li>\n<li class=\"\"><strong>Engagement &amp; Moderation:</strong> Enable <code>allow_raise_hand</code> and <code>polls_features.is_allow</code> to create an interactive environment identical to what users expect from desktop apps.</li>\n</ul>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"step-2-add-intelligent-cloud-powered-features\">Step 2: Add Intelligent, Cloud-Powered Features<a href=\"https://www.plugnmeet.org/blog/how-to-build-frictionless-browser-based-video-platform#step-2-add-intelligent-cloud-powered-features\" class=\"hash-link\" aria-label=\"Direct link to Step 2: Add Intelligent, Cloud-Powered Features\" title=\"Direct link to Step 2: Add Intelligent, Cloud-Powered Features\" translate=\"no\">​</a></h3>\n<p>The most advanced features today aren't limited by a user's device; they are powered by the cloud. This is a natural fit for a browser-based platform.</p>\n<p><strong>How to Configure It:</strong></p>\n<ol>\n<li class=\"\"><strong>Add API Keys:</strong> In your <code>config.yaml</code>, add your API keys for Azure and Google, as shown in our <a class=\"\" href=\"https://www.plugnmeet.org/blog/how-to-add-ai-meeting-assistant-features\">AI Features Guide</a>.</li>\n<li class=\"\"><strong>Enable in the Room:</strong> In your <code>createRoom</code> call, enable the <code>insights_features</code> block.</li>\n</ol>\n<p><strong>What You Deliver to the User (in the browser):</strong></p>\n<ul>\n<li class=\"\"><strong>Live Captions &amp; Translation:</strong> A user can click the \"T\" icon to see live captions or translate the conversation into their native language.</li>\n<li class=\"\"><strong>AI Meeting Summaries:</strong> A moderator can start the <code>meeting_summarizing</code> service to generate AI meeting notes, which are then available via the API.</li>\n</ul>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"step-3-leverage-unique-web-native-experiences\">Step 3: Leverage Unique, Web-Native Experiences<a href=\"https://www.plugnmeet.org/blog/how-to-build-frictionless-browser-based-video-platform#step-3-leverage-unique-web-native-experiences\" class=\"hash-link\" aria-label=\"Direct link to Step 3: Leverage Unique, Web-Native Experiences\" title=\"Direct link to Step 3: Leverage Unique, Web-Native Experiences\" translate=\"no\">​</a></h3>\n<p>Some features aren't just <em>possible</em> in a browser; they are <em>better</em>. The <strong>Embedded Web Content</strong> feature is a perfect example.</p>\n<p><strong>How to Enable It:</strong>\nIn your <code>createRoom</code> API call, simply ensure <code>display_external_link_features.is_allow</code> is set to <code>true</code>.</p>\n<p><strong>What This Unlocks:</strong>\nA moderator can now share any website directly within the meeting window. This is far more powerful than simple screen sharing. You can:</p>\n<ul>\n<li class=\"\">Collaborate on a live Google Doc or Miro board.</li>\n<li class=\"\">Review a project dashboard from Jira or Trello together.</li>\n<li class=\"\">Walk a client through a live website or web application.</li>\n</ul>\n<p>This is a uniquely powerful feature that a sandboxed desktop application often can't replicate.</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"step-4-ensure-reliability-with-adaptive-streaming\">Step 4: Ensure Reliability with Adaptive Streaming<a href=\"https://www.plugnmeet.org/blog/how-to-build-frictionless-browser-based-video-platform#step-4-ensure-reliability-with-adaptive-streaming\" class=\"hash-link\" aria-label=\"Direct link to Step 4: Ensure Reliability with Adaptive Streaming\" title=\"Direct link to Step 4: Ensure Reliability with Adaptive Streaming\" translate=\"no\">​</a></h3>\n<p>A common concern with web apps is performance on poor networks. Plug-N-Meet solves this in the browser using modern WebRTC standards.</p>\n<p><strong>How to Enable It:</strong>\nThis is on by default! <code>Simulcast</code> and <code>Dynacast</code> are core features of the underlying media server.</p>\n<p><strong>What This Means for Your Users:</strong></p>\n<ul>\n<li class=\"\">The platform automatically adjusts video quality to match each user's bandwidth.</li>\n<li class=\"\">Video from off-screen participants is paused to save CPU and data.</li>\n<li class=\"\">The result is a stable, smooth experience that prevents lag and buffering, even on weak Wi-Fi or mobile connections—all without a single line of extra code from you.</li>\n</ul>\n<hr>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"conclusion\">Conclusion<a href=\"https://www.plugnmeet.org/blog/how-to-build-frictionless-browser-based-video-platform#conclusion\" class=\"hash-link\" aria-label=\"Direct link to Conclusion\" title=\"Direct link to Conclusion\" translate=\"no\">​</a></h2>\n<p>You don't need to force your users to download, install, and update software to provide a professional video conferencing experience. By embracing a browser-first architecture, you can deliver a service that is faster, more accessible, and packed with powerful, modern features.</p>\n<p>With Plug-N-Meet, you can build a platform that \"just works,\" letting your users focus on the conversation, not the installation bar.</p>\n<hr>\n<p><strong>Ready to build your frictionless platform?</strong></p>\n<ul>\n<li class=\"\"><strong>Try the features in our <a href=\"https://demo.plugnmeet.com/landing.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Live Demo</a></strong></li>\n<li class=\"\"><strong>Review the full feature list in our <a class=\"\" href=\"https://www.plugnmeet.org/docs/intro\">Introduction</a></strong></li>\n<li class=\"\"><strong>Get started with the <a class=\"\" href=\"https://www.plugnmeet.org/docs/installation\">Installation Guide</a></strong></li>\n</ul>",
            "url": "https://www.plugnmeet.org/blog/how-to-build-frictionless-browser-based-video-platform",
            "title": "From Link to Live in 5 Seconds: How to Build a Frictionless Video Platform",
            "summary": "You sent a meeting link. Five minutes later, your most important client messages you: \"It's asking me to install something.\" The momentum is lost.",
            "date_modified": "2026-01-10T00:00:00.000Z",
            "author": {
                "name": "Chaboud Simon",
                "url": "https://github.com/saimonzone"
            },
            "tags": [
                "tutorial",
                "how-to",
                "browser-based",
                "no-installation",
                "webrtc",
                "user-experience"
            ]
        },
        {
            "id": "https://www.plugnmeet.org/blog/how-to-enable-end-to-end-encryption",
            "content_html": "<p>In an age of heightened privacy concerns, offering End-to-End Encryption (E2EE) is one of the most powerful ways to build trust with your users. It provides a mathematical guarantee that conversations are confidential and that not even your server can access the media streams.</p>\n<p>While the technology is complex, implementing it doesn't have to be. This guide will walk you through the two E2EE models available in Plug-N-Meet and show you how to enable them with a simple API call.</p>\n<hr>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"prerequisites\">Prerequisites<a href=\"https://www.plugnmeet.org/blog/how-to-enable-end-to-end-encryption#prerequisites\" class=\"hash-link\" aria-label=\"Direct link to Prerequisites\" title=\"Direct link to Prerequisites\" translate=\"no\">​</a></h2>\n<ul>\n<li class=\"\">A running Plug-N-Meet server.</li>\n<li class=\"\">The ability to make API calls to your server.</li>\n</ul>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"step-1-choose-your-security-model\">Step 1: Choose Your Security Model<a href=\"https://www.plugnmeet.org/blog/how-to-enable-end-to-end-encryption#step-1-choose-your-security-model\" class=\"hash-link\" aria-label=\"Direct link to Step 1: Choose Your Security Model\" title=\"Direct link to Step 1: Choose Your Security Model\" translate=\"no\">​</a></h3>\n<p>Before writing any code, you need to answer one question: <strong>Who should manage the encryption keys?</strong> Plug-N-Meet offers two distinct models, and your choice will determine the implementation.</p>\n<ul>\n<li class=\"\"><strong>Model A: Server-Generated (Simple &amp; Secure):</strong> The Plug-N-Meet server generates, manages, and distributes a unique secret for each session. This is the easiest and most convenient method.</li>\n<li class=\"\"><strong>Model B: User-Provided (Zero-Knowledge):</strong> The users are responsible for creating and sharing a secret among themselves. Your server never sees the secret, providing the absolute highest level of privacy.</li>\n</ul>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"step-2-implement-your-chosen-model\">Step 2: Implement Your Chosen Model<a href=\"https://www.plugnmeet.org/blog/how-to-enable-end-to-end-encryption#step-2-implement-your-chosen-model\" class=\"hash-link\" aria-label=\"Direct link to Step 2: Implement Your Chosen Model\" title=\"Direct link to Step 2: Implement Your Chosen Model\" translate=\"no\">​</a></h3>\n<p>Enabling E2EE is done within the <code>end_to_end_encryption_features</code> block of your <code>createRoom</code> API call.</p>\n<h4 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"the-underlying-security-one-time-session-keys\">The Underlying Security: One-Time Session Keys<a href=\"https://www.plugnmeet.org/blog/how-to-enable-end-to-end-encryption#the-underlying-security-one-time-session-keys\" class=\"hash-link\" aria-label=\"Direct link to The Underlying Security: One-Time Session Keys\" title=\"Direct link to The Underlying Security: One-Time Session Keys\" translate=\"no\">​</a></h4>\n<p>Before we look at the two models, it's important to understand a key security feature that applies to <strong>both</strong>: every session gets its own unique encryption key.</p>\n<p>No matter where the initial secret comes from (the server or the user), the client's browser never uses it directly. Instead, it combines the secret with the unique Session ID of the meeting to generate a final, one-time encryption key.</p>\n<p>This process ensures that even if you reuse the same room ID for multiple meetings, every single session is cryptographically isolated, preventing any link between them.</p>\n<h4 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"option-a-the-simple--secure-way-server-generated-keys\">Option A: The Simple &amp; Secure Way (Server-Generated Keys)<a href=\"https://www.plugnmeet.org/blog/how-to-enable-end-to-end-encryption#option-a-the-simple--secure-way-server-generated-keys\" class=\"hash-link\" aria-label=\"Direct link to Option A: The Simple &amp; Secure Way (Server-Generated Keys)\" title=\"Direct link to Option A: The Simple &amp; Secure Way (Server-Generated Keys)\" translate=\"no\">​</a></h4>\n<p>This is the recommended path for most applications. You get strong E2EE without needing to build any custom key-sharing logic.</p>\n<p>In your <code>createRoom</code> metadata, set <code>is_enabled</code> to <code>true</code> and <code>enabled_self_insert_encryption_key</code> to <code>false</code>.</p>\n<div class=\"language-json codeBlockContainer_ibEU theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_osW6\"><pre tabindex=\"0\" class=\"prism-code language-json codeBlock_lRqf thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_jDlT\"><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token property\" style=\"color:#36acaa\">\"room_id\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"e2ee-room-simple\"</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token property\" style=\"color:#36acaa\">\"metadata\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token property\" style=\"color:#36acaa\">\"room_features\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// ... other features</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token property\" style=\"color:#36acaa\">\"end_to_end_encryption_features\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">        </span><span class=\"token property\" style=\"color:#36acaa\">\"is_enabled\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:#36acaa\">true</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">        </span><span class=\"token property\" style=\"color:#36acaa\">\"enabled_self_insert_encryption_key\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:#36acaa\">false</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><br></div></code></pre></div></div>\n<p><strong>How it works:</strong> When a user joins, the server provides a unique secret for the session. The client then uses the one-time key generation process described above to secure the meeting.</p>\n<h4 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"option-b-the-zero-knowledge-way-user-provided-keys\">Option B: The Zero-Knowledge Way (User-Provided Keys)<a href=\"https://www.plugnmeet.org/blog/how-to-enable-end-to-end-encryption#option-b-the-zero-knowledge-way-user-provided-keys\" class=\"hash-link\" aria-label=\"Direct link to Option B: The Zero-Knowledge Way (User-Provided Keys)\" title=\"Direct link to Option B: The Zero-Knowledge Way (User-Provided Keys)\" translate=\"no\">​</a></h4>\n<p>Choose this model if you need to guarantee that your infrastructure has zero knowledge of the encryption keys.</p>\n<p>In your <code>createRoom</code> metadata, set both <code>is_enabled</code> and <code>enabled_self_insert_encryption_key</code> to <code>true</code>.</p>\n<div class=\"language-json codeBlockContainer_ibEU theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_osW6\"><pre tabindex=\"0\" class=\"prism-code language-json codeBlock_lRqf thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_jDlT\"><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token property\" style=\"color:#36acaa\">\"room_id\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"e2ee-room-zero-knowledge\"</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token property\" style=\"color:#36acaa\">\"metadata\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token property\" style=\"color:#36acaa\">\"room_features\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// ... other features</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token property\" style=\"color:#36acaa\">\"end_to_end_encryption_features\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">        </span><span class=\"token property\" style=\"color:#36acaa\">\"is_enabled\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:#36acaa\">true</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">        </span><span class=\"token property\" style=\"color:#36acaa\">\"enabled_self_insert_encryption_key\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:#36acaa\">true</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><br></div></code></pre></div></div>\n<p><strong>The User Experience:</strong>\nWhen a user joins this room, the Plug-N-Meet client will automatically prompt them to enter the shared secret key.</p>\n<ul>\n<li class=\"\"><strong>If they enter the correct key</strong>, their client will use it to generate the final session key (as described above) and they will be able to see, hear, and interact with other participants seamlessly.</li>\n<li class=\"\"><strong>If they enter an incorrect key (or no key)</strong>, they will still join the session, but they will not be able to see or hear anyone, nor will their own media be visible to others. They will see decryption error messages, indicating that they do not have the correct key to participate.</li>\n</ul>\n<p>This behavior is by design, as the server has zero knowledge of the key and therefore cannot validate it upon entry. You must provide your users with an \"out-of-band\" way to share this key (e.g., a secure chat app, a password manager, or verbally).</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"step-3-verify-its-working\">Step 3: Verify It's Working<a href=\"https://www.plugnmeet.org/blog/how-to-enable-end-to-end-encryption#step-3-verify-its-working\" class=\"hash-link\" aria-label=\"Direct link to Step 3: Verify It's Working\" title=\"Direct link to Step 3: Verify It's Working\" translate=\"no\">​</a></h3>\n<p>How do you know E2EE is active? The most obvious sign is that certain server-side features will be <strong>automatically disabled</strong>.</p>\n<p>If you try to use the following features in an E2EE-enabled room, they will fail, which is the expected behavior and proof that your server cannot access the media:</p>\n<ul>\n<li class=\"\"><strong>Cloud Recording:</strong> The server can't record what it can't see.</li>\n<li class=\"\"><strong>RTMP Broadcasting:</strong> Similarly, the server cannot broadcast an encrypted stream.</li>\n<li class=\"\"><strong>Audio-Based AI Features:</strong> The AI Meeting Assistant cannot transcribe or summarize audio it cannot decrypt.</li>\n</ul>\n<p>This is a core part of the security design, ensuring that your choice of privacy is enforced across the entire platform.</p>\n<hr>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"conclusion\">Conclusion<a href=\"https://www.plugnmeet.org/blog/how-to-enable-end-to-end-encryption#conclusion\" class=\"hash-link\" aria-label=\"Direct link to Conclusion\" title=\"Direct link to Conclusion\" translate=\"no\">​</a></h2>\n<p>Implementing End-to-End Encryption is a powerful step, but it's just one part of a broader \"<strong>privacy by design</strong>\" philosophy. Plug-N-Meet was architected to be a secure data relay, not a data store.</p>\n<p>Whether it's using the LiveKit SFU to route encrypted media packets or NATS to broadcast messages, the server's primary job is to pass data between participants. It has no interest in what that data is, and in the case of E2EE, it has no ability to inspect it. This philosophy extends to features like our use of <a class=\"\" href=\"https://www.plugnmeet.org/blog/client-side-storage-privacy-resilience\">client-side storage</a>, where session data lives in your browser, not on our servers.</p>\n<p>By choosing Plug-N-Meet, you're not just getting a feature; you're adopting a platform built on the principle that the most secure user data is the data you never have to touch.</p>\n<hr>\n<p><strong>Ready to learn more?</strong></p>\n<ul>\n<li class=\"\"><strong>Dive deep into our <a class=\"\" href=\"https://www.plugnmeet.org/docs/security-overview\">Security &amp; Privacy Overview</a></strong></li>\n<li class=\"\"><strong>Review the <a class=\"\" href=\"https://www.plugnmeet.org/blog/e2ee-key-models-guide\">E2EE Key Models Guide</a></strong></li>\n<li class=\"\"><strong>Explore the full <a class=\"\" href=\"https://www.plugnmeet.org/docs/api/room/create\">createRoom API call</a></strong></li>\n</ul>",
            "url": "https://www.plugnmeet.org/blog/how-to-enable-end-to-end-encryption",
            "title": "How to Enable End-to-End Encryption in Your Video App",
            "summary": "In an age of heightened privacy concerns, offering End-to-End Encryption (E2EE) is one of the most powerful ways to build trust with your users. It provides a mathematical guarantee that conversations are confidential and that not even your server can access the media streams.",
            "date_modified": "2026-01-05T00:00:00.000Z",
            "author": {
                "name": "Bob Teng",
                "url": "https://github.com/wbobteng"
            },
            "tags": [
                "tutorial",
                "how-to",
                "e2ee",
                "security",
                "encryption",
                "privacy",
                "developer"
            ]
        },
        {
            "id": "https://www.plugnmeet.org/blog/how-to-add-ai-meeting-assistant-features",
            "content_html": "<p>In today's competitive landscape, a basic video call is no longer enough. Users expect intelligent, accessible, and productive experiences. What if you could offer an <strong>AI meeting assistant</strong> that provides live translation, generates <strong>AI meeting notes</strong>, and can even answer questions directly in the chat?</p>\n<p>With Plug-N-Meet, you can. This isn't a complex, multi-month integration project. It's a 15-minute configuration change.</p>\n<p>This guide will show you how to add world-class <strong>AI meeting assistant features</strong>—powered by <strong>Microsoft Azure</strong> for translation and <strong>Google Gemini</strong> for your <strong>meeting summary AI</strong> and live chat assistant—to your Plug-N-Meet integration.</p>\n<hr>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"the-goal\">The Goal<a href=\"https://www.plugnmeet.org/blog/how-to-add-ai-meeting-assistant-features#the-goal\" class=\"hash-link\" aria-label=\"Direct link to The Goal\" title=\"Direct link to The Goal\" translate=\"no\">​</a></h2>\n<p>By the end of this guide, your <strong>meeting assistant AI</strong> will be able to:</p>\n<ol>\n<li class=\"\">Provide live, real-time captions and translation.</li>\n<li class=\"\">Answer questions and provide help through a dedicated, private chat channel.</li>\n<li class=\"\">Generate a full <strong>meeting summary AI</strong> with key decisions and action items after the session ends.</li>\n</ol>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"prerequisites\">Prerequisites<a href=\"https://www.plugnmeet.org/blog/how-to-add-ai-meeting-assistant-features#prerequisites\" class=\"hash-link\" aria-label=\"Direct link to Prerequisites\" title=\"Direct link to Prerequisites\" translate=\"no\">​</a></h2>\n<ul>\n<li class=\"\">A running Plug-N-Meet server. If you don't have one, follow our <a class=\"\" href=\"https://www.plugnmeet.org/docs/installation\">Installation Guide</a>.</li>\n<li class=\"\">API keys from your chosen AI providers:<!-- -->\n<ul>\n<li class=\"\"><strong>For Live Translation:</strong> An API Key and Region from <a href=\"https://azure.microsoft.com/en-us/products/ai-services/speech-to-text\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Microsoft Azure's Cognitive Services</a>.</li>\n<li class=\"\"><strong>For AI Chat &amp; Summaries:</strong> An API Key from <a href=\"https://aistudio.google.com/app/apikey\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Google AI Studio</a> for the Gemini API.</li>\n</ul>\n</li>\n</ul>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"step-1-configure-the-ai-providers\">Step 1: Configure the AI Providers<a href=\"https://www.plugnmeet.org/blog/how-to-add-ai-meeting-assistant-features#step-1-configure-the-ai-providers\" class=\"hash-link\" aria-label=\"Direct link to Step 1: Configure the AI Providers\" title=\"Direct link to Step 1: Configure the AI Providers\" translate=\"no\">​</a></h3>\n<p>The first step is to tell your Plug-N-Meet server how to connect to the AI services. Open your <code>config.yaml</code> file on your server and find the <code>insights</code> section.</p>\n<p>The new configuration is highly flexible. First, you define your <code>providers</code> (your accounts), and then you assign those providers to specific <code>services</code>.</p>\n<p>Here is a minimal configuration to enable all three features:</p>\n<div class=\"language-yaml codeBlockContainer_ibEU theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_osW6\"><pre tabindex=\"0\" class=\"prism-code language-yaml codeBlock_lRqf thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_jDlT\"><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token key atrule\" style=\"color:#00a4db\">insights</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token key atrule\" style=\"color:#00a4db\">enabled</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token boolean important\" style=\"color:#36acaa\">true</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\"># 1. Define all available provider accounts ONCE.</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token key atrule\" style=\"color:#00a4db\">providers</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token key atrule\" style=\"color:#00a4db\">azure</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:#393A34\">-</span><span class=\"token plain\"> </span><span class=\"token key atrule\" style=\"color:#00a4db\">id</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"my-azure-account\"</span><span class=\"token plain\"> </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\"># A unique name you choose</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">        </span><span class=\"token key atrule\" style=\"color:#00a4db\">credentials</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">          </span><span class=\"token key atrule\" style=\"color:#00a4db\">api_key</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"YOUR_AZURE_KEY\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">          </span><span class=\"token key atrule\" style=\"color:#00a4db\">region</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"eastus\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token key atrule\" style=\"color:#00a4db\">google</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:#393A34\">-</span><span class=\"token plain\"> </span><span class=\"token key atrule\" style=\"color:#00a4db\">id</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"my-gemini-account\"</span><span class=\"token plain\"> </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\"># A unique name you choose</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">        </span><span class=\"token key atrule\" style=\"color:#00a4db\">credentials</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">          </span><span class=\"token key atrule\" style=\"color:#00a4db\">api_key</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"YOUR_GEMINI_API_KEY\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\"># 2. Define the services that USE the providers.</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token key atrule\" style=\"color:#00a4db\">services</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\"># Transcription is required for both live captions and translation.</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token key atrule\" style=\"color:#00a4db\">transcription</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token key atrule\" style=\"color:#00a4db\">provider</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"azure\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token key atrule\" style=\"color:#00a4db\">id</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"my-azure-account\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\"># The AI text chat assistant.</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token key atrule\" style=\"color:#00a4db\">ai_text_chat</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token key atrule\" style=\"color:#00a4db\">provider</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"google\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token key atrule\" style=\"color:#00a4db\">id</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"my-gemini-account\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token key atrule\" style=\"color:#00a4db\">options</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">        </span><span class=\"token key atrule\" style=\"color:#00a4db\">chat_model</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"gemini-2.5-pro\"</span><span class=\"token plain\"> </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\"># Powerful model for in-depth answers</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\"># The meeting summarizer will use the audio from the transcription service.</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token key atrule\" style=\"color:#00a4db\">meeting_summarizing</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token key atrule\" style=\"color:#00a4db\">provider</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"google\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token key atrule\" style=\"color:#00a4db\">id</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"my-gemini-account\"</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token key atrule\" style=\"color:#00a4db\">options</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">        </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\"># Use gemini-2.5-flash for faster, cost-effective summaries.</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">        </span><span class=\"token key atrule\" style=\"color:#00a4db\">summarize_model</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"gemini-2.5-flash\"</span><br></div></code></pre></div></div>\n<p>Save the file and <strong>restart your PlugNmeet server</strong> for the changes to take effect.</p>\n<div class=\"language-bash codeBlockContainer_ibEU theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_osW6\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_lRqf thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_jDlT\"><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">sudo systemctl restart plugnmeet</span><br></div></code></pre></div></div>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"step-2-enable-ai-features-in-your-room\">Step 2: Enable AI Features in Your Room<a href=\"https://www.plugnmeet.org/blog/how-to-add-ai-meeting-assistant-features#step-2-enable-ai-features-in-your-room\" class=\"hash-link\" aria-label=\"Direct link to Step 2: Enable AI Features in Your Room\" title=\"Direct link to Step 2: Enable AI Features in Your Room\" translate=\"no\">​</a></h3>\n<p>Now that the server is configured, you can enable these features on a per-room basis. When you make your <code>createRoom</code> API call, add the <code>insights_features</code> block to your metadata.</p>\n<div class=\"language-json codeBlockContainer_ibEU theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_osW6\"><pre tabindex=\"0\" class=\"prism-code language-json codeBlock_lRqf thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_jDlT\"><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token property\" style=\"color:#36acaa\">\"room_id\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:#e3116c\">\"ai-powered-room\"</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token property\" style=\"color:#36acaa\">\"metadata\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token property\" style=\"color:#36acaa\">\"room_features\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token comment\" style=\"color:#999988;font-style:italic\">// ... other features like allow_webcams, etc.</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token property\" style=\"color:#36acaa\">\"insights_features\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">        </span><span class=\"token property\" style=\"color:#36acaa\">\"is_allow\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:#36acaa\">true</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">        </span><span class=\"token property\" style=\"color:#36acaa\">\"transcription_features\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">          </span><span class=\"token property\" style=\"color:#36acaa\">\"is_allow\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:#36acaa\">true</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">          </span><span class=\"token property\" style=\"color:#36acaa\">\"is_allow_translation\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:#36acaa\">true</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">        </span><span class=\"token property\" style=\"color:#36acaa\">\"ai_features\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">          </span><span class=\"token property\" style=\"color:#36acaa\">\"is_allow\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:#36acaa\">true</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">          </span><span class=\"token property\" style=\"color:#36acaa\">\"ai_text_chat_features\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">            </span><span class=\"token property\" style=\"color:#36acaa\">\"is_allow\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:#36acaa\">true</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">          </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">          </span><span class=\"token property\" style=\"color:#36acaa\">\"meeting_summarization_features\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">            </span><span class=\"token property\" style=\"color:#36acaa\">\"is_allow\"</span><span class=\"token operator\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\" style=\"color:#36acaa\">true</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">          </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><span class=\"token plain\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:#393A34\">}</span><br></div></code></pre></div></div>\n<p>That's it! Any room created with this metadata will now have the AI Meeting Assistant enabled.</p>\n<hr>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"step-3-experience-it-live\">Step 3: Experience It Live<a href=\"https://www.plugnmeet.org/blog/how-to-add-ai-meeting-assistant-features#step-3-experience-it-live\" class=\"hash-link\" aria-label=\"Direct link to Step 3: Experience It Live\" title=\"Direct link to Step 3: Experience It Live\" translate=\"no\">​</a></h3>\n<p>When a user joins a room created with these settings, the AI features are available to be activated.</p>\n<ul>\n<li class=\"\">\n<p><strong>Live Captions &amp; Translation:</strong> A moderator must first enable the service from the <strong>3-dots menu &gt; Transcription and Translation</strong>. Once enabled, participants will see a new <strong>\"T\" icon</strong> in their main control bar. Clicking this opens a menu where they can view the live captions and select their own preferred language for translation.</p>\n</li>\n<li class=\"\">\n<p><strong>Interactive AI Chat Assistant:</strong> Any user can interact with the AI assistant. In the side panel, a new <strong>\"AI Assistant\"</strong> tab will appear. Clicking this opens a dedicated chat interface where a user can privately ask the AI questions about the meeting content, get clarifications, or ask for help without cluttering the main participant chat.</p>\n</li>\n<li class=\"\">\n<p><strong>Automated Meeting Notes:</strong> To generate <strong>AI meeting notes</strong>, a moderator must start the service from the <strong>3-dots menu &gt; AI Tools &gt; Meeting Summarization</strong>. The <strong>meeting assistant AI</strong> will then process the audio in the background.</p>\n</li>\n</ul>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"conclusion\">Conclusion<a href=\"https://www.plugnmeet.org/blog/how-to-add-ai-meeting-assistant-features#conclusion\" class=\"hash-link\" aria-label=\"Direct link to Conclusion\" title=\"Direct link to Conclusion\" translate=\"no\">​</a></h2>\n<p>In just a few minutes, you've transformed a standard video call into an intelligent, interactive, and globally inclusive meeting experience. By leveraging Plug-N-Meet's provider-agnostic AI layer, you can easily add a powerful <strong>meeting assistant AI</strong> that gives you a significant edge, all while maintaining full control over your self-hosted platform.</p>\n<p>The real power of this platform is that your data isn't trapped. After the meeting ends, you can use the <strong><a class=\"\" href=\"https://www.plugnmeet.org/docs/api/artifact/fetch\">Artifacts API</a></strong> to programmatically retrieve the summary and transcription, allowing you to build powerful integrations with your new <strong>AI meeting notes</strong>.</p>",
            "url": "https://www.plugnmeet.org/blog/how-to-add-ai-meeting-assistant-features",
            "title": "How to Add an Interactive AI Assistant to Your Video App in 15 Minutes",
            "summary": "In today's competitive landscape, a basic video call is no longer enough. Users expect intelligent, accessible, and productive experiences. What if you could offer an AI meeting assistant that provides live translation, generates AI meeting notes, and can even answer questions directly in the chat?",
            "date_modified": "2026-01-03T00:00:00.000Z",
            "author": {
                "name": "Jibon L. Costa",
                "url": "https://github.com/jibon57"
            },
            "tags": [
                "tutorial",
                "how-to",
                "ai-meeting-assistant",
                "ai-text-chat",
                "ai-meeting-notes",
                "meeting-summary-ai",
                "ai-meeting-summarizer",
                "meeting-assistant-ai",
                "developer"
            ]
        },
        {
            "id": "https://www.plugnmeet.org/blog/obs-rtmp-whip-ingress",
            "content_html": "<p>Have you ever wanted to share a high-quality video during a meeting, but the file was too large to upload? Or maybe you have a copyrighted video that you are allowed to show, but you can't share the file itself. Perhaps you're a power user who wants to create a professional presentation with multiple cameras, overlays, and smooth transitions using a tool like OBS Studio.</p>\n<p>Traditionally, getting this kind of professional content into a live meeting has been complex or impossible.</p>\n<p>At plugNmeet, we believe you should be able to use the best tools for the job. That's why we've built a simple but powerful feature called <strong>Live Stream Input</strong>. Think of it as giving your meeting its own private, secure TV channel. You can broadcast directly into your meeting room from professional software like OBS, and your stream appears as a regular participant.</p>\n<hr>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"why-stream-directly-into-your-meeting\">Why Stream Directly into Your Meeting?<a href=\"https://www.plugnmeet.org/blog/obs-rtmp-whip-ingress#why-stream-directly-into-your-meeting\" class=\"hash-link\" aria-label=\"Direct link to Why Stream Directly into Your Meeting?\" title=\"Direct link to Why Stream Directly into Your Meeting?\" translate=\"no\">​</a></h2>\n<p>This feature unlocks a world of new possibilities:</p>\n<ul>\n<li class=\"\"><strong>Share Large or Private Videos:</strong> Play a high-resolution video file from your local computer without ever having to upload it.</li>\n<li class=\"\"><strong>Professional Live Productions:</strong> Use OBS Studio to switch between multiple cameras, share specific application windows, add professional graphics and lower thirds, and create a polished, TV-style presentation.</li>\n<li class=\"\"><strong>Enhanced Demonstrations:</strong> Show a complex software workflow or a live coding session with all the power and customization of your desktop setup.</li>\n</ul>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"how-to-set-it-up-a-simple-step-by-step-guide\">How to Set It Up: A Simple Step-by-Step Guide<a href=\"https://www.plugnmeet.org/blog/obs-rtmp-whip-ingress#how-to-set-it-up-a-simple-step-by-step-guide\" class=\"hash-link\" aria-label=\"Direct link to How to Set It Up: A Simple Step-by-Step Guide\" title=\"Direct link to How to Set It Up: A Simple Step-by-Step Guide\" translate=\"no\">​</a></h2>\n<p>Getting started is incredibly easy. Here’s how you do it from within your plugNmeet room:</p>\n<ol>\n<li class=\"\">\n<p><strong>Open the Settings Menu:</strong> Click the three-dots menu in the top-right corner of your meeting room and select <strong>Settings</strong>.</p>\n</li>\n<li class=\"\">\n<p><strong>Find Live Stream Input:</strong> In the settings panel, navigate to the <strong>Live Stream Input</strong> tab.</p>\n</li>\n<li class=\"\">\n<p><strong>Choose Your \"Channel\" Type (RTMP or WHIP):</strong>\nYou'll see an option called \"Input Type.\" This lets you choose the technology for your stream. Think of it like choosing between two types of cables to connect your camera:</p>\n<ul>\n<li class=\"\"><strong>RTMP:</strong> This is the trusty, universal standard. It's been used for years by platforms like YouTube and Twitch and is supported by almost every streaming application. It's a great, reliable choice.</li>\n<li class=\"\"><strong>WHIP:</strong> This is the new, modern standard designed specifically for real-time communication. It's generally faster, with less delay between your stream and what your audience sees. If your software supports it, WHIP is the future-proof option.</li>\n</ul>\n</li>\n<li class=\"\">\n<p><strong>Give Your Stream a Name:</strong> In the <strong>Display Name</strong> field, you can type in a name for your stream. This is the name that will appear in the participant list. For example, \"Main Presentation\" or \"Guest Speaker Cam.\" If you leave it blank, it will default to \"Broadcaster.\"</p>\n</li>\n<li class=\"\">\n<p><strong>Generate Your Private Link:</strong> Click the <strong>\"Generate link\"</strong> button. plugNmeet will instantly create a unique and secure address for your private stream. You'll see two fields: a <strong>URL</strong> and a <strong>Secret</strong> (or \"Stream Key\").</p>\n</li>\n</ol>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"connecting-your-streaming-software-like-obs\">Connecting Your Streaming Software (like OBS)<a href=\"https://www.plugnmeet.org/blog/obs-rtmp-whip-ingress#connecting-your-streaming-software-like-obs\" class=\"hash-link\" aria-label=\"Direct link to Connecting Your Streaming Software (like OBS)\" title=\"Direct link to Connecting Your Streaming Software (like OBS)\" translate=\"no\">​</a></h2>\n<p>Now, you just need to tell your software (we'll use OBS as an example) where to send the stream.</p>\n<ul>\n<li class=\"\">\n<p><strong>For RTMP (The Universal Standard):</strong></p>\n<ol>\n<li class=\"\">In OBS settings, go to \"Stream.\"</li>\n<li class=\"\">For \"Service,\" choose \"Custom...\"</li>\n<li class=\"\">Copy the <strong>URL</strong> from plugNmeet and paste it into the \"Server\" field in OBS.</li>\n<li class=\"\">Copy the <strong>Secret</strong> from plugNmeet and paste it into the \"Stream Key\" field in OBS.</li>\n</ol>\n</li>\n<li class=\"\">\n<p><strong>For WHIP (The Modern Choice):</strong></p>\n<ol>\n<li class=\"\">In OBS settings, go to \"Stream.\"</li>\n<li class=\"\">For \"Service,\" choose \"WHIP.\"</li>\n<li class=\"\">Combine the URL and Secret from plugNmeet into a single line. For example: <code>https://your-plugnmeet-domain.com/whip/A_LONG_SECRET_KEY_HERE</code></li>\n<li class=\"\">Paste this full, combined address into the \"Server\" field in OBS.</li>\n<li class=\"\"><strong>Leave the \"Bearer Token\" field in OBS blank.</strong></li>\n</ol>\n</li>\n</ul>\n<p>Once you hit \"Start Streaming\" in OBS, your stream will magically appear as a new participant in your plugNmeet room for everyone to see!</p>\n<hr>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"conclusion-professional-power-made-simple\">Conclusion: Professional Power, Made Simple<a href=\"https://www.plugnmeet.org/blog/obs-rtmp-whip-ingress#conclusion-professional-power-made-simple\" class=\"hash-link\" aria-label=\"Direct link to Conclusion: Professional Power, Made Simple\" title=\"Direct link to Conclusion: Professional Power, Made Simple\" translate=\"no\">​</a></h2>\n<p>The Live Stream Input feature is a perfect example of our philosophy. We've taken a powerful, professional-grade technology and made it accessible to everyone with just a few clicks. You don't need to be a technical expert to create a high-quality, polished, and engaging presentation.</p>\n<p>The best tools are the ones that get out of your way and let your content shine.</p>\n<hr>\n<p><strong>Ready to level up your presentations?</strong></p>\n<ul>\n<li class=\"\"><strong>Try our <a href=\"https://demo.plugnmeet.com/landing.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Live Demo</a> and test it yourself</strong></li>\n<li class=\"\"><strong>Explore our <a href=\"https://github.com/mynaparrot/plugNmeet-server\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Open-Source Project on GitHub</a></strong></li>\n<li class=\"\"><strong>Read our <a class=\"\" href=\"https://www.plugnmeet.org/docs/developer-guide/scalable-setup\">Scalable Deployment Guide</a></strong></li>\n</ul>",
            "url": "https://www.plugnmeet.org/blog/obs-rtmp-whip-ingress",
            "title": "Stream Like a Pro: How to Bring OBS into Your plugNmeet Room",
            "summary": "Have you ever wanted to share a high-quality video during a meeting, but the file was too large to upload? Or maybe you have a copyrighted video that you are allowed to show, but you can't share the file itself. Perhaps you're a power user who wants to create a professional presentation with multiple cameras, overlays, and smooth transitions using a tool like OBS Studio.",
            "date_modified": "2025-12-31T00:00:00.000Z",
            "author": {
                "name": "Bob Teng",
                "url": "https://github.com/wbobteng"
            },
            "tags": [
                "obs",
                "streaming",
                "rtmp",
                "whip",
                "features",
                "ease-of-use",
                "professional"
            ]
        },
        {
            "id": "https://www.plugnmeet.org/blog/smart-video-streaming",
            "content_html": "<p>We've all been in that frustrating video call. Someone is speaking, and suddenly their video freezes. Their voice becomes robotic and distorted. You miss a crucial piece of the conversation, and the flow of the meeting is completely broken.</p>\n<p>Often, we blame our internet connection. But what if the problem isn't just the connection, but the software's inability to adapt to it?</p>\n<p>At plugNmeet, we believe that a video conferencing platform should work intelligently to provide the best possible experience, no matter what your internet situation is. That's why we've built a \"smart streaming\" philosophy into our core, using advanced techniques to ensure your calls are smooth, stable, and use as little data as possible.</p>\n<hr>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"the-problem-the-one-size-fits-all-video-stream\">The Problem: The \"One-Size-Fits-All\" Video Stream<a href=\"https://www.plugnmeet.org/blog/smart-video-streaming#the-problem-the-one-size-fits-all-video-stream\" class=\"hash-link\" aria-label=\"Direct link to The Problem: The &quot;One-Size-Fits-All&quot; Video Stream\" title=\"Direct link to The Problem: The &quot;One-Size-Fits-All&quot; Video Stream\" translate=\"no\">​</a></h2>\n<p>Most video applications work like a firehose. They try to send a single, high-quality stream of video data to everyone, all the time. If your internet connection is a wide-open pipe, this works great.</p>\n<p>But what if your connection is more like a narrow straw? Trying to force a firehose of data through a tiny straw leads to a predictable result: a clog. In video terms, this \"clog\" is what you experience as lag, frozen video, and dropped calls.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"the-plugnmeet-solution-smart-adaptive-streaming\">The plugNmeet Solution: Smart, Adaptive Streaming<a href=\"https://www.plugnmeet.org/blog/smart-video-streaming#the-plugnmeet-solution-smart-adaptive-streaming\" class=\"hash-link\" aria-label=\"Direct link to The plugNmeet Solution: Smart, Adaptive Streaming\" title=\"Direct link to The plugNmeet Solution: Smart, Adaptive Streaming\" translate=\"no\">​</a></h2>\n<p>Instead of a single firehose, we use several smart techniques to adapt the video stream to the reality of each person's connection and what they are actually seeing on their screen.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"technique-1-offering-multiple-qualities-like-netflix-does\">Technique 1: Offering Multiple Qualities (Like Netflix Does)<a href=\"https://www.plugnmeet.org/blog/smart-video-streaming#technique-1-offering-multiple-qualities-like-netflix-does\" class=\"hash-link\" aria-label=\"Direct link to Technique 1: Offering Multiple Qualities (Like Netflix Does)\" title=\"Direct link to Technique 1: Offering Multiple Qualities (Like Netflix Does)\" translate=\"no\">​</a></h3>\n<p>Think about watching a movie on Netflix. If your internet slows down, the picture might get a little less sharp for a moment, but the movie keeps playing. Netflix does this by having multiple quality versions of the movie available.</p>\n<p>We do the same thing for live video. This is called <strong>Simulcast</strong>.</p>\n<p>For every person speaking, our server intelligently prepares multiple versions of their video stream: a high-quality one, a medium one, and a low-quality one. Your device automatically \"tunes in\" to the best quality it can handle at any given moment without freezing. The result is a continuous, uninterrupted conversation, even if the video quality has to adjust temporarily.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"technique-2-pausing-what-you-cant-see\">Technique 2: Pausing What You Can't See<a href=\"https://www.plugnmeet.org/blog/smart-video-streaming#technique-2-pausing-what-you-cant-see\" class=\"hash-link\" aria-label=\"Direct link to Technique 2: Pausing What You Can't See\" title=\"Direct link to Technique 2: Pausing What You Can't See\" translate=\"no\">​</a></h3>\n<p>Imagine a meeting with 20 people. On your screen, you can probably only see 9 of them at once. So why should your computer waste precious data downloading the video streams of the 11 people who are off-screen?</p>\n<p>It shouldn't. This is called <strong>Dynacast</strong>.</p>\n<p>plugNmeet is smart enough to automatically pause the video streams of participants who are not currently visible on your screen. The moment you scroll or switch pages to bring them into view, their video instantly resumes. This single technique can dramatically reduce the amount of data your computer needs to download, making the call more stable for you and everyone else.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"technique-3-sending-the-just-right-size\">Technique 3: Sending the \"Just Right\" Size<a href=\"https://www.plugnmeet.org/blog/smart-video-streaming#technique-3-sending-the-just-right-size\" class=\"hash-link\" aria-label=\"Direct link to Technique 3: Sending the &quot;Just Right&quot; Size\" title=\"Direct link to Technique 3: Sending the &quot;Just Right&quot; Size\" translate=\"no\">​</a></h3>\n<p>On your screen, the person speaking might be in a large main window, while other participants are in tiny thumbnail squares. It makes no sense to download a full, high-definition video stream just to display it in a tiny box.</p>\n<p>Our platform is smart about this, too. It automatically sends a much smaller, lower-resolution video for thumbnails, while reserving the high-quality video for the main speaker. This further reduces data usage and frees up your connection to focus on what's most important.</p>\n<h3 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"technique-4-prioritizing-voice-above-all-else\">Technique 4: Prioritizing Voice Above All Else<a href=\"https://www.plugnmeet.org/blog/smart-video-streaming#technique-4-prioritizing-voice-above-all-else\" class=\"hash-link\" aria-label=\"Direct link to Technique 4: Prioritizing Voice Above All Else\" title=\"Direct link to Technique 4: Prioritizing Voice Above All Else\" translate=\"no\">​</a></h3>\n<p>What is the most important part of any conversation? <strong>Hearing what the other person is saying.</strong></p>\n<p>Our platform understands this fundamental rule. When a user's internet connection becomes very unstable, the system performs a kind of digital triage. It knows that it is always better to have a clear conversation with a frozen video than to have a choppy, robotic voice with a moving picture.</p>\n<p>So, it makes a smart choice: it will <strong>temporarily pause the video stream</strong> to dedicate all available, limited bandwidth to keeping the audio channel clear and uninterrupted. The moment the connection stabilizes, the video seamlessly resumes. This ensures that your conversation can always continue, even in the most challenging network conditions.</p>\n<hr>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"conclusion-a-more-accessible-and-reliable-experience-for-everyone\">Conclusion: A More Accessible and Reliable Experience for Everyone<a href=\"https://www.plugnmeet.org/blog/smart-video-streaming#conclusion-a-more-accessible-and-reliable-experience-for-everyone\" class=\"hash-link\" aria-label=\"Direct link to Conclusion: A More Accessible and Reliable Experience for Everyone\" title=\"Direct link to Conclusion: A More Accessible and Reliable Experience for Everyone\" translate=\"no\">​</a></h2>\n<p>By combining these smart streaming techniques, plugNmeet creates a video experience that is:</p>\n<ul>\n<li class=\"\"><strong>More Resilient:</strong> It gracefully handles fluctuations in network quality, preventing the freezes and robotic voices that kill conversations.</li>\n<li class=\"\"><strong>More Cost-Effective:</strong> By intelligently reducing data usage, it's a lifesaver for anyone on a metered or expensive data plan.</li>\n<li class=\"\"><strong>More Accessible:</strong> It ensures that users in areas with variable connectivity can still have a meaningful and productive meeting.</li>\n</ul>\n<p>Our goal is to make real-time communication a stable, reliable utility for everyone, everywhere. Our smart streaming philosophy is how we deliver on that promise.</p>\n<hr>\n<p><strong>Ready to experience a smoother call?</strong></p>\n<ul>\n<li class=\"\"><strong>Try our <a href=\"https://demo.plugnmeet.com/landing.html\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Live Demo</a> and see the difference</strong></li>\n<li class=\"\"><strong>Read our <a class=\"\" href=\"https://www.plugnmeet.org/blog/backend-architecture-deep-dive\">Architecture Deep Dive</a> to learn more</strong></li>\n<li class=\"\"><strong>Explore our <a href=\"https://github.com/mynaparrot/plugNmeet-server\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Open-Source Project on GitHub</a></strong></li>\n</ul>",
            "url": "https://www.plugnmeet.org/blog/smart-video-streaming",
            "title": "A Smoother Call on Any Connection: Our Smart Approach to Video Streaming",
            "summary": "We've all been in that frustrating video call. Someone is speaking, and suddenly their video freezes. Their voice becomes robotic and distorted. You miss a crucial piece of the conversation, and the flow of the meeting is completely broken.",
            "date_modified": "2025-12-27T00:00:00.000Z",
            "author": {
                "name": "Jibon L. Costa",
                "url": "https://github.com/jibon57"
            },
            "tags": [
                "performance",
                "adaptive-streaming",
                "bandwidth",
                "user-experience",
                "accessibility"
            ]
        },
        {
            "id": "https://www.plugnmeet.org/blog/privacy-by-design-zero-trust",
            "content_html": "<p>In an era where data breaches are daily news and \"free\" services monetize your conversations, privacy can no longer be an afterthought. It must be the foundation.</p>\n<p>When we built Plug-N-Meet, we didn't just want to create another video conferencing tool. We wanted to build a platform that respects the user's right to privacy by default. This meant making hard architectural choices—prioritizing security over easy data harvesting, and giving control back to the user rather than hoarding it on the server.</p>\n<p>If you are building a telehealth platform, a legal consultation app, or simply a secure meeting space for your team, you need more than just a \"secure\" sticker. You need an architecture designed for the Zero-Trust era.</p>\n<p>Here is how Plug-N-Meet delivers on that promise.</p>\n<hr>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"1-the-gold-standard-true-zero-trust-e2ee\">1. The Gold Standard: True Zero-Trust E2EE<a href=\"https://www.plugnmeet.org/blog/privacy-by-design-zero-trust#1-the-gold-standard-true-zero-trust-e2ee\" class=\"hash-link\" aria-label=\"Direct link to 1. The Gold Standard: True Zero-Trust E2EE\" title=\"Direct link to 1. The Gold Standard: True Zero-Trust E2EE\" translate=\"no\">​</a></h2>\n<p>Most video platforms claim to be \"secure\" because they use encryption in transit (TLS/DTLS). While this protects your data from hackers on the coffee shop Wi-Fi, it has a major flaw: the server itself still decrypts your video to process it. This means the service provider (and anyone who hacks them) can see and hear everything.</p>\n<p>Plug-N-Meet offers a <strong>Zero-Trust</strong> alternative: <strong>User-Provided Key End-to-End Encryption (E2EE)</strong>.</p>\n<p>When you enable this mode (<code>enabled_self_insert_encryption_key: true</code>), the encryption keys are generated by the participants and shared directly between them (e.g., via a password manager or secure chat). <strong>These keys never leave the user's device.</strong></p>\n<ul>\n<li class=\"\"><strong>The Result:</strong> The server only relays encrypted packets. It mathematically cannot decrypt the video or audio.</li>\n<li class=\"\"><strong>The Benefit:</strong> You achieve true \"Zero Knowledge\" privacy. Even if the server is compromised, your meetings remain a black box.</li>\n</ul>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"2-data-minimization--ephemeral-storage\">2. Data Minimization &amp; Ephemeral Storage<a href=\"https://www.plugnmeet.org/blog/privacy-by-design-zero-trust#2-data-minimization--ephemeral-storage\" class=\"hash-link\" aria-label=\"Direct link to 2. Data Minimization &amp; Ephemeral Storage\" title=\"Direct link to 2. Data Minimization &amp; Ephemeral Storage\" translate=\"no\">​</a></h2>\n<p>A core principle of modern privacy (and regulations like GDPR) is <strong>Data Minimization</strong>: don't collect what you don't need, and don't keep it longer than necessary.</p>\n<p>Plug-N-Meet is designed with <strong>ephemeral storage</strong> at its core:</p>\n<ul>\n<li class=\"\"><strong>Volatile Session State:</strong> Active meeting data (who is in the room, who is muted) lives in high-performance, in-memory stores like Redis or NATS KV. When the meeting ends, this data is naturally cleared.</li>\n<li class=\"\"><strong>Client-Side History:</strong> Chat history and user preferences are stored in the user's own browser using <code>IndexedDB</code>, not permanently on our servers. This puts the user in control of their own data footprint.</li>\n<li class=\"\"><strong>Granular Analytics:</strong> You have the power to disable analytics entirely (<code>enable_analytics: false</code>) or configure the system to store only metadata (e.g., \"User A spoke for 5 minutes\") without ever recording the content of what was said.</li>\n</ul>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"3-the-nats-advantage-protocol-level-access-control\">3. The NATS Advantage: Protocol-Level Access Control<a href=\"https://www.plugnmeet.org/blog/privacy-by-design-zero-trust#3-the-nats-advantage-protocol-level-access-control\" class=\"hash-link\" aria-label=\"Direct link to 3. The NATS Advantage: Protocol-Level Access Control\" title=\"Direct link to 3. The NATS Advantage: Protocol-Level Access Control\" translate=\"no\">​</a></h2>\n<p>Many web applications rely solely on application-level logic (e.g., <code>if (user.isAdmin)</code>) to check if a user should see a message. If that logic has a bug, data leaks.</p>\n<p>Plug-N-Meet takes a more robust approach by leveraging the powerful security features of <strong>NATS JetStream</strong> to enforce the <strong>Principle of Least Privilege</strong>.</p>\n<ul>\n<li class=\"\"><strong>Dynamic Permissions:</strong> When a user joins, the system generates a custom set of NATS permissions specific to that user's session.</li>\n<li class=\"\"><strong>Protocol Enforcement:</strong> These permissions are enforced by the NATS server itself, not just the Plug-N-Meet application code.</li>\n<li class=\"\"><strong>The Result:</strong> A participant literally <strong>cannot</strong> subscribe to a data stream they aren't authorized for. Even if a modified client tries to \"listen in\" on another room or a private chat, the NATS server will reject the subscription request at the protocol level.</li>\n</ul>\n<p>This provides a hard security boundary that is rare in self-hosted web conferencing tools, ensuring that data isolation is enforced by the infrastructure, not just the code.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"4-defense-in-depth-security\">4. Defense-in-Depth Security<a href=\"https://www.plugnmeet.org/blog/privacy-by-design-zero-trust#4-defense-in-depth-security\" class=\"hash-link\" aria-label=\"Direct link to 4. Defense-in-Depth Security\" title=\"Direct link to 4. Defense-in-Depth Security\" translate=\"no\">​</a></h2>\n<p>Privacy relies on security, and security requires layers. Beyond NATS and E2EE, we employ multiple other safeguards:</p>\n<ul>\n<li class=\"\"><strong>Token-Based Access:</strong> Every user joins with a short-lived, one-time-use token. This prevents \"replay attacks\" where a hacker tries to reuse an old link to crash a meeting.</li>\n<li class=\"\"><strong>Salted Key Derivation:</strong> As detailed in our <a class=\"\" href=\"https://www.plugnmeet.org/docs/security-overview\">Security Overview</a>, we use the unique <code>session_id</code> as a salt when deriving encryption keys. This ensures that even if a room ID is reused, every single session has a unique, cryptographically isolated key.</li>\n</ul>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"5-privacy-aware-ai\">5. Privacy-Aware AI<a href=\"https://www.plugnmeet.org/blog/privacy-by-design-zero-trust#5-privacy-aware-ai\" class=\"hash-link\" aria-label=\"Direct link to 5. Privacy-Aware AI\" title=\"Direct link to 5. Privacy-Aware AI\" translate=\"no\">​</a></h2>\n<p>Integrating AI into meetings is powerful, but it's often a privacy nightmare. We've solved this with a \"Privacy First\" integration strategy.</p>\n<p>The system is designed to be <strong>context-aware</strong>. If you enable End-to-End Encryption (E2EE) with a user-provided key, Plug-N-Meet <strong>automatically disables</strong> all audio-based AI features (like transcription and recording).</p>\n<p>Why? Because the AI bot on the server cannot decrypt the audio stream. This prevents the dangerous scenario where a user <em>thinks</em> they are in a secure, private meeting, but an AI service is silently listening in the background. With Plug-N-Meet, if you choose maximum privacy, the system enforces it across the board.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"6-the-ultimate-privacy-feature-self-hosting\">6. The Ultimate Privacy Feature: Self-Hosting<a href=\"https://www.plugnmeet.org/blog/privacy-by-design-zero-trust#6-the-ultimate-privacy-feature-self-hosting\" class=\"hash-link\" aria-label=\"Direct link to 6. The Ultimate Privacy Feature: Self-Hosting\" title=\"Direct link to 6. The Ultimate Privacy Feature: Self-Hosting\" translate=\"no\">​</a></h2>\n<p>Finally, the most important privacy feature of Plug-N-Meet is that <strong>you own it</strong>.</p>\n<ul>\n<li class=\"\"><strong>No Black Boxes:</strong> Because the code is open source, it can be audited. There are no hidden \"telemetry\" trackers sending your data back to a headquarters.</li>\n<li class=\"\"><strong>Your Infrastructure, Your Rules:</strong> You aren't renting privacy from a third-party SaaS provider. You host the server, you control the database, and you own the logs.</li>\n</ul>\n<h2 class=\"anchor anchorTargetStickyNavbar_q1dW\" id=\"conclusion\">Conclusion<a href=\"https://www.plugnmeet.org/blog/privacy-by-design-zero-trust#conclusion\" class=\"hash-link\" aria-label=\"Direct link to Conclusion\" title=\"Direct link to Conclusion\" translate=\"no\">​</a></h2>\n<p>In a world of surveillance capitalism, privacy is a competitive advantage. Whether you are building for healthcare (HIPAA), education (FERPA), or enterprise security, Plug-N-Meet provides a foundation that doesn't just \"support\" privacy—it enforces it.</p>\n<p>By combining Zero-Trust E2EE, protocol-level access control, and the transparency of open source, we've built a platform where you don't have to trust us. You just have to trust the code.</p>\n<hr>\n<p><strong>Ready to build your secure platform?</strong></p>\n<ul>\n<li class=\"\"><strong>Deep dive into our <a class=\"\" href=\"https://www.plugnmeet.org/docs/security-overview\">Security Architecture</a></strong></li>\n<li class=\"\"><strong>Learn about our <a class=\"\" href=\"https://www.plugnmeet.org/blog/e2ee-key-models-guide\">E2EE Key Models</a></strong></li>\n<li class=\"\"><strong>Get started with the <a class=\"\" href=\"https://www.plugnmeet.org/docs/installation\">Installation Guide</a></strong></li>\n</ul>",
            "url": "https://www.plugnmeet.org/blog/privacy-by-design-zero-trust",
            "title": "Privacy by Design: Why Plug-N-Meet is Built for the Zero-Trust Era",
            "summary": "In an era where data breaches are daily news and \"free\" services monetize your conversations, privacy can no longer be an afterthought. It must be the foundation.",
            "date_modified": "2025-12-20T00:00:00.000Z",
            "author": {
                "name": "Jibon L. Costa",
                "url": "https://github.com/jibon57"
            },
            "tags": [
                "privacy",
                "security",
                "zero-trust",
                "e2ee",
                "open-source",
                "architecture",
                "gdpr"
            ]
        }
    ]
}