Skip to main content

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

ComponentTechnologyJustification
Primary LanguagePython 3.12AI/ML ecosystem, async support
Secondary LanguageTypeScript 5.xFrontend, type safety
API FrameworkFastAPI 0.109+Async, OpenAPI, performance
FrontendReact 18 + ViteModern, performant, ecosystem
State ManagementZustandLightweight, TypeScript-first

1.2 Data Infrastructure

ComponentTechnologyConfiguration
OLTP DatabasePostgreSQL 16RLS, JSONB, partitioning
Time SeriesTimescaleDB 2.xHypertables, compression
Audit Databaseimmudb 1.9+Tamper-evident, verifiable
CacheRedis 7 ClusterSentinel, persistence
Vector Storepgvector 0.6+HNSW index
Message QueueApache Kafka 3.6KRaft mode

1.3 AI/ML Stack

ComponentTechnologyModel/Config
LLM RuntimevLLM 0.3+Tensor parallelism
Primary ModelDeepSeek-R1-32BQuantized INT8
Agent FrameworkLangGraph 0.1+PostgreSQL checkpointer
ForecastingNeuralProphet 0.8+GPU-accelerated
Anomaly DetectionPyOD 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

OperationTarget (p50)Target (p95)Target (p99)
API Read50ms150ms300ms
API Write100ms300ms500ms
Trial Balance500ms2s5s
Financial Statement1s3s8s
Reconciliation (1000 txns)5s15s30s
Forecast Generation10s30s60s
LLM Inference2s5s10s

6.2 Scaling Specifications

ComponentMin ReplicasMax ReplicasCPU/Memory
API Gateway210500m / 1Gi
GL Service3201 / 2Gi
Planning Service2101 / 2Gi
Reporting Service2152 / 4Gi
AI Orchestrator282 / 4Gi
LLM Service248 / 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

VersionDateAuthorChanges
1.02026-02-03ClaudeInitial TDD creation