Awesome Open Source
Awesome Open Source

Virtual Office

Did you ever wonder where your colleague currently is? In what room they hide?

Virtual Office tries to give you transparency on what rooms are occupied and who is currently there.

Virtual Office Screenshot


  • React Web UI
  • Uses Webhooks to give a real-time status update on who currently participates in what room
  • Login via Slack

At TNG we've also hosted a big company internal event with 450 participants using Virtual Office. If you're interested, you can find our experiences in two articles (German only):


  1. Checkout the repository.

    git clone
  2. Install all dependencies.

    npm run installAll
  3. Create a new Slack App, as currently the only authentication option is Slack (

    • Extract Basic Information
      • You will need the Client ID and Client Secret in the server config
    • Configure OAuth & Permissions
      • Redirect URL: ${YOUR_BASE_URL_COMES_HERE}/auth/slack/callback
      • User Token Scope: identity.basic, identity.avatar,
  4. Adapt the Virtual Office configuration.

    • via .env file: Copy <client|server>/.env-example to <client|server>/.env and adapt the content. All entries will be available to the app as environment variables.
    • set the environment variables manually, i.e. for using it via some deployment plan in a CI server.
  5. Configure the webhooks

    • Navigate to the Marketplace
    • Click Manage and create a new webhook application
    • Fill out the usual information, go to Feature and enable Event Subscriptions, add a new subscription with
      • Events:
        • End Meeting
        • Participant/Host joined meeting
        • Participant/Host left meeting
      • Notification endpoint URL:
  6. Compile and start the application in production mode

    npm run buildAll
    cd server && npm start
  7. (Alternative) Serve the application in development mode

    cd server && npm run dev
    cd client && npm start

Available Environment Variables

Variable name Usage
PORT Port the app is running on, defaults to 9000
SLACK_CLIENT_ID The Client ID you got when creating the Slack application
SLACK_SECRET The Client Secret you got when creating the Slack application
CONFIG OR CONFIG_LOCATION A office config as JSON string OR
A file system location to the office configuration
SESSION_SECRET Secret that is used to encrypt cookies that are stored on client side. If you omit this option, a new secret will be generated on each server start (meaning that users will have to re-login after each server restart!)
ADMIN_USERNAME Username for accessing admin endpoints
ADMIN_PASSWORD Password for accessing admin endpoints
VIEW_MODE Determines how the frontend will show the rooms, either list or grid
THEME Either light or dark
BACKGROUND_URL URL to a background image
LOGO_URL URL to a logo image to be included in the header
FAVICON_URL URL to a favicon
WRITE_OFFICE_UPDATES_TO_FILE_SYSTEM When replacing the office via the /admin API, write the changes to the filesstem. Will only be done when set to true
TIMEZONE Timezone determining the start and end dates of sessions. If unset, the server will use the server timezone and the clients will use the client timezone. Timezone is in format "Europe/Berlin", a list can be found here
TITLE Title of the app
DISABLE_AUTH When hosting public events that do not require a login, set this to true
SESSION_START_MINUTES_OFFSET Number of minutes the session is considered active (so events will be sent to the client, people can join)
HIDE_ENDED_SESSIONS When sessions shall be hidden after they have ended, set this to true

Office Configuration

You can find an example office definition in server/office.json.

An example looks like this:

  "groups": [
      "id": "star_wars",
      "name": "Star Wars",
      "groupJoin": {
        "minimumParticipantCount": 3
      "disabledBefore": "2020-05-29T17:15:00.000+02:00",
      "disabledAfter": "2020-05-29T19:00:00.000+02:00"
  "rooms": [
      "meetingId": "1",
      "name": "Lobby",
      "subtitle": "This is where everything starts.",
      "joinUrl": "",
      "links": [
          "href": "",
          "text": "Google",
          "icon": ""
      "icon": ""
      "meetingId": "2",
      "name": "Alderaan",
      "joinUrl": "",
      "links": [
          "href": "",
          "text": "Issue Tracker",
          "icon": ""
          "href": "",
          "text": "Whiteboard",
          "icon": ""
      "groupId": "star_wars",
      "icon": ""

Important: For the webhooks to work, the meetingId has to be the meeting ID, as this id acts as correlation id for webhook events from zoom.

groupJoin within the groups property is optional and defines whether existing rooms will be filled up by a separate join button on the group.

Admin Endpoints

You can view all available endpoints at /api-docs.


You have to provide configuration parameters to the server by environment variables. As a convenient alternative you can provide it via an .env file (see .env-example) that will be loaded by dotenv.

See Contributing for details.

Get A Weekly Email With Trending Projects For These Topics
No Spam. Unsubscribe easily at any time.
typescript (11,582
video (868
collaboration (112
remote (75
zoom (74
office (73
conference (72
virtual (38