Skip to main content

CLI Reference

Every Facilio product ships as part of a single CLI — @facilio/cli (binary: facilio). One login covers every product; every product's commands live under its own namespace.

The command surface splits into three groups:

GroupNamespaceWhat it does
1. Faciliofacilio <cmd>Auth commands shared across every product: login, logout, whoami.
2. Vibefacilio vibe <cmd>Build and deploy Vibe apps: deploy, app, db, function, jobs.
3. Connectionsfacilio connections <cmd>Discover and run actions across 1000+ integrated apps: search, schemas, list, link, unlink, wait, execute.
Coming from @facilio/vibe-cli?

The old package (binary: vibe) is deprecated. Mental model for the migration: prefix facilio before every vibe command, and drop the vibe prefix from login / logout / whoami since those are now shared across products. See Migrating from @facilio/vibe-cli.

Install#

# Install globally so `facilio` is on PATHnpm install -g @facilio/cli
# Or run via npx without a global installnpx @facilio/cli <command>

If npm install -g fails with a permissions error, reconfigure npm's global prefix into your home dir (don't use sudo):

mkdir -p ~/.npm-globalnpm config set prefix ~/.npm-globalexport PATH="$HOME/.npm-global/bin:$PATH"npm install -g @facilio/cli

Migrating from @facilio/vibe-cli#

The old package is deprecated. To migrate:

npm uninstall -g @facilio/vibe-clinpm install -g @facilio/cli

Your existing vibe.json files keep working unchanged — only the binary name and command layout changed. On first facilio login, the CLI reads any credentials left behind by the old vibe binary (keychain service facilio-vibe-cli, or ~/.vibe/credentials.json) and migrates them into the new locations (service facilio-cli, or ~/.facilio/credentials.json) — you don't need to re-authenticate.


1. Facilio commands#

Auth commands shared across every Facilio product. One facilio login covers Vibe, Connections, and everything else — you never re-authenticate per product.

facilio login#

Authenticate with Facilio using OAuth2 device flow. Works on laptops, over SSH, and inside containers.

facilio login

Flow:

  1. The CLI prints a code and opens a browser tab.
  2. You click Approve in the browser. (This is the one human action in the whole workflow.)
  3. The CLI polls until approval lands, then prints Logged in as <email>.

Run once per machine.

Flags#

FlagNotes
--api-key <key>Log in with an API key instead of the browser flow. Use - to read from stdin. Preferred for CI.
--region <key>Region for --api-key logins (US, UK, AE, AU, …). Resolves the right server URLs. Defaults to US.
--server <url>Pin the vibe-server URL for this session (overrides the region table).
--client-id <id>Override the OAuth client id (default: FACILIO_CLIENT_ID).
--identity-url <url>Identity-service base URL (default: FACILIO_IDENTITY_URL).

Where credentials are stored#

The CLI persists the token to the OS-provided secret store by default, with a 0600 file fallback only when no OS store is available:

PlatformStorageMechanism
macOSKeychainsecurity CLI — service facilio-cli, account default
LinuxSecret Service / libsecretsecret-tool (needs libsecret-tools)
WindowsCredential Manager (DPAPI)Per-user encrypted ciphertext written to ~/.facilio/credentials.dpapi
Fallback~/.facilio/credentials.json (mode 0600)Used when no OS store is available (headless Linux without libsecret, etc.)

facilio whoami reports which sink is in use. To force the file fallback (e.g. in CI without a session keyring), set FACILIO_NO_KEYCHAIN=1.

Headless / SSH / Docker: if no browser opens, the CLI prints a URL and a code — open the URL on any device.


facilio whoami#

Print the email and server of the active session. Use in scripts to sanity-check login state.

$ facilio whoamiyou@yourcompany.com  •  https://app.facilio.com

Exits non-zero if not logged in.


facilio logout#

