Setup guide
How it works
World Space Meet has two parts. This app (accounts, rooms, dashboard, API) runs on your Hostinger Node.js hosting. The live video and audio run on a media service called LiveKit, because shared web hosting cannot carry real-time video traffic. LiveKit Cloud has a free plan, so you can launch without paying for video.
Get your free video keys
- Create a free account at cloud.livekit.io. No card is needed for the Build plan.
- Create a project. Copy its WebSocket URL. It looks like
wss://your-project.livekit.cloud. - Open the project's Settings, then Keys, and create a key. Copy the API key and the API secret.
- In World Space Meet, sign in as the owner, open Settings on the dashboard, paste the three values, press Save, then Test connection.
The free Build plan includes about 5,000 participant-minutes a month and 100 people connected at once (at the time of writing; check LiveKit's pricing page). A one-hour call with 5 people uses 300 minutes. Past the limit, new joins fail until the month resets or you upgrade.
Deploy on Hostinger Business
- Use the ZIP file that came with this project:
world-space-meet-hostinger.zip. It has nonode_modules, which is what Hostinger expects. - In hPanel open Websites, then Add Website, then Node.js web app, then Upload your files, and choose the ZIP.
- Confirm the settings: Node.js 20 or newer, install command
npm install, start commandnpm start. - Add environment variables (optional but recommended):
SIGNUPS=closedafter you create the owner account, andDATA_FILEif you want the data file in a specific folder. - Press Deploy, then connect your domain from the site's dashboard. HTTPS is required for camera access, and Hostinger provides the certificate.
Your data (accounts and rooms) is stored in one JSON file, data/wsmeet-data.json. Back it up before redeploying. For a larger business, move to the MySQL database that comes with your plan.
First run
- Open your site and create an account. The first account becomes the owner and gets the Pro plan.
- Add the LiveKit keys in Settings (see above).
- Start a room. Open the link in a private window to join as a guest and test video.
- Every account belongs to a company or organization, named at sign-up. All applications and API keys created afterward live under that company. Set
SIGNUPS=closedif you do not want strangers creating accounts, or leave it open to run it as a SaaS. New accounts start on the Free plan (20 rooms a month). Change any account to Pro in Settings.
Using rooms
- Meeting: everyone can use camera, mic, screen share, and emoji reactions.
- Interview: guests wait until the host admits them. The Admit button appears on the host's screen.
- Webinar: the host speaks, guests watch and can chat or raise a hand.
- Instant: a quick room with no setup.
- Hosts can mute everyone, remove a person, and end the room for all. Guests need no account, only the link.
- Recurring rooms: when scheduling, set Repeats to daily or weekly. The same link keeps working; its shown time rolls forward automatically once the previous one has passed.
API and embed
Create a room from your own server with the API key from the dashboard:
curl -X POST /v1/meetings \
-H "x-api-key: YOUR_KEY" -H "content-type: application/json" \
-d '{"template":"interview","title":"Round 2"}'
The reply has id and hostKey. Send guests to /?room=ID&join=1. Give the host a link after storing hostKey in their session, or have them sign in and press Open.
Embed a room inside another site:
<iframe src="/?room=ID&join=1&embed=1&theme=light" allow="camera; microphone; display-capture; fullscreen" style="width:100%;height:600px;border:0"></iframe>
Applications and branding for 3rd parties
The Applications panel on the dashboard is where you give a 3rd party (a client, a partner, or your own other product) its own way into World Space Meet. Each application gets:
- Its own API key (starts with
nxa_), separate from your personal one, that only that application uses. - Its own look: a primary color, an accent color, a font, a corner style, and an optional logo URL. Any room created with that application's key automatically opens with that look for every guest — no theme parameters to pass, no setup on the guest's side. This is what makes the interface adaptive: the same World Space Meet code renders differently depending on which application called it.
- A choice of who keeps the meeting data:
- Hosted here (default): meetings created by that application show up in your dashboard like any other, same as your own rooms.
- Your own system (external database): you give it a webhook URL, and every event for that application's meetings (created, joined, ended) is delivered there instead. Once a meeting created this way ends, World Space Meet does not keep a copy of it — your own system is the record of it.
To use an application from outside code, send its key the same way as a personal key:
curl -X POST /v1/meetings -H "x-api-key: nxa_YOUR_APP_KEY" -H "content-type: application/json" -d '{"template":"meeting","title":"Branded room"}'
Companies and their applications are kept separate from each other: one company can never see or edit another company's applications, API keys, or data.
Webhooks
From the dashboard, add a URL and pick which events to send: meeting.created, participant.joined, meeting.ended. Each account manages its own webhooks (up to 10), and each one gets its own signing secret, shown once when you create it.
Every delivery is a POST with a JSON body and an x-wsmeet-signature header, which is an HMAC-SHA256 of the raw body using your webhook's secret. Verify it before trusting the payload:
const crypto = require('crypto');
const expected = crypto.createHmac('sha256', YOUR_WEBHOOK_SECRET).update(rawBody).digest('hex');
if (expected !== req.headers['x-wsmeet-signature']) return res.status(401).end();
Use Send test on the dashboard to confirm your endpoint is reachable before relying on it.
World Space ID (single sign-on)
World Space Meet can let people sign in with World Space ID — your organization's own OAuth 2.0 / OIDC identity provider — instead of, or alongside, an email and password. This uses the standard Authorization Code flow with PKCE, the same pattern World Space ID's other client apps use.
- In World Space ID's admin area, register a new OAuth client app. Set its redirect URI to
https://your-domain.com/auth/wsid/callback. Copy the client ID and client secret it gives you. - In the dashboard's owner Settings, open World Space ID sign-in, paste your World Space ID base URL (e.g.
https://id.yourcompany.com), the client ID and client secret, then press Save and Test connection. - A "Sign in with World Space ID" button now appears on the sign-in page automatically. The first person who signs in that way becomes the owner if no account exists yet; everyone else is created as a regular member of their World Space ID organization.
Behind the scenes: /auth/wsid/start generates a PKCE verifier/challenge pair and redirects to World Space ID's authorize endpoint; /auth/wsid/callback exchanges the returned code for tokens (verifying the PKCE verifier), fetches the user's profile from the userinfo endpoint, and creates or updates a local account matched by the World Space ID sub (falling back to matching by email). A normal session cookie is issued either way, so the rest of the app doesn't need to know how someone signed in.
Live Monitor
The owner's dashboard includes a Live Monitor panel: a running log of sign-ins (password or World Space ID) and admin actions — who, when, from which IP, and the device (mobile or desktop, OS, browser) detected from their browser. It keeps the most recent 500 events and needs no setup.
Updates, version and rollback
The dashboard's Updates & version panel shows the running version and lets the owner apply an update without using Hostinger's File Manager: build a release .zip containing only public/*.html, server.js, package.json and/or README.md, and upload it there. The server backs up every file it's about to replace into data/backups/<timestamp>/ first, rejects any filename outside that allowed set (no path traversal, no new dependencies), then extracts the new files in place. Static page changes apply immediately; if server.js or package.json changed, restart the Node.js app from hPanel to pick them up.
Every one of those backups stays listed in the Backups & rollback card below it. Press "Restore this version" on any of them to put those exact files back — the files it's about to overwrite are backed up first too, so a restore is itself undoable.
Setup page and super admin
On a brand-new install with no accounts yet, opening the site sends you to /setup.html — a guided installer in the same spirit as World Space ID's own setup.php. It checks the Node.js version, confirms the data folder is writable, and creates the first account, which becomes the super admin (role admin, Pro plan, full access to every owner-only panel). After that first account exists, new visitors go straight to the sign-in page instead.
Database (optional MySQL)
World Space Meet keeps accounts, rooms and settings in a single JSON file by default — nothing to install, nothing that can refuse a connection. If you'd rather use your own MySQL database (Hostinger's databases work fine), save its host, port, user, password and database name in Dashboard > Settings > Database, press Test connection (a real SELECT 1 against it), then press Switch storage to MySQL. Everything currently in the local file is copied over immediately, and every save from then on writes to both the local file and MySQL, with MySQL as the source of truth read back at startup.
This is a snapshot sync, not a relational schema — one table (wsmeet_store) holding the whole app's data as a single JSON column, updated on every write. It trades normalized SQL for a safe, mistake-proof migration, and it solves the one thing that actually matters on most hosts: your accounts and rooms surviving a redeploy that wipes the filesystem.
Important: the database connection itself is normally saved inside that same local file, so on a host where redeploys wipe the filesystem entirely (not just update it), the server would have nothing left telling it where to reconnect. Avoid that by setting DB_HOST, DB_USER, DB_PASSWORD, DB_NAME and optionally DB_PORT as environment variables in hPanel (Node.js app > Settings) once you've switched storage to MySQL — env vars survive a redeploy even when the filesystem doesn't, and the server falls back to them automatically whenever the local file has no working database connection of its own.
Video & API providers
The Video & API panel lets the owner configure more than one WebRTC backend and switch the active one at any time — new rooms use whichever is active when they're created:
- LiveKit — open source; use LiveKit Cloud's free tier or point the URL at a server you self-host. Needs a project URL, API key and secret (Settings panel).
- Jitsi Meet — open source and free.
meet.jit.sineeds no account or keys at all; point the domain field at your own self-hosted Jitsi instance instead if you run one. - Daily.co — commercial, with a free tier and paid plans for more concurrent participants, recording and other add-ons. Needs only an API key from the Daily dashboard.
- Twilio Video — commercial, with a free trial. Unlike Jitsi and Daily.co, Twilio has no prebuilt call screen of its own, so World Space Meet draws its own video grid for it, the same as it does for LiveKit (mic, camera, screen share, chat, reactions, hand-raise and captions all work). Needs an Account SID and an API Key (SID + secret) from the Twilio Console, under Account > API keys & tokens — use an API Key, not the Account's main Auth Token.
Switching providers doesn't affect rooms already in progress, only rooms created after the switch. Host controls that need server-side coordination — "Mute all" and "Remove participant" — only work with LiveKit; Jitsi, Daily.co and Twilio don't expose an equivalent through this API, so those buttons stay hidden for rooms on those providers. "End for all" works everywhere.
Recording
Hosts can record LiveKit rooms (meeting, interview, webinar, instant — not Jitsi or Daily.co, which have their own recording inside their own call UI) with the Start recording button in the room's controls. World Space Meet doesn't encode video itself: LiveKit's Egress service composites the room and uploads the finished file straight to an S3-compatible bucket, so recording needs two things configured in Dashboard > Settings > Recording:
- An S3-compatible bucket — AWS S3, Cloudflare R2, Backblaze B2, MinIO, or similar — with its endpoint, region, access key and secret.
- LiveKit Egress itself, which only comes with LiveKit Cloud or a self-hosted LiveKit deployment that includes the Egress worker; the plain
--devLiveKit server used for local testing doesn't run it.
Point LiveKit's webhook (in your LiveKit project settings) at the URL shown on the Recording card (/webhooks/livekit). It's signed with your LiveKit API key and secret and verified the same way — when a recording finishes, that webhook is how the file's location reaches World Space Meet; there's no polling. A recording's status and file location are available at GET /v1/meetings/:id/recordings.
Live captions
The "Captions" button in LiveKit and Twilio rooms (meeting, interview, webinar, instant — not Jitsi or Daily.co, which have their own captions inside their own call UI) turns on speech-to-text using each participant's own browser, not a server-side service: no API key, no extra cost, works in Chrome and Edge (browsers without the Web Speech API simply don't show the button). Each person captions their own microphone; the text shows as an overlay for everyone in the room and is saved to that room's transcript, the same place typed chat goes.
AI meeting summaries
There's no server-side speech-to-text pipeline — this summarizes whatever ended up in the room's transcript: typed chat messages, and spoken lines captured by live captions if anyone turned them on. Once the meeting ends, anyone can turn the transcript into brief meeting notes (a summary paragraph plus bullet points) from the "Past meetings & AI summaries" card on the dashboard's Overview page.
To turn it on, add an AI provider in Dashboard > Settings > AI meeting summaries: a base URL, a model name, and an API key. It works with any OpenAI-compatible chat completions API — OpenAI itself, Groq, OpenRouter, or a local server like Ollama (point the base URL at its OpenAI-compatible endpoint). Summaries are generated on request, not automatically, and are cached on the room once made.
Billing (Stripe Checkout)
Anyone can upgrade themselves from Free (20 rooms/month) to Pro (2,000 rooms/month) without the owner doing anything manually, using Stripe Checkout — card details are entered on Stripe's own hosted page and never touch this server.
- In your Stripe dashboard, copy a secret key (Developers > API keys).
- Create a webhook endpoint pointed at
https://your-domain.com/api/billing/webhook, subscribed to thecheckout.session.completedandcustomer.subscription.deletedevents, and copy its signing secret. - Paste both, plus the monthly price in cents, into Dashboard > Settings > Billing, then press Test connection.
From then on, a Free account sees an "Upgrade to Pro" banner on their Overview page. It opens a Stripe Checkout session (a $-per-month subscription) with that account's id attached as the client_reference_id; when Stripe confirms payment, the signed webhook flips their plan to Pro, and a cancelled subscription flips it back to Free. The dashboard also double-checks right after a successful checkout redirect, so the upgrade shows immediately even if the webhook is a few seconds behind. Use a sk_test_ key and Stripe's test card numbers to try the whole flow before going live.
Accounts and passwords
Forgot your password? Use the link on the sign-in page. This install has no outgoing email set up, so instead of emailing a link, it shows the link on screen right after you request it. Treat that link like a password: anyone with it can reset the account. It expires in one hour and works once.
New accounts also get an email verification link the same way — shown on screen, not mailed. An unverified account still works normally; the Overview page just shows a banner with a "Show verification link" button until it's confirmed.
Team
The account that creates an organization is its owner. From Dashboard > Team, the owner can invite teammates by email; each invite is a one-time link (expires in 7 days) handed back the same way the password reset and verification links are — copy it and send it yourself. Anyone who accepts joins the same organization, sharing its rooms, applications and plan, but keeps their own login, API key, and webhook settings. Members can see the team list but can't invite or remove anyone; only the owner (or the site super admin) can.
Troubleshooting
- "Video service is not set up": add the LiveKit keys in Settings and press Test connection.
- Camera does not start: the page must use https (or localhost), and the browser must have permission.
- Test connection fails: the URL must start with
wss://, and the key and secret must come from the same LiveKit project. - Lost accounts after a redeploy: restore
data/wsmeet-data.jsonfrom your backup. - Guests cannot join in an interview: the host must press Admit first.