LLM API Architecture & Billing System
Agencio Predict - Complete System, BYOK, Credits & Provider Documentation
3 Billing Modes | 4 LLM Providers | 19 LLM-Powered Features | AES-256-GCM Encryption
Table of Contents
BYOK (Bring Your Own Key)
1. System Overview
The LLM API system routes requests through a unified billing layer with three payment options and four provider backends.
flowchart TB
subgraph UserRequest["User/System Request"]
Feature["LLM-Powered Feature"]
end
subgraph BillingLayer["Billing Layer"]
Wrapper["llmCallWithBilling()"]
Resolver["resolveBillingContext()"]
Recorder["recordBilling()"]
end
subgraph BillingModes["Billing Modes"]
Subscription["Subscription Mode"]
Credits["Credits Mode"]
BYOK["BYOK Mode"]
end
subgraph Providers["LLM Providers"]
Router["Agencio LLMRouter"]
Anthropic["Anthropic Direct"]
Bedrock["Amazon Bedrock"]
UserKey["User's API Key"]
end
subgraph Models["Models"]
Sonnet["Claude Sonnet 4"]
Haiku["Claude Haiku 4.5"]
Opus["Claude Opus 4.5"]
end
Feature --> Wrapper
Wrapper --> Resolver
Resolver --> Subscription
Resolver --> Credits
Resolver --> BYOK
Subscription --> Router
Credits --> Router
BYOK --> UserKey
Router --> Anthropic
Router --> Bedrock
UserKey --> Anthropic
Anthropic --> Sonnet
Anthropic --> Haiku
Anthropic --> Opus
Wrapper --> Recorder
Key Components
Component
File
Purpose
llmCallWithBilling()
client-with-billing.ts
Main wrapper - reserves credits, injects BYOK keys, records usage
resolveBillingContext()
ai-billing-router.ts
Determines which billing mode and key to use
llmCall()
client.ts
Core LLM routing, provider selection, JSON extraction
byokService
byok-service.ts
BYOK key encryption, validation, storage
creditService
ai-credit-service.ts
Credit balance, reservations, transactions
2. Provider Routing Chain
The system uses a priority-based fallback chain to select the LLM provider.
flowchart TB
subgraph Resolution["Provider Resolution Order"]
Check1{"BYOK Key Provided?"}
Check2{"LLMRouter Configured?"}
Check3{"Anthropic Key in Env?"}
Check4{"Bedrock Enabled?"}
end
subgraph Providers["Provider Selection"]
BYOKProvider["Use BYOK Key (User's Account)"]
RouterProvider["Use LLMRouter (Platform Key)"]
AnthropicProvider["Use Anthropic Direct (Platform Key)"]
BedrockProvider["Use AWS Bedrock (IAM Auth)"]
Error["Error: No Provider"]
end
Check1 -->|"Yes"| BYOKProvider
Check1 -->|"No"| Check2
Check2 -->|"Yes"| RouterProvider
Check2 -->|"No"| Check3
Check3 -->|"Yes"| AnthropicProvider
Check3 -->|"No"| Check4
Check4 -->|"Yes"| BedrockProvider
Check4 -->|"No"| Error
BYOKProvider --> Result["Make LLM Call"]
RouterProvider --> Result
AnthropicProvider --> Result
BedrockProvider --> Result
Provider Configuration
Provider
Environment Variables
Priority
Notes
BYOK
User's encrypted key in DB
1 (Highest)
Skips platform routing entirely
LLMRouter
LLMROUTER_BASE_URL, LLMROUTER_API_KEY
2
Preferred for platform-billed calls
Anthropic Direct
CLAUDE_API_KEY or ANTHROPIC_API_KEY
3
Fallback when router unavailable
Bedrock
USE_BEDROCK=true, AWS_REGION
4
Uses IAM authentication
Model Tier Mapping
Tier Name
Anthropic Model
Bedrock Model
Use Case
opus
claude-sonnet-4-20250514
anthropic.claude-3-5-sonnet-v2
Complex reasoning, critiques, reports
haiku
claude-haiku-4-5-20251001
anthropic.claude-3-5-haiku-v1
Fast tasks, sentiment, validation
3. Three Billing Modes
Users can choose how they want to pay for LLM usage. The billing mode is orthogonal to subscription tier (features).
Subscription Mode
How it works: AI included in subscription plan. Monthly token allowance based on tier.
Cost tracking: Usage logged but not billed separately. Counts against plan limits.
Best for: Users with Pro/Enterprise subscriptions who want predictable costs.
Plan Token Limit AI Features
Free 10K/month Basic only
Pro 500K/month Full except autonomous
Enterprise 5M/month All features
Credits Mode
How it works: Pay-per-use with prepaid balance. Credits reserved before call, finalized after.
Cost tracking: Per-call cost calculation with transaction audit trail.
Best for: Users who want granular cost control or occasional heavy usage.
Feature Detail
Reserve-Finalize Credits locked before LLM call, actual cost deducted after
Auto-Refill Optional automatic top-up when balance low
Daily Limits Org-configurable daily spending caps
Fallback Falls back to subscription if credits exhausted
BYOK Mode (Bring Your Own Key)
How it works: User provides their own API key. Billed directly to their Anthropic/OpenAI account.
Cost tracking: Platform tracks usage stats only. No platform billing.
Best for: Power users with existing API contracts, enterprise users with negotiated rates.
Feature Detail
Encryption AES-256-GCM with per-user key derivation
Validation Key tested with provider API before storage
Providers Anthropic (active), OpenAI (planned)
Limits No platform limits - user's API quota applies
4. Billing Resolution Flow
Every LLM call goes through this flow to determine billing mode and key source.
sequenceDiagram
participant Feature as LLM Feature
participant Wrapper as llmCallWithBilling
participant Router as BillingRouter
participant Credit as CreditService
participant BYOK as BYOKService
participant LLM as LLM Client
participant Provider as API Provider
Feature->>Wrapper: Call with userId, messages
Wrapper->>Wrapper: estimateTokens(messages)
Wrapper->>Router: resolveBillingContext(userId, estimate)
Router->>Router: getUserBillingConfig(userId)
alt Mode = Subscription
Router->>Router: checkFeatureAccess()
Router-->>Wrapper: context {mode: subscription}
else Mode = Credits
Router->>Credit: estimateCost(model, tokens)
Router->>Credit: checkOrgLimits(userId, cost)
Router->>Credit: checkCredits(userId, cost)
Router->>Credit: reserveCredits(userId, cost)
Router-->>Wrapper: context {mode: credits, reservationId}
else Mode = BYOK
Router->>BYOK: getPreferredProvider(userId)
Router->>BYOK: getDecryptedKey(userId, provider)
Router-->>Wrapper: context {mode: byok, apiKey}
end
Wrapper->>LLM: llmCall(messages, context.apiKey?)
LLM->>Provider: POST /v1/messages
Provider-->>LLM: response {usage}
LLM-->>Wrapper: result
Wrapper->>Router: recordBilling(context, usage)
alt Credits Mode
Router->>Credit: finalizeUsage(reservationId, actualCost)
end
Wrapper-->>Feature: LLMCallResult
Error Handling
Error Type
When
Handling
InsufficientBillingError
Credits exhausted, no valid mode
Try subscription fallback, then throw
LLMError
Provider unavailable or network failure
Release credit reservation, throw
SchemaError
LLM output fails Zod validation
Retry with temperature 0.1, log rejection
5. BYOK Architecture
Bring Your Own Key allows users to use their own Anthropic/OpenAI API keys with full encryption.
flowchart TB
subgraph UserAction["User Actions"]
AddKey["Add API Key"]
UseFeature["Use LLM Feature"]
end
subgraph Storage["Secure Storage"]
Encrypt["encryptKey()"]
DB[("billing.user_byok_keys")]
Decrypt["decryptKey()"]
end
subgraph Encryption["AES-256-GCM"]
Derive["scrypt(secret, salt)"]
IV["Random IV (16 bytes)"]
Cipher["AES-256-GCM Cipher"]
Tag["Auth Tag (16 bytes)"]
end
subgraph LLMCall["LLM Call"]
Inject["Inject into Authorization"]
Provider["User's API Account"]
end
AddKey --> Encrypt
Encrypt --> Derive
Derive --> Cipher
IV --> Cipher
Cipher --> Tag
Cipher --> DB
UseFeature --> Decrypt
DB --> Decrypt
Decrypt --> Inject
Inject --> Provider
Key Storage Schema
billing.user_byok_keys
├── id UUID PRIMARY KEY
├── user_id UUID NOT NULL
├── provider VARCHAR(20) -- 'anthropic' | 'openai'
├── encrypted_key TEXT -- AES-256-GCM ciphertext (base64)
├── key_iv TEXT -- Initialization vector (hex)
├── key_tag TEXT -- Authentication tag (hex)
├── key_last_four VARCHAR(4) -- Display only ("...wxyz")
├── is_valid BOOLEAN -- Validation status
├── last_validated_at TIMESTAMPTZ
├── validation_error TEXT
├── total_calls INTEGER -- Usage counter
├── label VARCHAR(100) -- User-provided name
└── UNIQUE(user_id, provider) -- One key per provider per user
6. Key Encryption (AES-256-GCM)
BYOK keys are encrypted with per-user key derivation for defense in depth.
flowchart LR
subgraph Derivation["Key Derivation"]
Secret["BYOK_ENCRYPTION_SECRET"]
Salt["Salt: byok:{userId}"]
Scrypt["scrypt(secret, salt, 32)"]
DerivedKey["Derived Key (256-bit)"]
end
subgraph Encryption["Encryption"]
PlainKey["User's API Key"]
RandomIV["Random IV (16 bytes)"]
GCM["AES-256-GCM"]
Ciphertext["Encrypted (base64)"]
AuthTag["Auth Tag (16 bytes)"]
end
subgraph Storage["Database"]
Store[("encrypted_key key_iv key_tag")]
end
Secret --> Scrypt
Salt --> Scrypt
Scrypt --> DerivedKey
DerivedKey --> GCM
PlainKey --> GCM
RandomIV --> GCM
GCM --> Ciphertext
GCM --> AuthTag
Ciphertext --> Store
AuthTag --> Store
RandomIV --> Store
Security Properties
Encryption Strengths
Per-user key derivation - Compromise of one user's key doesn't affect others
Random IV per encryption - No IV reuse vulnerability
Auth tag validation - Detects tampering/corruption
Scrypt KDF - Resistant to brute-force attacks
Server-side only - Keys never sent to client
Decryption Flow
function decryptKey(encrypted, iv, tag, userId) {
// 1. Derive same key using scrypt
const derivedKey = scrypt(BYOK_ENCRYPTION_SECRET, `byok:${userId}`, 32);
// 2. Create decipher with IV
const decipher = createDecipheriv('aes-256-gcm', derivedKey, Buffer.from(iv, 'hex'));
// 3. Set auth tag (validates integrity)
decipher.setAuthTag(Buffer.from(tag, 'hex'));
// 4. Decrypt base64 ciphertext
let plaintext = decipher.update(encrypted, 'base64', 'utf8');
plaintext += decipher.final('utf8');
return plaintext; // User's API key
}
7. BYOK User Interface
Users manage their API keys at /settings/ai-billing/byok.
flowchart TB
subgraph SettingsPage["Settings > AI Billing"]
ModeSelector["Billing Mode Selector"]
SubscriptionCard["Subscription Status"]
CreditsCard["Credits Balance"]
BYOKCard["BYOK Keys"]
end
subgraph BYOKPage["BYOK Management"]
KeyList["API Key List"]
AddForm["Add New Key Form"]
Validation["Real-time Validation"]
end
subgraph KeyEntry["Key Entry UI"]
Provider["Provider Selector"]
KeyInput["API Key Input"]
Label["Optional Label"]
SaveBtn["Save & Validate"]
end
subgraph KeyDisplay["Key Display"]
ProviderIcon["Provider Icon"]
Last4["Last 4 Chars"]
Status["Valid/Invalid Badge"]
Actions["Validate | Delete"]
Stats["Total Calls | Last Used"]
end
ModeSelector --> BYOKCard
BYOKCard --> BYOKPage
BYOKPage --> KeyList
BYOKPage --> AddForm
AddForm --> KeyEntry
KeyList --> KeyDisplay
SaveBtn --> Validation
API Endpoints
Endpoint
Method
Purpose
/api/predict/v1/ai-billing/byok
GET
List user's BYOK keys (without plaintext)
/api/predict/v1/ai-billing/byok
POST
Add/update BYOK key (validates with provider)
/api/predict/v1/ai-billing/byok/:provider
DELETE
Remove BYOK key
/api/predict/v1/ai-billing/byok/:provider/validate
POST
Re-validate existing key
8. Key Validation
Keys are validated against provider APIs before storage and can be re-validated on demand.
flowchart TB
subgraph Input["User Input"]
Key["API Key (sk-ant-..."]
end
subgraph FormatCheck["Format Validation"]
AnthropicFormat{"Starts with sk-ant-?"}
OpenAIFormat{"Starts with sk-?"}
end
subgraph ProviderTest["Provider API Test"]
AnthropicTest["POST /v1/messages model: haiku, max_tokens: 1"]
OpenAITest["GET /v1/models"]
end
subgraph Response["Response Handling"]
Success["200: Valid"]
Auth401["401: Invalid Key"]
OtherError["Other: Assume Valid"]
end
subgraph Storage["Store Result"]
StoreValid["is_valid: true"]
StoreInvalid["is_valid: false validation_error: '...'"]
end
Key --> AnthropicFormat
Key --> OpenAIFormat
AnthropicFormat -->|"Yes"| AnthropicTest
OpenAIFormat -->|"Yes"| OpenAITest
AnthropicTest --> Success
AnthropicTest --> Auth401
AnthropicTest --> OtherError
OpenAITest --> Success
OpenAITest --> Auth401
Success --> StoreValid
Auth401 --> StoreInvalid
OtherError --> StoreValid
Validation Behavior
Network errors are treated as "valid" - If the provider API is unreachable, the key is assumed valid to avoid blocking users due to temporary outages. Keys can be re-validated later.
9. Credit System Flow
Credits use a reserve-finalize pattern to prevent overdrafts during concurrent calls.
sequenceDiagram
participant User
participant Feature as LLM Feature
participant Credit as CreditService
participant DB as Database
participant LLM as LLM Provider
User->>Feature: Request AI feature
Feature->>Credit: estimateCost(model, ~tokens)
Credit-->>Feature: estimatedCents: 15
Feature->>Credit: checkCredits(userId, 15)
Credit->>DB: SELECT available_cents, reserved_cents
DB-->>Credit: {available: 1000, reserved: 200}
Note over Credit: effective = 1000 - 200 = 800
Credit-->>Feature: sufficient: true
Feature->>Credit: reserveCredits(userId, 15, callId)
Credit->>DB: UPDATE reserved_cents += 15
Credit->>DB: INSERT transaction (type: reservation)
Credit-->>Feature: reservationId, newBalance: 785
Feature->>LLM: Make LLM call
LLM-->>Feature: response {usage: {input: 100, output: 50}}
Feature->>Credit: finalizeUsage(reservationId, actualCost: 12)
Credit->>DB: UPDATE reserved_cents -= 15
Credit->>DB: UPDATE available_cents -= 12
Credit->>DB: INSERT transaction (type: usage, -12)
Credit-->>Feature: success, newBalance: 788
Feature-->>User: AI response
Credit Transaction Types
Type
Amount
When
reservation
0 (tracking only)
Before LLM call - locks estimated cost
usage
Negative (debit)
After successful call - actual cost
release
Positive (credit)
On error - releases reservation
purchase
Positive (credit)
After Stripe payment
sync_adjustment
+/-
License server sync correction
10. Model Pricing
Pricing is stored in billing.ai_model_pricing and admin-configurable.
Model
Provider
Input (per 1M tokens)
Output (per 1M tokens)
Markup
claude-sonnet-4-20250514
Anthropic
$3.00
$15.00
0%
claude-haiku-4-5-20251001
Anthropic
$0.25
$1.25
0%
claude-opus-4-5
Anthropic
$15.00
$75.00
0%
gpt-4o
OpenAI
$2.50
$10.00
0%
gpt-4o-mini
OpenAI
$0.15
$0.60
0%
Cost Calculation Formula
inputCost = (inputTokens / 1,000,000) × inputCostPer1M
outputCost = (outputTokens / 1,000,000) × outputCostPer1M
baseCost = inputCost + outputCost
totalCost = baseCost × (1 + markupPercent / 100)
finalCents = Math.max(1, Math.ceil(totalCost × 100))
11. Usage Tracking
All LLM calls are logged to predict.ai_usage_log regardless of billing mode.
flowchart TB
subgraph AllModes["All Billing Modes"]
Sub["Subscription"]
Cred["Credits"]
BYOK["BYOK"]
end
subgraph UsageLog["predict.ai_usage_log"]
Fields["user_id, org_id model, provider billing_mode input_tokens, output_tokens estimated_cost_cents endpoint, created_at"]
end
subgraph CreditsOnly["Credits Mode Only"]
Transactions["billing.ai_credit_transactions"]
DailyUsage["billing.member_ai_daily_usage"]
end
subgraph BYOKOnly["BYOK Mode Only"]
KeyStats["user_byok_keys.total_calls++"]
end
Sub --> UsageLog
Cred --> UsageLog
BYOK --> UsageLog
Cred --> Transactions
Cred --> DailyUsage
BYOK --> KeyStats
Usage Query Examples
-- User's daily usage (last 30 days)
SELECT DATE(created_at) as date,
COUNT(*) as total_calls,
SUM(input_tokens + output_tokens) as total_tokens,
SUM(estimated_cost_cents) as total_cost_cents
FROM predict.ai_usage_log
WHERE user_id = $1 AND created_at >= NOW() - INTERVAL '30 days'
GROUP BY DATE(created_at)
ORDER BY date DESC;
-- Usage breakdown by billing mode
SELECT billing_mode, COUNT(*) as calls,
SUM(input_tokens + output_tokens) as tokens
FROM predict.ai_usage_log
WHERE created_at >= NOW() - INTERVAL '7 days'
GROUP BY billing_mode;
-- Model popularity
SELECT model, COUNT(*) as call_count
FROM predict.ai_usage_log
GROUP BY model ORDER BY call_count DESC;
12. Organization Spending Limits
Organization owners can set daily spending limits for members in credits mode.
flowchart TB
subgraph OrgLimits["billing.org_ai_limits"]
DefaultLimit["default_daily_limit_cents: $10"]
AllowOverride["allow_member_override: false"]
MaxOverride["max_member_daily_limit_cents: $50"]
MonthlyBudget["monthly_budget_cents: $1000"]
end
subgraph MemberUsage["billing.member_ai_daily_usage"]
TodayUsed["used_cents: $7"]
CustomLimit["custom_limit_cents: null"]
end
subgraph Check["checkOrgLimits()"]
GetLimits["Get org limits"]
GetUsage["Get today's usage"]
CalcEffective["effectiveLimit = allowOverride ? min(custom, max) : default"]
Compare["todayUsed + estimatedCost <= limit?"]
end
subgraph Result["Result"]
Allowed["allowed: true"]
Blocked["allowed: false reason: 'Daily limit reached'"]
end
OrgLimits --> GetLimits
MemberUsage --> GetUsage
GetLimits --> CalcEffective
GetUsage --> Compare
CalcEffective --> Compare
Compare -->|"Yes"| Allowed
Compare -->|"No"| Blocked
Limit Configuration
Setting
Default
Description
default_daily_limit_cents
1000 ($10)
Default daily limit for all members
allow_member_override
false
Can members request higher limits?
max_member_daily_limit_cents
5000 ($50)
Cap on member override requests
monthly_budget_cents
null
Org-wide monthly cap (advisory)
alert_at_percent
80
Send alert when budget % reached
13. LLM-Powered Features
Complete inventory of features that consume LLM API calls.
User-Initiated Features
Feature
Trigger
Model
Calls
Cost/Call
From English Translation
User creates algorithm
Haiku
1-3
~$0.03
Strategy Critique
User requests critique
Sonnet
2
~$0.12
LLM Jury (Modifications)
User proposes modification
Sonnet×2 + Haiku
3
~$0.22
AI Suggestions
User requests suggestions
Sonnet
1
~$0.08
Stock Hunter Scan
User initiates scan
Haiku/Sonnet
1 per symbol
$0.015-$0.18
Research Reports
User requests report
Sonnet
8 sections
~$0.80
AI Assistant Chat
User sends message
Sonnet
1 per turn
~$0.02
Automated Features (Platform Cost)
Feature
Trigger
Model
Frequency
Daily Cost
EOD Critic
Scheduler (nightly)
Sonnet
1 per live run/day
~$1 (50 runs)
Stock Hunter Discovery
Scheduler (configurable)
Haiku/Sonnet
100s of symbols
$1.50-$18
Sentiment Scoring
Scheduler (15 min)
Haiku
5000 calls/day budget
~$15
Entity Extraction
Social feed ingestion
Haiku
Per post batch
~$5
Black Swan Analysis
Detection trigger
Sonnet
Per alert
~$0.03/alert
Narrative Synthesis
Post-market
Sonnet
Daily (optional)
~$0.05
Marketing Briefs
Scheduler
Sonnet
Daily/weekly
~$0.18
14. Cost Breakdown
Estimated annual costs by feature category.
$68K
Trading Algorithm Ticks (if enabled, 50 algos)
$30-50K
Stock Hunter Discovery (100+ symbols/day)
$5.5K
Sentiment Scoring (5K calls/day)
$0.4-10K
EOD Critic (scales with live runs)
$1-2K
User-Initiated (reports, critiques)
$2K
Other Automated (narratives, briefs)
Cost Control Mechanisms
Available Controls
Stock Hunter Config (/admin/stock-hunter-config): Model tier, pre-filtering, caching, daily caps
Sentiment Budget : LLM_SENTIMENT_DAILY_BUDGET env var (default 5000)
Org Daily Limits : Per-member spending caps in credits mode
Rate Limits : 30 turns/user/hour for chat interfaces
BYOK Mode : Offloads cost to user's account
15. Platform vs BYOK Gaps
Critical differences and limitations between billing modes.
Key Limitation: Billing Mode ≠ Feature Access
billing_mode (how you pay) is ORTHOGONAL to subscription_tier (what you can do).
A Free-tier user with BYOK keys still cannot use Algorithm Builder. BYOK only changes payment source, not feature gates.
Feature Availability Matrix
Feature
Subscription
Credits
BYOK
Gate Type
Console Insights
Yes
Yes
Yes
None
Stock Hunter
Yes
Yes
Yes
None
AI Assistant Chat
Yes
Yes
Yes
Rate Limit
Algorithm Builder
Yes
Yes
Yes
Enterprise Tier
AI Modifications (Jury)
Yes
Yes
Yes
Enterprise Tier
Live Trading
Yes
Yes
Yes
Pro+ Tier + MFA
EOD Critic
Yes
N/A
N/A
Platform Cost
Mode-Specific Limitations
Subscription Limitations
Monthly token allowance (10K/500K/5M by tier)
Soft limits - warnings at 80%, 95%
No per-call cost visibility
Resets on billing cycle
Credits Limitations
Requires prepaid balance before use
Subject to org daily spending limits
Reservation overhead on each call
Falls back to subscription if credits exhausted
BYOK Limitations
No org spending controls - user's API quota only
No auto-refill - user manages their account
No cost tracking - platform only counts calls
Anthropic only - OpenAI support planned
No fallback - if key invalid, call fails
Identified Gaps
BYOK vs Platform Credits - Missing Features
Feature
Credits
BYOK
Impact
Daily spending limits
Yes
No
Org can't control BYOK user spend
Cost tracking
Yes
No
No platform cost visibility for BYOK
Auto-refill
Yes
No
User must manage own account
Fallback on error
Yes
No
Invalid BYOK key = call fails
OpenAI provider
Planned
Planned
Currently Anthropic only
Key rotation
N/A
No
No automated expiration/rotation
16. Database Schema
Key tables for LLM billing and usage tracking.
erDiagram
user_ai_billing ||--o| ai_credits : has
user_ai_billing ||--o{ user_byok_keys : has
ai_credits ||--o{ ai_credit_transactions : logs
users ||--o{ ai_usage_log : generates
organizations ||--o| org_ai_limits : configures
organizations ||--o{ member_ai_daily_usage : tracks
user_ai_billing {
uuid user_id PK
uuid organization_id FK
varchar billing_mode
varchar byok_provider
timestamp last_ai_call_at
bigint total_calls_count
}
ai_credits {
uuid user_id PK
bigint available_cents
bigint reserved_cents
boolean auto_refill_enabled
integer auto_refill_threshold_cents
timestamp last_sync_at
}
user_byok_keys {
uuid id PK
uuid user_id FK
varchar provider
text encrypted_key
text key_iv
text key_tag
varchar key_last_four
boolean is_valid
integer total_calls
}
ai_credit_transactions {
uuid id PK
uuid user_id FK
varchar type
integer amount_cents
bigint balance_after_cents
varchar model
integer prompt_tokens
varchar call_id UK
}
ai_usage_log {
uuid id PK
uuid user_id FK
varchar model
varchar billing_mode
integer input_tokens
integer output_tokens
integer estimated_cost_cents
varchar endpoint
}
org_ai_limits {
uuid organization_id PK
integer default_daily_limit_cents
boolean allow_member_override
integer max_member_daily_limit_cents
integer monthly_budget_cents
}
member_ai_daily_usage {
uuid id PK
uuid user_id FK
uuid organization_id FK
date usage_date
integer used_cents
integer call_count
}
ai_model_pricing {
varchar model PK
varchar provider
integer input_cost_per_1m_cents
integer output_cost_per_1m_cents
decimal markup_percent
boolean is_active
}
Key Files
File
Purpose
packages/be/src/algorithms/llm/client.ts
Core LLM routing, provider selection, JSON extraction
packages/be/src/algorithms/llm/client-with-billing.ts
Billing wrapper - reserves credits, injects BYOK, records usage
packages/be/src/billing/services/ai-billing-router.ts
Mode resolution, credit/subscription logic, recording
packages/be/src/billing/services/byok-service.ts
BYOK encryption, validation, storage (491 lines)
packages/be/src/billing/services/ai-credit-service.ts
Credit balance, reservations, transactions
db/migrations/147_ai_billing_credits.sql
Main billing schema
Environment Variables
Provider Configuration
Variable
Purpose
Required
LLMROUTER_BASE_URL
Agencio LLM Router endpoint
Optional (preferred)
LLMROUTER_API_KEY
Router authentication
If router configured
CLAUDE_API_KEY
Direct Anthropic API key
Fallback
ANTHROPIC_API_KEY
Alias for Claude key
Fallback
USE_BEDROCK
Enable AWS Bedrock
Optional
AWS_REGION
Bedrock region
If Bedrock enabled
Encryption & Billing
Variable
Purpose
Required
BYOK_ENCRYPTION_SECRET
Master key for BYOK encryption
Yes (for BYOK)
ENCRYPTION_SECRET
Fallback encryption key
Fallback
LLM_SENTIMENT_DAILY_BUDGET
Daily sentiment scoring limit
Optional (default 5000)