Documentation

Install, authenticate, query. Everything ships as structured data you can pipe, filter, and script.

Quickstart #

1

Install

$ npm install -g @usefin/cli

# verify
$ usefin --version
@usefin/cli/1.7.3
2

Authenticate

# API key auth
$ usefin auth:login stripe --api-key sk_live_...

# or interactive mode
$ usefin auth:login stripe --interactive
3

Query

$ usefin stripe:invoices:list --status open --json
[
  { "id": "inv_4a8Bx", "customer": "cus_R1k9", "amount_due": 15000, "status": "open" },
  { "id": "inv_9zKm2", "customer": "cus_T4w7", "amount_due": 8400, "status": "open" }
]

$ usefin stripe:invoices:list --status open --json | jq '.[].amount_due'
15000
8400

$ usefin stripe:customers:list --csv > customers.csv
4

Verify

$ usefin doctor
  ✓  stripe        authenticated · reachable

Command grammar #

Every command follows one pattern. No provider-specific syntax to memorize.

usefin [provider]:[object]:[verb] [--flags]
usefin stripe:invoices:list List all Stripe invoices
usefin quickbooks:accounts:list List QuickBooks chart of accounts
usefin gusto:employees:list --json List Gusto employees as JSON
usefin ramp:expenses:list --csv List Ramp expenses as CSV
usefin carta:cap-table:get --json Get cap table from Carta as JSON

Standard verbs

listPaginated collection → stdout
getSingle record by ID → stdout
createCreate a resource, returns created object
updatePatch a resource by ID
deleteDelete a resource by ID
runLong-running job (e.g. payroll run)
syncPull remote state into local cache
exportSerialize to file (CSV, JSONL)

Authentication #

Credentials are encrypted with AES-256-GCM and stored locally. Never in plaintext.

# authenticate with an API key
$ usefin auth:login stripe --api-key sk_live_...
✓ Authenticated with Stripe via API key

# interactive mode (guided prompts)
$ usefin auth:login stripe --interactive

# check status of all providers
$ usefin auth:status
  ✓  stripe         authenticated · api key
  ✓  quickbooks     authenticated · oauth2
  ✗  gusto          token expired · run: usefin auth:rotate gusto

# rotate credentials
$ usefin auth:rotate stripe

# remove credentials
$ usefin auth:logout stripe

Utility commands #

Built-in commands for onboarding, diagnostics, and provider discovery.

# interactive setup wizard
$ usefin init

# list all supported providers
$ usefin providers
$ usefin providers --category "Payments" --json

# run diagnostics
$ usefin doctor

# deep diagnostics with API latency probes
$ usefin doctor --deep
  ✓  stripe          authenticated · reachable · api probe 142ms
  ✓  quickbooks      authenticated · reachable · api probe 238ms
  !  plaid           authenticated · unreachable

Provider coverage #

140 supported (GA) providers in the CLI today; full catalog below for reference. Same grammar, same flags, same output format. Real API when authenticated, mock fallback when not.

Multi-tenant / SaaS #

usefin supports multi-tenant SaaS platforms out of the box. Two modes: CLI-based config isolation and library-based injectable auth.

CLI mode — config dir per tenant

Set USEFIN_CONFIG_DIR to isolate credentials and audit logs per tenant. Each tenant gets its own AES-256-GCM encrypted credential store and independent audit log.

# each tenant gets isolated credentials + audit log
$ USEFIN_CONFIG_DIR=/data/tenants/tenant_123 usefin xero:invoices:list
$ USEFIN_CONFIG_DIR=/data/tenants/tenant_456 usefin quickbooks:bills:list

Library mode — injectable auth

Use runWithAuth() to scope authentication to a specific tenant within your Node.js application. All downstream getAuthManager() calls resolve the scoped instance via AsyncLocalStorage.

import { runWithAuth, AuthManager } from '@usefin/core';

// custom credential store backed by your DB
const store = new DbCredentialStore(tenantId);
const auth = new AuthManager(store);

