Technical Design Document (TDD)
AI-First Open-Source FP&A Platform
Version: 1.0
Date: February 2026
Classification: CODITECT Engineering
1. Technology Stack Decisions
1.1 Runtime Environment
| Component | Technology | Justification |
|---|---|---|
| Primary Language | Python 3.12 | AI/ML ecosystem, async support |
| Secondary Language | TypeScript 5.x | Frontend, type safety |
| API Framework | FastAPI 0.109+ | Async, OpenAPI, performance |
| Frontend | React 18 + Vite | Modern, performant, ecosystem |
| State Management | Zustand | Lightweight, TypeScript-first |
1.2 Data Infrastructure
| Component | Technology | Configuration |
|---|---|---|
| OLTP Database | PostgreSQL 16 | RLS, JSONB, partitioning |
| Time Series | TimescaleDB 2.x | Hypertables, compression |
| Audit Database | immudb 1.9+ | Tamper-evident, verifiable |
| Cache | Redis 7 Cluster | Sentinel, persistence |
| Vector Store | pgvector 0.6+ | HNSW index |
| Message Queue | Apache Kafka 3.6 | KRaft mode |
1.3 AI/ML Stack
| Component | Technology | Model/Config |
|---|---|---|
| LLM Runtime | vLLM 0.3+ | Tensor parallelism |
| Primary Model | DeepSeek-R1-32B | Quantized INT8 |
| Agent Framework | LangGraph 0.1+ | PostgreSQL checkpointer |
| Forecasting | NeuralProphet 0.8+ | GPU-accelerated |
| Anomaly Detection | PyOD 1.1+ | Isolation Forest, ECOD |
2. API Specifications
2.1 REST API Standards
Base URL: https://api.fpa-platform.com/v1
Common Headers:
Authorization: Bearer <jwt_token>
X-Tenant-ID: <tenant_uuid>
X-Request-ID: <request_uuid>
X-Idempotency-Key: <idempotency_key>
Content-Type: application/json
Accept: application/json
Response Envelope:
{
"data": { },
"meta": {
"request_id": "uuid",
"timestamp": "ISO8601",
"pagination": {
"page": 1,
"per_page": 50,
"total": 1000,
"total_pages": 20
}
},
"errors": []
}
2.2 Core Endpoints
General Ledger API
# Accounts
GET /accounts # List accounts (paginated)
POST /accounts # Create account
GET /accounts/{id} # Get account details
PUT /accounts/{id} # Update account
DELETE /accounts/{id} # Soft delete account
GET /accounts/{id}/balances # Get account balances
# Journal Entries
GET /journal-entries # List entries (paginated)
POST /journal-entries # Create entry
GET /journal-entries/{id} # Get entry details
PUT /journal-entries/{id} # Update draft entry
DELETE /journal-entries/{id} # Delete draft entry
POST /journal-entries/{id}/post # Post entry
POST /journal-entries/{id}/reverse # Reverse entry
# Trial Balance
GET /trial-balance # Generate trial balance
GET /trial-balance/export # Export to Excel/CSV
# Financial Statements
GET /financial-statements/income-statement
GET /financial-statements/balance-sheet
GET /financial-statements/cash-flow
Planning API
# Budgets
GET /budgets # List budgets
POST /budgets # Create budget
GET /budgets/{id} # Get budget details
PUT /budgets/{id} # Update budget
POST /budgets/{id}/lock # Lock budget
GET /budgets/{id}/vs-actual # Budget vs actual
# Forecasts
GET /forecasts # List forecasts
POST /forecasts/generate # Generate AI forecast
GET /forecasts/{id} # Get forecast details
PUT /forecasts/{id} # Update forecast
GET /forecasts/{id}/accuracy # Forecast accuracy metrics
# Scenarios
GET /scenarios # List scenarios
POST /scenarios # Create scenario
GET /scenarios/{id} # Get scenario
PUT /scenarios/{id} # Update scenario
GET /scenarios/compare # Compare scenarios
AI Agent API
# Conversations
POST /agents/chat # Send message to orchestrator
GET /agents/conversations # List conversations
GET /agents/conversations/{id} # Get conversation history
# Tasks
POST /agents/tasks # Create async task
GET /agents/tasks/{id} # Get task status
GET /agents/tasks/{id}/result # Get task result
# Specific Agents
POST /agents/reconciliation/run # Run reconciliation
POST /agents/variance/analyze # Analyze variances
POST /agents/forecast/generate # Generate forecast
POST /agents/compliance/check # Run compliance check
2.3 WebSocket API
// Connection
const ws = new WebSocket('wss://api.fpa-platform.com/v1/ws');
// Authentication
ws.send(JSON.stringify({
type: 'auth',
token: 'jwt_token',
tenant_id: 'tenant_uuid'
}));
// Subscribe to events
ws.send(JSON.stringify({
type: 'subscribe',
channels: [
'journal_entries',
'reconciliation',
'forecasts',
'agents'
]
}));
// Event format
{
type: 'event',
channel: 'journal_entries',
event: 'journal_entry.posted',
data: { id: 'uuid', ... },
timestamp: 'ISO8601'
}
3. Database Schemas
3.1 Core Accounting Schema
-- ===========================================
-- CORE ACCOUNTING SCHEMA
-- ===========================================
-- Tenant Management
CREATE TABLE tenants (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255) NOT NULL,
slug VARCHAR(100) UNIQUE NOT NULL,
settings JSONB DEFAULT '{}',
subscription_tier VARCHAR(50) DEFAULT 'starter',
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
deleted_at TIMESTAMPTZ
);
-- Chart of Accounts
CREATE TABLE accounts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),
code VARCHAR(50) NOT NULL,
name VARCHAR(255) NOT NULL,
description TEXT,
account_type VARCHAR(20) NOT NULL CHECK (
account_type IN ('asset', 'liability', 'equity', 'revenue', 'expense')
),
account_subtype VARCHAR(50),
parent_id UUID REFERENCES accounts(id),
level INTEGER DEFAULT 1,
path LTREE,
is_active BOOLEAN DEFAULT true,
is_header BOOLEAN DEFAULT false,
currency_code VARCHAR(3) DEFAULT 'USD',
normal_balance VARCHAR(10) CHECK (normal_balance IN ('debit', 'credit')),
metadata JSONB DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
deleted_at TIMESTAMPTZ,
UNIQUE(tenant_id, code)
);
CREATE INDEX idx_accounts_tenant ON accounts(tenant_id) WHERE deleted_at IS NULL;
CREATE INDEX idx_accounts_path ON accounts USING GIST(path);
CREATE INDEX idx_accounts_type ON accounts(tenant_id, account_type);
-- Fiscal Periods
CREATE TABLE fiscal_periods (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),
year INTEGER NOT NULL,
period INTEGER NOT NULL CHECK (period BETWEEN 1 AND 13),
start_date DATE NOT NULL,
end_date DATE NOT NULL,
status VARCHAR(20) DEFAULT 'open' CHECK (
status IN ('open', 'soft_close', 'hard_close')
),
closed_at TIMESTAMPTZ,
closed_by UUID,
UNIQUE(tenant_id, year, period)
);
-- Journal Entries
CREATE TABLE journal_entries (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),
entry_number VARCHAR(50) NOT NULL,
entry_date DATE NOT NULL,
fiscal_period_id UUID REFERENCES fiscal_periods(id),
entry_type VARCHAR(30) DEFAULT 'standard' CHECK (
entry_type IN ('standard', 'adjusting', 'closing', 'reversing', 'recurring')
),
description TEXT,
reference VARCHAR(255),
source_system VARCHAR(100),
source_id VARCHAR(255),
status VARCHAR(20) DEFAULT 'draft' CHECK (
status IN ('draft', 'pending_approval', 'approved', 'posted', 'reversed')
),
posted_at TIMESTAMPTZ,
posted_by UUID,
reversed_by_id UUID REFERENCES journal_entries(id),
reversal_of_id UUID REFERENCES journal_entries(id),
total_debit DECIMAL(18,2) DEFAULT 0,
total_credit DECIMAL(18,2) DEFAULT 0,
currency_code VARCHAR(3) DEFAULT 'USD',
metadata JSONB DEFAULT '{}',
created_by UUID NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(tenant_id, entry_number)
);
CREATE INDEX idx_je_tenant_date ON journal_entries(tenant_id, entry_date);
CREATE INDEX idx_je_status ON journal_entries(tenant_id, status);
CREATE INDEX idx_je_source ON journal_entries(tenant_id, source_system, source_id);
-- Journal Lines
CREATE TABLE journal_lines (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
journal_entry_id UUID NOT NULL REFERENCES journal_entries(id) ON DELETE CASCADE,
line_number INTEGER NOT NULL,
account_id UUID NOT NULL REFERENCES accounts(id),
debit DECIMAL(18,2) DEFAULT 0 CHECK (debit >= 0),
credit DECIMAL(18,2) DEFAULT 0 CHECK (credit >= 0),
memo TEXT,
department_id UUID,
project_id UUID,
customer_id UUID,
vendor_id UUID,
dimensions JSONB DEFAULT '{}',
UNIQUE(journal_entry_id, line_number),
CHECK (debit > 0 OR credit > 0),
CHECK (NOT (debit > 0 AND credit > 0))
);
CREATE INDEX idx_jl_account ON journal_lines(account_id);
CREATE INDEX idx_jl_journal ON journal_lines(journal_entry_id);
-- Account Balances (Materialized)
CREATE TABLE account_balances (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),
account_id UUID NOT NULL REFERENCES accounts(id),
fiscal_period_id UUID NOT NULL REFERENCES fiscal_periods(id),
opening_balance DECIMAL(18,2) DEFAULT 0,
debit_total DECIMAL(18,2) DEFAULT 0,
credit_total DECIMAL(18,2) DEFAULT 0,
closing_balance DECIMAL(18,2) DEFAULT 0,
transaction_count INTEGER DEFAULT 0,
last_updated TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(tenant_id, account_id, fiscal_period_id)
);
-- Row Level Security
ALTER TABLE tenants ENABLE ROW LEVEL SECURITY;
ALTER TABLE accounts ENABLE ROW LEVEL SECURITY;
ALTER TABLE fiscal_periods ENABLE ROW LEVEL SECURITY;
ALTER TABLE journal_entries ENABLE ROW LEVEL SECURITY;
ALTER TABLE journal_lines ENABLE ROW LEVEL SECURITY;
ALTER TABLE account_balances ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON accounts
USING (tenant_id = current_setting('app.current_tenant')::UUID);
CREATE POLICY tenant_isolation ON journal_entries
USING (tenant_id = current_setting('app.current_tenant')::UUID);
3.2 Planning Schema
-- ===========================================
-- PLANNING SCHEMA
-- ===========================================
-- Budget Versions
CREATE TABLE budget_versions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),
name VARCHAR(255) NOT NULL,
fiscal_year INTEGER NOT NULL,
version_number INTEGER DEFAULT 1,
budget_type VARCHAR(30) CHECK (
budget_type IN ('annual', 'quarterly', 'rolling', 'zero_based')
),
status VARCHAR(20) DEFAULT 'draft' CHECK (
status IN ('draft', 'submitted', 'approved', 'locked', 'archived')
),
approved_at TIMESTAMPTZ,
approved_by UUID,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Budget Lines
CREATE TABLE budget_lines (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
budget_version_id UUID NOT NULL REFERENCES budget_versions(id),
account_id UUID NOT NULL REFERENCES accounts(id),
period_1 DECIMAL(18,2) DEFAULT 0,
period_2 DECIMAL(18,2) DEFAULT 0,
period_3 DECIMAL(18,2) DEFAULT 0,
period_4 DECIMAL(18,2) DEFAULT 0,
period_5 DECIMAL(18,2) DEFAULT 0,
period_6 DECIMAL(18,2) DEFAULT 0,
period_7 DECIMAL(18,2) DEFAULT 0,
period_8 DECIMAL(18,2) DEFAULT 0,
period_9 DECIMAL(18,2) DEFAULT 0,
period_10 DECIMAL(18,2) DEFAULT 0,
period_11 DECIMAL(18,2) DEFAULT 0,
period_12 DECIMAL(18,2) DEFAULT 0,
annual_total DECIMAL(18,2) GENERATED ALWAYS AS (
period_1 + period_2 + period_3 + period_4 + period_5 + period_6 +
period_7 + period_8 + period_9 + period_10 + period_11 + period_12
) STORED,
department_id UUID,
cost_center_id UUID,
notes TEXT,
UNIQUE(budget_version_id, account_id, department_id)
);
-- Forecasts
CREATE TABLE forecasts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),
name VARCHAR(255) NOT NULL,
forecast_type VARCHAR(30) CHECK (
forecast_type IN ('cash_flow', 'revenue', 'expense', 'balance_sheet')
),
horizon_weeks INTEGER DEFAULT 13,
method VARCHAR(50),
model_config JSONB DEFAULT '{}',
accuracy_metrics JSONB DEFAULT '{}',
generated_at TIMESTAMPTZ DEFAULT NOW(),
generated_by VARCHAR(50), -- 'user' or 'system'
status VARCHAR(20) DEFAULT 'active'
);
-- Forecast Values (TimescaleDB)
CREATE TABLE forecast_values (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
forecast_id UUID NOT NULL REFERENCES forecasts(id),
account_id UUID NOT NULL REFERENCES accounts(id),
forecast_date DATE NOT NULL,
value DECIMAL(18,2) NOT NULL,
lower_bound DECIMAL(18,2),
upper_bound DECIMAL(18,2),
confidence DECIMAL(5,4),
drivers JSONB DEFAULT '{}'
);
SELECT create_hypertable('forecast_values', 'forecast_date');
-- Scenarios
CREATE TABLE scenarios (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),
name VARCHAR(255) NOT NULL,
description TEXT,
base_budget_id UUID REFERENCES budget_versions(id),
scenario_type VARCHAR(30) CHECK (
scenario_type IN ('best_case', 'worst_case', 'most_likely', 'custom')
),
assumptions JSONB DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Scenario Adjustments
CREATE TABLE scenario_adjustments (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
scenario_id UUID NOT NULL REFERENCES scenarios(id),
account_id UUID NOT NULL REFERENCES accounts(id),
adjustment_type VARCHAR(20) CHECK (
adjustment_type IN ('percentage', 'absolute', 'driver')
),
adjustment_value DECIMAL(18,4),
driver_formula TEXT,
periods INTEGER[] -- Which periods to apply
);
3.3 Integration Schema
-- ===========================================
-- INTEGRATION SCHEMA
-- ===========================================
-- Integration Connections
CREATE TABLE integration_connections (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),
connection_name VARCHAR(255) NOT NULL,
connector_type VARCHAR(50) NOT NULL, -- 'quickbooks', 'netsuite', etc.
status VARCHAR(20) DEFAULT 'active' CHECK (
status IN ('active', 'paused', 'error', 'disconnected')
),
credentials_encrypted BYTEA,
config JSONB DEFAULT '{}',
last_sync_at TIMESTAMPTZ,
last_sync_status VARCHAR(20),
sync_frequency VARCHAR(20) DEFAULT 'daily',
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Account Mappings
CREATE TABLE account_mappings (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
connection_id UUID NOT NULL REFERENCES integration_connections(id),
external_account_id VARCHAR(255) NOT NULL,
external_account_code VARCHAR(100),
external_account_name VARCHAR(255),
internal_account_id UUID REFERENCES accounts(id),
mapping_status VARCHAR(20) DEFAULT 'unmapped' CHECK (
mapping_status IN ('unmapped', 'mapped', 'suggested', 'ignored')
),
confidence_score DECIMAL(5,4),
mapped_at TIMESTAMPTZ,
UNIQUE(connection_id, external_account_id)
);
-- Sync Logs
CREATE TABLE sync_logs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
connection_id UUID NOT NULL REFERENCES integration_connections(id),
sync_type VARCHAR(30) CHECK (
sync_type IN ('full', 'incremental', 'cdc')
),
started_at TIMESTAMPTZ NOT NULL,
completed_at TIMESTAMPTZ,
status VARCHAR(20) CHECK (
status IN ('running', 'completed', 'failed', 'cancelled')
),
records_processed INTEGER DEFAULT 0,
records_created INTEGER DEFAULT 0,
records_updated INTEGER DEFAULT 0,
records_failed INTEGER DEFAULT 0,
error_details JSONB DEFAULT '[]',
metadata JSONB DEFAULT '{}'
);
-- Transaction Staging
CREATE TABLE staged_transactions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
connection_id UUID NOT NULL REFERENCES integration_connections(id),
sync_log_id UUID REFERENCES sync_logs(id),
external_id VARCHAR(255) NOT NULL,
transaction_date DATE NOT NULL,
transaction_type VARCHAR(50),
amount DECIMAL(18,2),
currency_code VARCHAR(3),
description TEXT,
external_account_id VARCHAR(255),
raw_data JSONB NOT NULL,
processing_status VARCHAR(20) DEFAULT 'pending' CHECK (
processing_status IN ('pending', 'processed', 'error', 'duplicate')
),
journal_entry_id UUID REFERENCES journal_entries(id),
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_staged_status ON staged_transactions(connection_id, processing_status);
3.4 Audit Schema
-- ===========================================
-- AUDIT SCHEMA (PostgreSQL + immudb reference)
-- ===========================================
-- Audit Events (PostgreSQL - queryable)
CREATE TABLE audit_events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
event_timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(),
event_type VARCHAR(100) NOT NULL,
actor_id UUID,
actor_type VARCHAR(50), -- 'user', 'system', 'agent'
resource_type VARCHAR(100) NOT NULL,
resource_id UUID,
action VARCHAR(50) NOT NULL, -- 'create', 'update', 'delete', 'view', 'export'
old_values JSONB,
new_values JSONB,
changes JSONB, -- Computed diff
ip_address INET,
user_agent TEXT,
request_id UUID,
session_id UUID,
immudb_tx_id BIGINT, -- Reference to immudb for verification
metadata JSONB DEFAULT '{}'
);
-- Partition by month for performance
CREATE TABLE audit_events_2026_01 PARTITION OF audit_events
FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');
CREATE TABLE audit_events_2026_02 PARTITION OF audit_events
FOR VALUES FROM ('2026-02-01') TO ('2026-03-01');
-- Continue for future months...
CREATE INDEX idx_audit_tenant_time ON audit_events(tenant_id, event_timestamp DESC);
CREATE INDEX idx_audit_resource ON audit_events(tenant_id, resource_type, resource_id);
CREATE INDEX idx_audit_actor ON audit_events(tenant_id, actor_id);
-- Compliance Evidence
CREATE TABLE compliance_evidence (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
framework VARCHAR(50) NOT NULL, -- 'sox', 'soc2', 'hipaa'
control_id VARCHAR(100) NOT NULL,
control_name VARCHAR(255),
evidence_type VARCHAR(50), -- 'screenshot', 'log', 'report', 'attestation'
evidence_date DATE NOT NULL,
description TEXT,
file_path VARCHAR(500),
file_hash VARCHAR(64), -- SHA-256
collected_by UUID,
verified_by UUID,
verified_at TIMESTAMPTZ,
immudb_tx_id BIGINT,
metadata JSONB DEFAULT '{}'
);
4. Event Schemas
4.1 Domain Events
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"EventHeader": {
"type": "object",
"properties": {
"event_id": { "type": "string", "format": "uuid" },
"event_type": { "type": "string" },
"event_version": { "type": "string", "pattern": "^\\d+\\.\\d+$" },
"timestamp": { "type": "string", "format": "date-time" },
"tenant_id": { "type": "string", "format": "uuid" },
"correlation_id": { "type": "string", "format": "uuid" },
"causation_id": { "type": "string", "format": "uuid" },
"actor": {
"type": "object",
"properties": {
"id": { "type": "string", "format": "uuid" },
"type": { "enum": ["user", "system", "agent"] }
}
}
},
"required": ["event_id", "event_type", "timestamp", "tenant_id"]
},
"JournalEntryPosted": {
"allOf": [
{ "$ref": "#/definitions/EventHeader" },
{
"properties": {
"event_type": { "const": "journal_entry.posted" },
"payload": {
"type": "object",
"properties": {
"journal_entry_id": { "type": "string", "format": "uuid" },
"entry_number": { "type": "string" },
"entry_date": { "type": "string", "format": "date" },
"total_debit": { "type": "number" },
"total_credit": { "type": "number" },
"line_count": { "type": "integer" },
"accounts_affected": {
"type": "array",
"items": { "type": "string", "format": "uuid" }
}
},
"required": ["journal_entry_id", "entry_number", "entry_date"]
}
}
}
]
},
"ReconciliationCompleted": {
"allOf": [
{ "$ref": "#/definitions/EventHeader" },
{
"properties": {
"event_type": { "const": "reconciliation.completed" },
"payload": {
"type": "object",
"properties": {
"reconciliation_id": { "type": "string", "format": "uuid" },
"account_id": { "type": "string", "format": "uuid" },
"period_end_date": { "type": "string", "format": "date" },
"matched_count": { "type": "integer" },
"unmatched_count": { "type": "integer" },
"match_rate": { "type": "number" },
"exceptions": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": { "type": "string" },
"amount": { "type": "number" },
"description": { "type": "string" }
}
}
}
}
}
}
}
]
},
"ForecastGenerated": {
"allOf": [
{ "$ref": "#/definitions/EventHeader" },
{
"properties": {
"event_type": { "const": "forecast.generated" },
"payload": {
"type": "object",
"properties": {
"forecast_id": { "type": "string", "format": "uuid" },
"forecast_type": { "type": "string" },
"method": { "type": "string" },
"horizon_weeks": { "type": "integer" },
"accuracy_metrics": {
"type": "object",
"properties": {
"mape": { "type": "number" },
"rmse": { "type": "number" },
"mae": { "type": "number" }
}
},
"confidence_interval": { "type": "number" }
}
}
}
}
]
}
}
}
4.2 Integration Events
{
"SyncStarted": {
"event_type": "integration.sync_started",
"payload": {
"connection_id": "uuid",
"sync_log_id": "uuid",
"sync_type": "incremental",
"connector_type": "quickbooks"
}
},
"SyncCompleted": {
"event_type": "integration.sync_completed",
"payload": {
"connection_id": "uuid",
"sync_log_id": "uuid",
"records_processed": 1500,
"records_created": 45,
"records_updated": 12,
"duration_seconds": 127
}
},
"MappingSuggested": {
"event_type": "integration.mapping_suggested",
"payload": {
"connection_id": "uuid",
"external_account_id": "string",
"suggested_internal_account_id": "uuid",
"confidence_score": 0.87,
"reasoning": "Similar name and account type"
}
}
}
5. Configuration Specifications
5.1 Application Configuration
# config/application.yaml
app:
name: fpa-platform
version: 1.0.0
environment: ${ENV:development}
server:
host: 0.0.0.0
port: 8080
workers: ${WORKERS:4}
timeout_seconds: 30
database:
primary:
host: ${DB_HOST:localhost}
port: ${DB_PORT:5432}
name: ${DB_NAME:fpa_platform}
user: ${DB_USER:fpa}
password: ${DB_PASSWORD}
pool_size: ${DB_POOL_SIZE:20}
max_overflow: 10
ssl_mode: ${DB_SSL_MODE:prefer}
timescale:
host: ${TSDB_HOST:localhost}
port: ${TSDB_PORT:5432}
name: ${TSDB_NAME:fpa_timeseries}
redis:
url: ${REDIS_URL:redis://localhost:6379}
pool_size: 10
ttl_default: 3600
kafka:
brokers: ${KAFKA_BROKERS:localhost:9092}
consumer_group: fpa-platform
topics:
journal_entries: fpa.journal-entries
reconciliation: fpa.reconciliation
forecasts: fpa.forecasts
integration: fpa.integration
auth:
openfga:
api_url: ${OPENFGA_URL:http://localhost:8080}
store_id: ${OPENFGA_STORE_ID}
keycloak:
url: ${KEYCLOAK_URL:http://localhost:8081}
realm: ${KEYCLOAK_REALM:fpa}
ai:
llm:
provider: vllm
base_url: ${LLM_URL:http://localhost:8000/v1}
model: deepseek-ai/DeepSeek-R1-Distill-Qwen-32B
max_tokens: 4096
temperature: 0.1
embedding:
provider: sentence-transformers
model: all-MiniLM-L6-v2
dimension: 384
observability:
tracing:
enabled: true
exporter: otlp
endpoint: ${OTEL_ENDPOINT:http://localhost:4317}
metrics:
enabled: true
port: 9090
logging:
level: ${LOG_LEVEL:INFO}
format: json
5.2 LangGraph Workflow Configuration
# config/workflows/variance_analysis.yaml
workflow:
name: variance_analysis
version: "1.0"
description: "Automated Budget vs Actual variance analysis"
nodes:
- id: load_data
type: data_loader
config:
sources:
- type: budget
version: approved
- type: actuals
period: current
cache_ttl: 300
- id: calculate_variance
type: calculator
depends_on: [load_data]
config:
metrics:
- absolute_variance
- percentage_variance
- trend_variance
threshold_materiality: 0.05
- id: identify_drivers
type: analyzer
depends_on: [calculate_variance]
config:
algorithm: contribution_analysis
max_drivers: 5
include_dimensions: [department, project, customer]
- id: generate_commentary
type: nlg_generator
depends_on: [identify_drivers]
config:
model: deepseek-r1
template: variance_commentary
tone: executive
max_length: 500
- id: compliance_check
type: validator
depends_on: [generate_commentary]
config:
rules:
- no_pii
- factual_accuracy
- audit_trail
checkpoints:
enabled: true
store: postgresql
retention_days: 90
error_handling:
retry_attempts: 3
backoff_multiplier: 2
fallback_node: manual_review
5.3 OpenFGA Authorization Model
# config/auth/openfga_model.yaml
model:
schema: "1.1"
types:
- name: user
- name: organization
relations:
- name: member
types: [user]
- name: admin
types: [user]
- name: owner
types: [user]
- name: tenant
relations:
- name: organization
types: [organization]
- name: viewer
computed: member from organization
- name: editor
computed: admin from organization
- name: admin
computed: owner from organization
- name: account
relations:
- name: tenant
types: [tenant]
- name: can_view
computed: viewer from tenant
- name: can_edit
computed: editor from tenant
- name: journal_entry
relations:
- name: tenant
types: [tenant]
- name: author
types: [user]
- name: can_view
computed: can_view from tenant
- name: can_edit
union:
- author
- can_edit from tenant
- name: can_approve
difference:
base: admin from tenant
subtract: author
- name: can_post
computed: can_approve
- name: budget
relations:
- name: tenant
types: [tenant]
- name: owner
types: [user]
- name: can_view
computed: viewer from tenant
- name: can_edit
union:
- owner
- editor from tenant
- name: can_approve
computed: admin from tenant
- name: can_lock
computed: admin from tenant
- name: report
relations:
- name: tenant
types: [tenant]
- name: can_view
computed: viewer from tenant
- name: can_export
computed: editor from tenant
- name: can_schedule
computed: admin from tenant
6. Performance Specifications
6.1 Performance Targets
| Operation | Target (p50) | Target (p95) | Target (p99) |
|---|---|---|---|
| API Read | 50ms | 150ms | 300ms |
| API Write | 100ms | 300ms | 500ms |
| Trial Balance | 500ms | 2s | 5s |
| Financial Statement | 1s | 3s | 8s |
| Reconciliation (1000 txns) | 5s | 15s | 30s |
| Forecast Generation | 10s | 30s | 60s |
| LLM Inference | 2s | 5s | 10s |
6.2 Scaling Specifications
| Component | Min Replicas | Max Replicas | CPU/Memory |
|---|---|---|---|
| API Gateway | 2 | 10 | 500m / 1Gi |
| GL Service | 3 | 20 | 1 / 2Gi |
| Planning Service | 2 | 10 | 1 / 2Gi |
| Reporting Service | 2 | 15 | 2 / 4Gi |
| AI Orchestrator | 2 | 8 | 2 / 4Gi |
| LLM Service | 2 | 4 | 8 / 64Gi (GPU) |
6.3 Database Tuning
-- PostgreSQL configuration for FP&A workload
-- postgresql.conf
# Memory
shared_buffers = 8GB
effective_cache_size = 24GB
work_mem = 256MB
maintenance_work_mem = 2GB
# Parallelism
max_worker_processes = 16
max_parallel_workers_per_gather = 4
max_parallel_workers = 16
max_parallel_maintenance_workers = 4
# WAL
wal_level = logical
max_wal_size = 4GB
min_wal_size = 1GB
checkpoint_completion_target = 0.9
# Query Planning
random_page_cost = 1.1
effective_io_concurrency = 200
default_statistics_target = 500
# Connections
max_connections = 500
7. Appendices
Appendix A: Environment Variables
# Application
ENV=production
LOG_LEVEL=INFO
WORKERS=4
# Database
DB_HOST=postgres.internal
DB_PORT=5432
DB_NAME=fpa_platform
DB_USER=fpa_app
DB_PASSWORD=<secret>
DB_SSL_MODE=require
# Redis
REDIS_URL=redis://redis.internal:6379
# Kafka
KAFKA_BROKERS=kafka-1:9092,kafka-2:9092,kafka-3:9092
# Auth
OPENFGA_URL=http://openfga:8080
OPENFGA_STORE_ID=<store-id>
KEYCLOAK_URL=https://auth.fpa-platform.com
KEYCLOAK_REALM=fpa
# AI
LLM_URL=http://vllm:8000/v1
EMBEDDING_URL=http://embedding:8080
# Observability
OTEL_ENDPOINT=http://otel-collector:4317
Appendix B: Revision History
| Version | Date | Author | Changes |
|---|---|---|---|
| 1.0 | 2026-02-03 | Claude | Initial TDD creation |