skills/quickbooks-auth/SKILL.md
QuickBooks OAuth adapter for QuickBooks Online accounting integration. Built on top of auth-provider for secure token management with automatic refresh, multi-profile support, sandbox/production toggle, and health checks.
npx skillsauth add ticruz38/skills quickbooks-authInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
QuickBooks OAuth 2.0 integration for accessing QuickBooks Online accounting API. This skill provides a simplified interface built on top of the auth-provider skill, handling authentication, token storage, automatic refresh, and health monitoring.
npm install
npm run build
The QuickBooks OAuth skill uses the auth-provider skill's configuration. Ensure you have set up the auth-provider environment:
# ~/.openclaw/.env
# QuickBooks OAuth credentials (from https://developer.intuit.com/)
QUICKBOOKS_CLIENT_ID=your-quickbooks-client-id
QUICKBOOKS_CLIENT_SECRET=your-quickbooks-client-secret
QUICKBOOKS_REDIRECT_URI=http://localhost:8080/auth/callback
# Optional: Default environment (sandbox or production)
QUICKBOOKS_ENVIRONMENT=sandbox
# Optional: Custom encryption key
AUTH_PROVIDER_KEY=your-encryption-key-min-32-chars
http://localhost:8080/auth/callback to the redirect URIs# Check default profile
node dist/cli.js status
# Check specific profile
node dist/cli.js status mycompany
# Connect to sandbox (default)
node dist/cli.js connect mycompany
# Connect to production
node dist/cli.js connect mycompany production
After opening the authorization URL and granting permission:
node dist/cli.js complete mycompany "AUTH_CODE" "STATE" "REALM_ID"
Note: The Realm ID is provided by QuickBooks in the callback URL after authorization.
node dist/cli.js profiles
# Check specific profile
node dist/cli.js health mycompany
# Check all profiles
node dist/cli.js health
# Disconnect specific profile
node dist/cli.js disconnect mycompany
# Disconnect all profiles
node dist/cli.js disconnect
node dist/cli.js env
import { QuickBooksAuthClient } from './index';
// Create client for default profile (sandbox)
const client = new QuickBooksAuthClient();
// Or for specific profile and environment
const productionClient = QuickBooksAuthClient.forProfile('mycompany', 'production');
const sandboxClient = QuickBooksAuthClient.forProfile('testcompany', 'sandbox');
// Step 1: Initiate authorization
const auth = client.initiateAuth();
console.log('Open this URL:', auth.url);
console.log('State:', auth.state); // Save for step 2
// Step 2: Complete with authorization code and realmId
const result = await client.completeAuth(code, state, realmId);
if (result.success) {
console.log('Connected!');
console.log('Realm ID:', result.realmId);
console.log('Environment:', result.environment);
} else {
console.error('Failed:', result.error);
}
if (await client.isConnected()) {
console.log('Connected!');
// Get realm ID
const realmId = await client.getRealmId();
console.log('Realm ID:', realmId);
// Get environment
const environment = await client.getEnvironment();
console.log('Environment:', environment);
} else {
console.log('Not connected');
}
// Gets valid token (auto-refreshes if needed)
const token = await client.getAccessToken();
// Use with QuickBooks API
const baseUrl = await client.getApiBaseUrl();
const response = await fetch(`${baseUrl}/${realmId}/companyinfo/${realmId}`, {
headers: {
'Authorization': `Bearer ${token}`,
'Accept': 'application/json'
}
});
// Convenient method for authenticated requests
const response = await client.fetch('/companyinfo/1234567890');
const data = await response.json();
const health = await client.healthCheck();
if (health.status === 'healthy') {
console.log('Token is valid');
console.log('Realm ID:', health.realmId);
console.log('Expires at:', new Date(health.expiresAt! * 1000));
} else {
console.warn('Token issue:', health.message);
}
// Disconnect specific profile
await client.disconnect();
// Or disconnect all profiles
import { disconnectAll } from './index';
await disconnectAll();
Manage multiple QuickBooks companies:
import { QuickBooksAuthClient, getConnectedProfiles } from './index';
// Different profiles for different companies
const companyA = QuickBooksAuthClient.forProfile('company-a', 'production');
const companyB = QuickBooksAuthClient.forProfile('company-b', 'sandbox');
const testCompany = QuickBooksAuthClient.forProfile('test', 'sandbox');
// Connect each
companyA.initiateAuth();
companyB.initiateAuth();
// List all connected profiles
const profiles = await getConnectedProfiles();
console.log('Connected profiles:', profiles);
// Check each profile's health
for (const profileName of profiles) {
const client = QuickBooksAuthClient.forProfile(profileName);
const health = await client.healthCheck();
const realmId = await client.getRealmId();
console.log(`${profileName} (${realmId}): ${health.status}`);
}
Use sandbox environment for development and testing:
# Connect to sandbox
node dist/cli.js connect mycompany sandbox
Sandbox API base URL: https://sandbox-quickbooks.api.intuit.com/v3/company
Use production environment for live data:
# Connect to production
node dist/cli.js connect mycompany production
Production API base URL: https://quickbooks.api.intuit.com/v3/company
const client = QuickBooksAuthClient.forProfile('mycompany');
const realmId = await client.getRealmId();
// Get company info
const response = await client.fetch(`/companyinfo/${realmId}`);
const data = await response.json();
console.log('Company:', data.CompanyInfo.CompanyName);
// List customers
const response = await client.fetch('/query?query=select * from Customer maxresults 10');
const customers = await response.json();
// List invoices
const response = await client.fetch('/query?query=select * from Invoice maxresults 10');
const invoices = await response.json();
const invoice = {
Line: [{
DetailType: 'SalesItemLineDetail',
Amount: 100.00,
SalesItemLineDetail: {
ItemRef: { value: '1', name: 'Services' }
}
}],
CustomerRef: { value: '1' }
};
const response = await client.fetch('/invoice', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(invoice)
});
try {
const token = await client.getAccessToken();
if (!token) {
console.log('Not authenticated');
return;
}
// Make API request
const response = await client.fetch('/companyinfo/1234567890');
if (!response.ok) {
if (response.status === 401) {
console.error('Token expired or invalid - will auto-refresh on next call');
} else if (response.status === 403) {
console.error('Permission denied - check QuickBooks subscription');
} else if (response.status === 404) {
console.error('Company or resource not found');
}
}
} catch (error) {
console.error('Request failed:', error.message);
}
Tokens are securely stored by the auth-provider skill in:
~/.openclaw/skills/auth-provider/credentials.db
All sensitive data is AES-256 encrypted. The Realm ID is stored in the token metadata.
# Type checking
npm run typecheck
# Build
npm run build
# Run CLI commands
npm run cli -- status
npm run cli -- connect mycompany
npm run status # shortcut
The user needs to connect their QuickBooks account:
node dist/cli.js connect mycompany
Ensure your environment variables are set:
node dist/cli.js env
The Realm ID is required and must be captured from the OAuth callback URL. When completing auth:
node dist/cli.js complete mycompany "CODE" "STATE" "REALM_ID"
Tokens auto-refresh when calling getAccessToken() or fetch(). If issues persist, check health:
node dist/cli.js health
Check your QuickBooks OAuth credentials in ~/.openclaw/.env:
# Verify auth-provider environment
node ../auth-provider/dist/cli.js env-check
@openclaw/auth-provider: For OAuth flow and token storagesqlite3: Database access (via auth-provider)testing
Suggest recipes based on dietary preferences, available ingredients, and cuisine preferences
development
Extract data from receipt photos using Google Vision API
business
QuickBooks Online integration for accounting sync - sync customers, invoices, and transactions with two-way sync and conflict resolution
development
Natural language to SQL query agent with schema discovery and XML result export. Use when an agent needs to (1) discover and interpret database schemas, (2) convert natural language questions into SQL queries using LLM, (3) execute queries securely, and (4) export results as XML to configurable storage (Google Drive, S3, local, etc.). Fully configurable via environment variables.