Files · Agnostic AI Cost Control for QuickBooks Online SMBs
87 (1 binary, 598.0 kB total)attempt 1
README.md·5395 B·markdown
markdown
# Agnostic AI Cost Control for QuickBooks Online SMBs
> Monitor and cap AI spending across any LLM provider, then sync categorized costs directly into QuickBooks Online as operational expenses.
A tutorialized reference solution from [reaatech.com](https://reaatech.com), demonstrating how to build production-grade AI systems with the `@reaatech/*` package family.
## Problem
SMBs using multiple LLM providers (OpenAI, Anthropic, DeepSeek, Google, etc.) lack a unified view of their AI spend. Without central budget enforcement, costs can spiral out of control. This reference solution provides:
- Pre-flight budget checks before every LLM call
- Real-time OTel-span-based spend recording
- Daily QBO journal entry sync for accounting
## How It Works
1. **Budget pre-check** — Every LLM request passes through `BudgetService.check()` to verify the estimated cost fits within the remaining budget. If not, the request is blocked or downgraded.
2. **Real-time spend recording** — After each LLM call completes, `BudgetService.record()` writes the actual spend to the `SpendStore`. OpenTelemetry spans are also bridged via `SpanListener` for automatic recording.
3. **QuickBooks Online sync** — The `QboSyncService` aggregates spend entries since the last sync, creates a journal entry (debit by model/provider, credit total to cash), and posts it to QBO via the Intuit API.
## Quick Start
```bash
cp .env.example .env
# Fill in QBO credentials and database URL
pnpm install
pnpm dev
```
## API Reference
| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/api/budget/check` | Pre-flight budget check |
| `POST` | `/api/cost/record` | Record actual spend |
| `POST` | `/api/cost/sync` | Sync spend to QBO journal entry |
| `GET` | `/api/budget/status` | Get budget state for a scope |
| `GET` | `/api/spend/dashboard` | Get spend dashboard data |
### POST /api/budget/check
Request body:
```json
{
"scopeType": "user",
"scopeKey": "user-42",
"estimatedCost": 0.05,
"modelId": "openai/gpt-5.2"
}
```
Response (200):
```json
{ "allowed": true, "action": "Allow", "remaining": 99.95, "spent": 0.05 }
```
Response (403):
```json
{ "allowed": false, "action": "Blocked", "reason": "Hard cap reached" }
```
### POST /api/cost/record
Request body:
```json
{
"requestId": "req-abc",
"scopeType": "user",
"scopeKey": "user-42",
"cost": 0.045,
"inputTokens": 1500,
"outputTokens": 200,
"modelId": "openai/gpt-5.2",
"provider": "openai",
"timestamp": "2026-05-21T00:00:00Z"
}
```
Response (200):
```json
{ "recorded": true, "state": { "spent": 0.045, "remaining": 99.955, ... } }
```
### POST /api/cost/sync
Response (200):
```json
{ "synced": true, "journalEntryId": "JE-12345", "totalCost": 12.50 }
```
### GET /api/budget/status?scopeType=user&scopeKey=user-42
Response (200):
```json
{ "spent": 0.045, "remaining": 99.955, "limit": 100, "state": "Active" }
```
### GET /api/spend/dashboard?scopeType=user&scopeKey=*
Response (200):
```json
{ "totalSpend": 0.045, "rate": 0.009, "projection": 0.54, "recentModels": [], "spikeCount": 0 }
```
## Configuration
| Env Var | Description | Default |
|---------|-------------|---------|
| `QBO_CLIENT_ID` | QuickBooks OAuth 2.0 client ID | — |
| `QBO_CLIENT_SECRET` | QuickBooks OAuth 2.0 client secret | — |
| `QBO_REFRESH_TOKEN` | QuickBooks OAuth 2.0 refresh token | — |
| `QBO_COMPANY_ID` | QuickBooks Online company ID | — |
| `QBO_ENVIRONMENT` | `sandbox` or `production` | sandbox |
| `DATABASE_URL` | PostgreSQL connection string | — |
| `PORT` | Express server port | 3000 |
| `DEFAULT_BUDGET_LIMIT` | Default budget limit in USD | 100.0 |
| `DEFAULT_SOFT_CAP` | Soft cap threshold (0.0–1.0) | 0.8 |
| `DEFAULT_HARD_CAP` | Hard cap threshold (0.0–1.0) | 1.0 |
## QuickBooks Online Setup
1. Go to [developer.intuit.com](https://developer.intuit.com) and create an OAuth 2.0 app.
2. Set the redirect URI to `https://developer.intuit.com/v2/OAuth2Playground/RedirectUrl`.
3. Use the OAuth 2.0 Playground to generate a refresh token.
4. Map the journal entry accounts: "AI Expenses" (debit) = 5000, "Cash" (credit) = 1000.
5. Set the environment variables in `.env`.
## Architecture
- **BudgetController** (@reaatech/agent-budget-engine) — The governor that enforces budget limits, evaluates policies, and manages the per-scope state machine.
- **SpendStore** (@reaatech/agent-budget-spend-tracker) — In-memory circular buffer for O(1) spend lookups, rate calculations, projections, and spike detection.
- **SpanListener** (@reaatech/agent-budget-otel-bridge) — Bridges OpenTelemetry GenAI spans into budget spend entries automatically.
- **QboClient** — Handles OAuth 2.0 token management and posts journal entries to the QuickBooks Online API.
- **CostTrackingPricingProvider** — Maps model IDs to per-million-token costs for budget estimation.
## Project layout
```
app/ Next.js App Router pages + API routes
src/ services, lib, adapters
tests/ vitest suite (mirrors src/)
packages/ API references for every dependency (read these first)
DEV_PLAN.md build plan for this recipe
```
## Testing
```bash
pnpm test # vitest run with coverage
pnpm typecheck # tsc --noEmit
pnpm lint # eslint .
```
## License
MIT — see [LICENSE](./LICENSE).