await runWithAuth(auth, async () => {
  // all provider calls use this tenant's credentials
  const invoices = await xeroClient.list('invoices');
});

Environment variables

  • USEFIN_CONFIG_DIRCredentials + audit isolation
  • FIN_AUDIT_LOGOverride audit log path

Credential contract

  • InterfaceCredentialStore (sync)
  • Methodsget, set, remove, list
  • EncryptionAES-256-GCM per config dir
  • BackendsFile, DB, secrets manager

Tenant isolation guarantees

  • CredentialsSeparate encrypted stores
  • Audit logsPer-tenant log files
  • ConcurrencyAsyncLocalStorage scoping
  • CleanupresetAuthManager()

Architecture #

A TypeScript monorepo designed for isolation, extensibility, and security.

packages/
  core/                 @usefin/core - CLI framework
    src/
      commands/          auth/*, doctor
      lib/
        auth/            AES-256-GCM encrypted credential storage, multi-tenant (AsyncLocalStorage)
        errors/          Normalized FinError codes
        output/          JSON, CSV, table renderers
        filter-utils     Safe record filtering (prototype-pollution denylist)
        providers/       Plugin registry
    bin/run.js           CLI entry point
  plugin-stripe/       @usefin/plugin-stripe
    src/
      commands/stripe/
        customers/       list, get
        invoices/        list, get
        payments/        list, get
  plugin-accounting/   @usefin/plugin-accounting (42 providers, 25 base objects aligned with ApiDeck)
    src/
      lib/             Shared base, provider clients (Xero, QBO, NetSuite, Zoho, Sage, MYOB, FreeAgent, FreshBooks, Wave)
      schemas/
        base-objects     25 unified objects: accounts, invoices, bills, ..., ledger-accounts
      commands/
        xero/            39 objects (25 base + 14 extras: quotes, webhooks, aged-*...)
        quickbooks/      38 objects (25 base + 13 extras: estimates, webhooks, budgets...)
        netsuite/        25 base objects via SuiteQL + Record REST API
        d365-bc/         29 objects (25 base + credit-memos, employees, sales-orders, bank-txns)

Stack

  • LanguageTypeScript (ES2022)
  • Frameworkoclif v4
  • Monorepopnpm workspaces
  • TUI@clack/prompts
  • ValidationZod
  • CredentialsAES-256-GCM

Error codes

  • AUTH_REQUIREDNot authenticated
  • AUTH_EXPIREDToken expired
  • RATE_LIMITRate limited
  • NOT_FOUNDResource missing
  • VALIDATIONInvalid input
  • NETWORKConnection failed

Plugin contract

  • IsolationIndependent npm packages
  • VersioningSemver per provider
  • AuthShared credential store
  • OutputShared renderer
  • ErrorsNormalized FinError
  • TestingVitest per package

Security model #

fin handles credentials and financial data. Here's how it protects both.

Credential lifecycle

  • Encryption at restAES-256-GCM
  • StorageIsolated keychain per provider
  • Corrupt file recoveryGraceful fallback to empty store
  • OAuth tokensAutomatic rotation on expiry
  • OAuth timeout10-minute callback limit
  • API keysManual rotation via usefin auth:rotate
  • LogoutCredential wipe with usefin auth:logout

Transport security

  • ProtocolTLS 1.2+ enforced
  • ProxyNone — direct to provider API
  • Data persistenceNone — all output to stdout
  • TelemetryNone — no usage tracking
  • Verbose redactionAuth responses suppressed in logs
  • Token errorsOnly status + OAuth error fields

Least-privilege model

  • Default modeRead-only
  • PermissionsScoped per provider
  • Safety net--dry-run previews without executing
  • Input filteringPrototype pollution denylist
  • Create / update / deleteWhere the provider API supports it; use --data for payloads

Audit & compliance

  • Audit logStructured JSON via --audit
  • Per-tenant logsVia USEFIN_CONFIG_DIR
  • ReplayCommand replay from audit log
  • OutputDeterministic, pipeable formats
  • Visibility--verbose shows request/response headers