Quickstart #
Install
$ npm install -g @usefin/cli # verify $ usefin --version @usefin/cli/1.7.3
Authenticate
# API key auth $ usefin auth:login stripe --api-key sk_live_... # or interactive mode $ usefin auth:login stripe --interactive
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
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 → stdoutgetSingle record by ID → stdoutcreateCreate a resource, returns created objectupdatePatch a resource by IDdeleteDelete a resource by IDrunLong-running job (e.g. payroll run)syncPull remote state into local cacheexportSerialize 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 isolationFIN_AUDIT_LOGOverride audit log path
Credential contract
- Interface
CredentialStore(sync) - Methods
get,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
- Cleanup
resetAuthManager()
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-runpreviews without executing - Input filteringPrototype pollution denylist
- Create / update / deleteWhere the provider API supports it; use
--datafor payloads
Audit & compliance
- Audit logStructured JSON via
--audit - Per-tenant logsVia
USEFIN_CONFIG_DIR - ReplayCommand replay from audit log
- OutputDeterministic, pipeable formats
- Visibility
--verboseshows request/response headers