Revoke the session server-side (best effort) and remove the stored token from the OS keychain (or the file fallback, whichever holds it).

facilio logout

2. Vibe commands#

Build and deploy Vibe apps. Every Vibe command lives under facilio vibe <cmd> and is scoped to the app in your current directory's vibe.json.

vibe.json — project config#

Place at the project root. The CLI reads and writes this file.

{  "name": "my-app",  "app": "my-app",  "build": {    "publish": "dist"  }}
FieldRequiredNotes
nameoptionalHuman-readable name. Shown in facilio vibe app list.
appyes (after first deploy)The linkName of the app on the server. Written automatically by facilio vibe app create.
build.publishyesFolder containing built static files. Must contain index.html. Default: dist.

facilio vibe app create scaffolds or patches this file for you — you don't need to write it by hand.


facilio vibe app create#

Create a new Vibe app on the server. Writes (or patches) vibe.json in the current directory.

facilio vibe app create

The command prompts interactively for name, description, logo, and output directory. Every field except name is optional — press Enter to skip.

Flags#

All flags are optional; anything you don't pass is prompted for.

FlagNotes
--nameHuman-readable name. Server derives the linkName (subdomain) from this.
--descriptionOptional description shown in facilio vibe app list.
--logoPath to a png/jpg/svg/webp/gif/ico file, ≤ 750 KB.

Output directory#

Only prompted in interactive mode. Defaults to dist. If your bundler emits elsewhere (build/, out/, public/), edit vibe.json build.publish after the create call.

Result#

  • vibe.json is created (or patched) with "app": "<linkName>" so subsequent facilio vibe deploy calls don't need any flags.
  • The app's live URL is printed.

Run this once per app. If you're shipping a new version of an existing app, skip this step.


facilio vibe app list#

List all Vibe apps in the org.

$ facilio vibe app listLINK NAME       NAME              STATUS     LAST PUBLISHED       URLmy-dashboard    My Dashboard      DEPLOYED   2026-06-25 10:14     https://my-dashboard.vibe.facilio.comasset-list      Asset list        DEPLOYED   2026-06-24 18:02     https://asset-list.vibe.facilio.com

facilio vibe deploy#

Zip the publish folder, upload it, and publish it as a new version.

# In the project root, after running your buildfacilio vibe deploy

What it does:

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

The live URL is stable across deploys. The versioned URL is immutable — useful for rollback links or sharing a snapshot.

Flags#

FlagNotes
--prodMark this deployment as production.
--app <linkName>Override the app value from vibe.json (e.g., shipping the same build to a different app).

You run your build, not the CLI#

The CLI does not invoke npm run build, vite build, etc. Build first, then deploy:

npm run build && facilio vibe deploy

Hard constraint on the publish folder#

The folder named by build.publish must contain index.html at its root. JS, CSS, and asset files alongside it are fine and expected — they're loaded by <script src=...> / <link rel=...> tags inside index.html as your bundler emits them. No index.html in the publish folder → the deploy succeeds but the URL serves nothing useful.


Database — facilio vibe db#

Each app can have its own dedicated database: a Postgres schema plus a scoped role and login user, provisioned on demand. All vibe db commands are app-scoped — they resolve the target app from --app or vibe.json, and add --app <linkName> to override.

facilio vibe db create#

Provision the database for the current app. Idempotent — re-running returns the existing schema/role/user rather than creating a second one. Run this once before importing tables.

facilio vibe db create
✓ Database ready.  Schema: vibe_ab12cd…  Role:   vibe_ab12cd…_rw  User:   vibe_ab12cd…_usr

The schema is named after the app and is isolated — no other app can see it.

facilio vibe db import#

Create a table by importing a CSV file. Columns are inferred from the CSV header and data server-side. Requires facilio vibe db create first.

