Skip to main content

Getting Started

This guide walks you through shipping your first Vibe app — from an empty machine to a live URL.

Prerequisites#

  • Node.js ≥ 20 and npm. If you don't have them, see Installing Node below.
  • A Facilio account with permission to create Vibe apps.
  • A modern browser for the one-time OAuth approval during vibe login.

The four-step workflow#

1. vibe login                          # once per machine2. vibe app create  (first time only)  # writes vibe.json with the app linkName3. <your build command>                # e.g. npm run build, vite build, etc.4. vibe deploy                         # zips build.publish, uploads, publishes

Repeat steps 3–4 to ship new versions. Every vibe deploy produces an incrementing versionNumber and a stable live URL plus an immutable versionedUrl.

The only step that needs a human is vibe login — it opens a browser, shows a code, and waits for you to click Approve. Everything else can be fully automated.

0. Install Node.js and npm#

Skip if node -v reports v20 or newer.

# macOS / Linux — nvm (no sudo, installs into $HOME)curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bashexport NVM_DIR="$HOME/.nvm"[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"nvm install --lts && nvm use --ltsnode -v && npm -v

Windows (PowerShell): nvm-sh doesn't work. Use winget install OpenJS.NodeJS.LTS or install scoop and run scoop install nodejs-lts.

1. Install the CLI and SDK#

# CLI — install globally so `vibe` is on PATHnpm install -g @facilio/vibe-cli
# SDK — install per-project (when you scaffold your app below)npm install @facilio/vibe-sdk

If npm install -g fails with a permissions error, do NOT run sudo. Reconfigure npm's global prefix to live in your home dir, or use npx:

# Option A — install globals under ~/.npm-globalmkdir -p ~/.npm-globalnpm config set prefix ~/.npm-globalexport PATH="$HOME/.npm-global/bin:$PATH"npm install -g @facilio/vibe-cli
# Option B — skip global install; run via npx every timenpx @facilio/vibe-cli loginnpx @facilio/vibe-cli app create --name "My App"npx @facilio/vibe-cli deploy

If you installed Node via nvm, npm install -g already works without sudo.

2. Authenticate#

vibe login

A browser tab opens with an approval code. Click Approve to finish signing in. The CLI polls until approval lands and prints Logged in as <email>..

If the browser didn't open (headless box, SSH session), the CLI prints a URL and a code — open the URL on any device and enter the code.

Sanity check:

vibe whoami# you@yourcompany.com  •  https://app.facilio.com

3. Scaffold the project#

Vibe accepts any framework. The example below uses Vite + React, but plain HTML or any other framework works the same.

mkdir -p ~/vibe-apps && cd ~/vibe-appsnpm create vite@latest my-dashboard -- --template reactcd my-dashboardnpm installnpm install @facilio/vibe-sdk

4. Wire the SDK#

Edit src/App.jsx:

import { useEffect, useState } from 'react';import { createVibe } from '@facilio/vibe-sdk';
const vibe = createVibe();
export default function App() {  const [me, setMe] = useState(null);  const [assets, setAssets] = useState([]);  const [error, setError] = useState(null);
  useEffect(() => {    (async () => {      const user = await vibe.getCurrentUser();      if (!user) {        vibe.login();   // redirects to identity-service        return;      }      setMe(user);      try {        const { response } = await vibe.executeAction('facilio-cmms', 'list-assets');        setAssets(response?.data ?? []);      } catch (err) {        setError(err.message);      }    })();  }, []);
  if (error) return <p style={{ color: 'crimson' }}>{error}</p>;  if (!me) return <p>Loading…</p>;
  return (    <main style={{ fontFamily: 'system-ui', padding: 24 }}>      <h1>Hello, {me.user.name}</h1>      <p>Org: {me.org.orgId}</p>      <h2>Assets ({assets.length})</h2>      <ul>        {assets.map(a => <li key={a.id}>{a.name} #{a.id}</li>)}      </ul>    </main>  );}

Key patterns

  • getCurrentUser() is the single source of truth for "signed in?". If it returns null, call vibe.login().
  • Do not wire vibe.login() into the catch block of executeAction. A 401 from a data call is just an error — surface err.message.
  • All Facilio data access goes through vibe.executeAction(connectionSlug, actionSlug, payload). Slugs and payload shapes come from the MCP catalog.

5. Create the app#

vibe app create --name "My Dashboard"

This is non-interactive when --name is passed. The CLI writes vibe.json to the project root with the assigned linkName:

{  "name": "my-dashboard",  "app": "my-dashboard",  "build": {    "publish": "dist"  }}

You only run this once per app. Subsequent deploys read app from vibe.json.

If your bundler emits to a folder other than dist/ (e.g. build/, out/), edit build.publish accordingly.

6. Build and deploy#

npm run build && vibe deploy

The CLI:

  1. Reads vibe.json → finds the build.publish directory.
  2. Zips its contents (level-9 deflate).
  3. Uploads, triggers publish, polls until DEPLOYED.
  4. Prints the live URL and the immutable versioned URL.
✔ Deployed v1  Live:    https://my-dashboard.vibe.facilio.com  Archive: https://my-dashboard.vibe.facilio.com/v/1

Open the live URL in a browser. The first load redirects you through identity-service (because getCurrentUser() returned null); after approval you land back on the app with the assets list rendered.

7. Iterate#

For every change after the first deploy: edit source, build, deploy. No further login or app create.

npm run build && vibe deploy

Each deploy bumps the version. The live URL stays the same; the versioned URL (/v/N) is immutable.

Without a bundler (vanilla HTML)#

If you just want a one-page tool, skip the bundler entirely:

my-app/├── vibe.json└── dist/    ├── index.html    └── app.js

dist/index.html:

<!doctype html><html>  <head><meta charset="utf-8"><title>Asset list</title></head>  <body>    <button id="load">Load assets</button>    <ul id="list"></ul>    <script src="https://unpkg.com/@facilio/vibe-sdk"></script>    <script src="./app.js"></script>  </body></html>

dist/app.js:

const vibe = VibeSDK.createVibe();document.getElementById('load').onclick = async () => {  const user = await vibe.getCurrentUser();  if (!user) return vibe.login();  const { response } = await vibe.executeAction('facilio-cmms', 'list-assets');  document.getElementById('list').innerHTML =    (response?.data ?? []).map(a => `<li>${a.name} #${a.id}</li>`).join('');};

vibe.json:

{ "name": "asset-list", "app": "asset-list", "build": { "publish": "dist" } }

Then vibe login → vibe app create --name "Asset list" → vibe deploy. No bundler, no transpile.

Checklist before vibe deploy#

  • vibe login succeeded (vibe whoami prints your email).
  • vibe app create has been run at least once; vibe.json contains "app": "<linkName>".
  • The folder named in vibe.json build.publish exists and contains index.html at its root.
  • Any Facilio data access in the code uses vibe.executeAction(...) with slugs discovered from https://mcp.facilio.com/mcp — not invented.
  • Login flow is wired: getCurrentUser() checked on load, login() / logout() available to the user.

Next steps#