facilio vibe db import --file customers.csv --table customers
✓ Imported 128 rows into vibe_ab12cd….customers  Columns: 5
FlagRequiredNotes
--file <path>yesPath to the CSV file. Prompted if omitted.
--table <name>noTarget table name. Defaults to a sanitized form of the file name.
--app <linkName>noOverride the app target from vibe.json.

The import is not atomic (CREATE TABLE followed by chunked INSERTs) — a mid-way failure can leave a partially populated table.

facilio vibe db tables (alias ls)#

List the tables in the app's database.

facilio vibe db tables
NAME       TYPE   ROWS---------  -----  ----customers  table  128orders     table  512

facilio vibe db describe <table> (alias desc)#

Show a table's columns (name, type, nullable) and its total row count.

facilio vibe db describe customers
→ vibe_ab12cd….customers  (128 rows)COLUMN  TYPE     NULLABLE------  -------  --------id      integer  noname    text     yesemail   text     yes

Functions — facilio vibe function (alias fn)#

Functions are server-side handlers you author, compile to WASM, and invoke. They can run SQL against the app's database and call Facilio connections. Functions are app-scoped: the logical name you choose (e.g. workorderlist) is unique within your app, and the backend uploads it under an app-unique physical name so functions in different apps never collide or see each other. Every command resolves the app from --app or vibe.json.

Lifecycle: create → build → run. Updating re-uploads the source and requires a rebuild.

Writing a function#

For the authoritative, always-current version of this format, run facilio vibe function instructions — it prints the guide straight from ai-studio, so it never drifts from a hand-copied template.

A function is a JS/TS module built on @facilio/studio-functions. Register one or more named handlers, then call server.execute():

import StudioFunctions, { secret } from "@facilio/studio-functions";
const server = new StudioFunctions({ name: "workorderlist", version: "1.0.0" });
server.addHandler({  name: "list",  description: "List open work orders",  parameters: {    limit: { description: "Max rows", type: "number" },  },  execute: async (args) => {    const schema = secret("SCHEMA");   // this app's DB schema    const dbUser = secret("DB_USER");  // its DB login user    // ...run SQL / call connections, then return any JSON-serializable value    return { rows: [] };  },});
server.execute();

Secrets are injected by the backend at run time and read with secret("KEY") — the caller never passes them:

SecretWhat it is
CONNECTIONS_TOKENToken to reach Facilio connections.
AGENTS_TOKENToken to reach the agents (ai-studio) service.
SCHEMAThe app's provisioned DB schema (present once facilio vibe db create has run).
DB_USERThe app's DB login user.

facilio vibe function instructions (alias guide)#

Print the authoritative guide for writing a function. vibe-server fetches it from ai-studio (the same source the platform uses), so it's always current — prefer this over any static example when authoring. Org-level; takes no --app.

facilio vibe function instructions# capture it to a file, or hand it to an AI agent:facilio vibe function instructions > function-guide.md

facilio vibe function create <name>#

Upload a new function from a code file. Fails if a function with that name already exists in the app.

facilio vibe function create workorderlist --code ./workorderlist.js --description "List open work orders"
FlagRequiredNotes
--code <path>yesPath to the function source file. Prompted if omitted.
--package <path>noOptional package.json for the function's dependencies.
--description <text>noStored with the function; shown in facilio vibe function list.
--app <linkName>noOverride the app target from vibe.json.

facilio vibe function update <name>#

Replace an existing function's source. Fails if it doesn't exist. Re-uploading invalidates the previous build — run facilio vibe function build again afterward. Same flags as create.

facilio vibe function update workorderlist --code ./workorderlist.js

facilio vibe function build <name>#

Compile the uploaded source to WASM. Synchronous — may take a while. On success it lists the discovered handler names.

facilio vibe function build workorderlist
✓ Built "workorderlist".  Built at: 2026-07-03T09:14:02Z  WASM size: 480321 bytes  Handlers: list  Run one with `facilio vibe function run workorderlist <handler>`.

facilio vibe function run <name> <handler> (alias exec)#

Execute one handler of a built function. --args is a JSON object of the handler's arguments. Secrets are injected by the backend (see above) — you never pass them. Prints the handler's return value.

facilio vibe function run workorderlist list --args '{"limit": 20}'
FlagNotes
--args <json>Handler arguments as a JSON object. Defaults to {}.
--app <linkName>Override the app target from vibe.json.

facilio vibe function list (alias ls)#

List the app's functions.

facilio vibe function list
NAME           BUILT  DESCRIPTION-------------  -----  ---------------------workorderlist  yes    List open work orders

facilio vibe function get <name> (alias show)#

Show a function's build state, description, and source. Pass --code-only to print just the code — handy for redirecting to a file.

facilio vibe function get workorderlistfacilio vibe function get workorderlist --code-only > workorderlist.js

facilio vibe function delete <name> (alias rm)#

Delete a function and all its artifacts.

facilio vibe function delete workorderlist

Running from a deployed app: functions can also be invoked from the browser at runtime via vibe.executeFunction. The backend resolves which app you're in from the subdomain, so browser calls run only that app's functions.


Scheduled Jobs — facilio vibe jobs (alias job)#

Scheduled jobs run one of your app's built functions on a recurring schedule — cron or fixed interval — without a browser being open. Every fire executes as the app's dedicated public user (the one facilio vibe app create provisions), so the function's identity, tokens, and DB access work exactly like an authenticated request. Jobs are app-scoped — resolved from --app or vibe.json.

Typical loop: create a job → it fires on schedule → observe Last run on the CLI, or the row it writes in your app's DB / UI.

Product-level bounds#

Two hard bounds the server enforces on every create and update — they're a product decision, not a technical limit:

BoundValueWhy
Min interval between fires15 minutesConsecutive fires of the same job cannot be closer than this. For interval schedules, intervalSeconds >= 900. For cron, the CLI parses your expression and rejects it if two consecutive slots are under 15 min apart.
Max timeout per fire15 minutes (900 s)A single fire cannot claim more than 15 min of wall clock — after that the runner is cancelled and the fire is recorded as failed. Also the default when --timeout is omitted.

Prerequisites#

Before you can schedule a job:

  1. The app must exist (facilio vibe app create).
  2. The function must be built (facilio vibe function build <name>).
  3. The app must have been promoted to production — scheduled jobs run against the prod function name (<name>_<uuid>), never against preview. If you schedule a job on an app that only exists on preview, every fire will fail with "function not found".

facilio vibe jobs create <name>#

Schedule a new job. Requires --function and exactly one of --cron or --interval.

# Cron — every day at 9 AM (in the org's timezone; falls back to UTC)facilio vibe jobs create daily-report \  --function sendDailyReport \  --handler default \  --cron '0 0 9 * * *' \  --payload '{"recipients":["ops@example.com"]}' \  --timeout 300
# Interval — every 30 minutes, minimal flagsfacilio vibe jobs create healthcheck \  --function pingUpstream \  --interval 1800
# Create as PAUSED — no scheduler row until you `resume`facilio vibe jobs create monthly-cleanup \  --function purgeStale \  --cron '0 0 3 1 * *' \  --paused
FlagRequiredNotes
--function <name>yesLogical function name from facilio vibe function list. Must be built.
--handler <name>noEntry point inside the function. Omit → SDK default.
--cron <expr>one of theseSpring 6-field cron: second minute hour day-of-month month day-of-week. Consecutive fires must be ≥ 15 min apart.
--interval <seconds>one of theseFixed-delay in seconds. Must be ≥ 900.
--timeout <seconds>noWall-clock per fire, 1..900. Defaults to 900 (15 min).
--payload <json>noJSON object forwarded verbatim as the function's args at every fire.
--pausednoCreate in paused state; scheduler row is written only when you resume.
--app <linkName>noOverride the app target from vibe.json.

Cron vs interval — which to pick#

Reach for cron when...Reach for interval when...
Timing matters to the outside world — "9 AM daily", "1st of the month", "Monday 8 AM".The exact moment doesn't matter — "roughly every N minutes".
You want fires aligned to wall-clock ticks.You want guaranteed spacing regardless of how long the last run took.
You care about "which day" or "which weekday".Health checks, polling, periodic sync.

Same 15-min floor and 900-s timeout ceiling apply to both. Cron expressions are interpreted in the org's configured timezone (from your Facilio account settings), falling back to UTC.

facilio vibe jobs list (alias ls)#

List every job configured for the app. Newest-first.

facilio vibe jobs list
NAME          FUNCTION        SCHEDULE       STATUS   LASTRUN------------  --------------  -------------  -------  ---------------------------------daily-report  sendDaily...    cron 0 0 9...  active   2026-07-17T03:00:04Z (success)healthcheck   pingUpstream    every 1800s    active   —

facilio vibe jobs get <name> (alias show)#

Full detail — schedule, timeout, payload, and last-run outcome including any error.

facilio vibe jobs get daily-report
daily-report  (id 1, status=active)  Function : sendDailyReport › default  Schedule : cron 0 0 9 * * *  Timeout  : 300s  Payload  : {"recipients":["ops@example.com"]}  Last run : 2026-07-17T03:00:04Z — success

If a fire failed, Last run shows failed and Error shows the failure reason (truncated to ~2 KB). That's the single most useful signal for debugging.

facilio vibe jobs update <name>#

Partial PATCH — pass only the flags you want to change. Everything else stays as it was. Immutable fields (name, functionName) can't be updated — delete + recreate to change either.

# Change the schedulefacilio vibe jobs update daily-report --cron '0 30 9 * * *'
# Replace the payloadfacilio vibe jobs update daily-report --payload '{"recipients":["ops@example.com","cto@example.com"]}'
# Bump the timeoutfacilio vibe jobs update healthcheck --timeout 60
FlagNotes
--handler <name>Change the handler entry point.
--cron <expr>Switch to a cron schedule. Rejects --interval on the same call.
--interval <seconds>Switch to an interval schedule. Rejects --cron on the same call.
--timeout <seconds>Change the wall-clock timeout. Same 1..900 bound.
--payload <json>Replace the payload (JSON object).
--status <active\|paused>Enable / disable firing without deleting the row.
--app <linkName>Override the app target from vibe.json.

Passing no flags is rejected — nothing to update.

facilio vibe jobs pause <name>#

Sugar for update --status paused. Stops firing but keeps the row so you can resume later without losing the schedule or payload.

facilio vibe jobs pause daily-report

Behind the scenes: cancels the scheduled_tasks row so a paused job incurs zero scheduler wakeups. The Vibe_App_Jobs row stays intact.

facilio vibe jobs resume <name>#

Sugar for update --status active. Re-creates the scheduler row using the job's current schedule; first fire is at "now + interval" (interval jobs) or the next cron slot (cron jobs).

facilio vibe jobs resume daily-report

facilio vibe jobs delete <name> (alias rm)#

Remove the job and cancel its scheduler row.

facilio vibe jobs delete daily-report

Failure model — keep it simple#

  • A failed fire keeps firing on schedule. No exponential backoff, no max-retries pause — the job stays active and fires again next slot. LAST_RUN_STATUS + LAST_RUN_ERROR on the row (visible via jobs get) record the last outcome.
  • Broken job? Pause it manually. facilio vibe jobs pause <name>. Fix the function or the payload, then resume.
  • Wall-clock timeout hits? The runner is cancelled with an interrupt, the fire is recorded as failed: timed out after Ns, and the job reschedules to its next slot as usual.

Common rejection reasons#

MessageCause
intervalSeconds must be at least 900Interval schedule shorter than 15 min.
cronExpression must schedule fires at least 15 minutes apartCron expression whose consecutive slots are too close.
timeoutSeconds must be at most 900Timeout above the 15-min ceiling.
function '<name>' not found in app <linkName>Function doesn't exist. Build it first.
app has no public user provisionedLegacy app that predates the public-user feature — redeploy the app to provision one.
job '<name>' already exists in app <linkName> (409)Job name isn't unique per app. Pick a different name or update the existing one.

3. Connections commands#

Facilio Connections is Facilio's integration layer: a catalog of 1000+ external apps (Xero, Salesforce, HubSpot, Slack, …) and 5000+ actions across them, all callable through one authenticated interface. From the CLI, you can discover actions, authorize accounts, and run those actions directly — using the same facilio login session you use for Vibe.

Every Connections command lives under facilio connections <cmd>. The typical loop is:

  1. search — find actions by natural-language description ("create xero invoice").
  2. schemas — look up the input/output JSON Schemas for the actions you found.
  3. link — authorize the account (once per connection per user).
  4. execute — run the action with a JSON payload.

list, unlink, and wait are used less frequently to inspect and manage connected accounts.

Global flags#

These flags apply to every facilio connections <cmd> invocation:

FlagNotes
--mcp-url <url>Override the Connections MCP endpoint. Defaults to FACILIO_MCP_URL / the region default.
--app <slug>Scope every call to one connection (uses the /<slug>/mcp endpoint). Faster than searching across all 1000+ apps when you already know which one you're targeting.
--jsonPrint raw JSON payloads. Use in scripts and agents.

facilio connections search#

Find actions by use case. The query is free-form natural language; the server ranks matching actions across every connection.

facilio connections search create xero invoicefacilio connections search list open workorders --app facilio-cmmsfacilio connections search send slack message --json

Returns action slugs of the form <connection>.<action> (e.g. xero.create_invoice). Use those slugs with schemas and execute.


facilio connections schemas <action_slugs...>#

Show the input (and optionally output) JSON Schemas for one or more actions.

facilio connections schemas xero.create_invoicefacilio connections schemas xero.create_invoice salesforce.create_lead --with-output
FlagNotes
--with-outputAlso fetch the output schema so you know how to parse the response.

Feed the input schema to an LLM or read it yourself to build the --params payload for execute.


facilio connections list <connections...> (alias ls)#

Show your connected accounts for one or more connections. Use to check whether you've already authorized an app and to look up the account_slug for execute --account.

facilio connections list xerofacilio connections list xero salesforce hubspot

facilio connections link <connection>#

Authorize a connection. Opens the OAuth URL for the target app in your browser (or prints it, so you can complete the auth on another device).

facilio connections link xerofacilio connections link salesforce --wait --timeout 120facilio connections link hubspot --no-open           # prints the URL only
FlagNotes
--no-openDon't auto-open the browser. Useful on headless machines.
--waitBlock until the connection becomes ACTIVE.
--timeout <seconds>With --wait, give up after this many seconds.

facilio connections unlink <connection>#

Remove your authorization for a connection. Prompts to confirm.

facilio connections unlink xerofacilio connections unlink xero --yes                # skip confirmation

facilio connections wait <connections...>#

Poll until one or all of the named connections become ACTIVE. Handy after link --no-open when the OAuth handshake happens on a different device.

facilio connections wait xerofacilio connections wait xero salesforce --mode all --timeout 300
FlagNotes
--mode <mode>any (default — exit as soon as one becomes active) or all (wait for every named connection).
--timeout <seconds>Give up after this many seconds.

facilio connections execute [action_slugs...] (alias exec)#

Run one or more actions. Pass the JSON payload with --params — repeat --params once per slug to run several actions in parallel.

# Run one actionfacilio connections execute xero.create_invoice --params '{"amount":100,"contactId":"abc"}'
# Read the payload from stdin (safer for secrets and long payloads)cat payload.json | facilio connections execute xero.create_invoice --params -
# Target a specific connected account (from `connections list`)facilio connections execute xero.create_invoice --params '{"amount":100}' --account xero-us
# Validate without executing — prints the request that would be sentfacilio connections execute xero.create_invoice --params '{"amount":100}' --dry-run
# Just show the input schema and exit (equivalent to `connections schemas`)facilio connections execute xero.create_invoice --get-schema
# Batch mode — read {action_slug, arguments, account_slug?} tuples from a JSON filefacilio connections execute --file batch.json
# Run multiple actions in parallel — one --params per slug, in orderfacilio connections execute xero.create_invoice salesforce.create_lead \  --params '{"amount":100}' \  --params '{"name":"Acme"}'
FlagNotes
--params <json>Action arguments as a JSON object. Use - to read from stdin. Repeat once per slug to run several actions in parallel.
--account <slug>Target a specific connected account (from facilio connections list). Only relevant when you have multiple accounts for the same connection.
--file <path>Batch mode. Reads a JSON array of {action_slug, arguments, account_slug?} objects.
--dry-runValidate arguments against the action schema and print the request without executing.
--get-schemaPrint the input schema for the given action slugs and exit — no execution.

Common errors#

ErrorCauseFix
Not logged inNo token in the OS keychain or file fallbackRun facilio login
vibe.json not foundRunning facilio vibe deploy outside the project rootcd into the project root, or run facilio vibe app create first
index.html missing in dist/Build didn't emit an entry pointCheck your bundler config; ensure the output dir matches build.publish
EACCES on npm install -gNode was installed via system package managerReconfigure npm prefix (see Install) — do not use sudo
App with linkName already existsRe-running facilio vibe app create for an app that already existsSkip this step; subsequent deploys use vibe.json

Env vars#

All env vars use the FACILIO_ prefix. Legacy VIBE_ names from the old @facilio/vibe-cli are still honored as silent fallbacks so existing setups keep working.

VariablePurpose
FACILIO_API_KEYLog in via API key without the browser flow. Set once and every command authenticates from it. Preferred for CI.
FACILIO_REGIONRegion key for API-key logins (US, UK, AE, AU, …). Resolves the right server URLs. Defaults to US.
FACILIO_NO_KEYCHAINSet to 1 / true / yes to skip the OS secret store and use ~/.facilio/credentials.json (0600) instead. Useful in CI without a session keyring.
FACILIO_VIBE_SERVER_URLOverride the vibe-server API URL. Defaults to the server picked at login.
FACILIO_IDENTITY_SERVER_URLOverride the identity-service base URL. Defaults are set per environment.
FACILIO_CONNECTIONS_SERVER_URLOverride the Connections MCP endpoint. Overridable per-command via --mcp-url.
FACILIO_CLIENT_IDOverride the OAuth client id.

End-to-end script#

For an agent shipping an app on a fresh machine:

# 0. Bootstrap Node if missingcommand -v node >/dev/null 2>&1 || {  curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash  export NVM_DIR="$HOME/.nvm"; \. "$NVM_DIR/nvm.sh"  nvm install --lts && nvm use --lts}
# 1. Scaffold the projectmkdir -p ~/vibe-apps && cd ~/vibe-appsnpm create vite@latest my-dashboard -- --template reactcd my-dashboardnpm installnpm install @facilio/vibe-sdk
# 2. Install the CLI (fall back to npx if global install fails)npm install -g @facilio/cli 2>/dev/null || echo "Falling back to npx"
# 3. Authenticate — ONLY step where a human clicks oncefacilio login
# 4. Create the appfacilio vibe app create
# 5. Edit src/ to use createVibe(), executeAction(), etc.
# 6. Build and deploynpm run build && facilio vibe deploy

See Getting Started for the walkthrough with code, and Building with AI Agents for the full agent recipe.

On This Topic