Page 1: System Overview & Functional Requirements
An automated Section 41 R&D Tax Credit calculator/pipeline represents a high-value corporate domain[cite: 1]. FinTech automation platforms in this space extract messy corporate records (payroll registers, Jira logs, and time-tracking files) and output audit-ready data structures using Python and SQL[cite: 1].
Data Pipeline High-Level Flow
[Raw HR/Payroll Data] & [Raw Jira/Time Logs]
│
▼
[Python ETL Engine] (IRS 4-Part Filter)
│
▼
[PostgreSQL/SQL Schema] (Transformations & ASC Calculations)
│
▼
[Form 6765 Audit Output]
Input Data Requirements
- HR / Payroll Data: Ingests CSV files containing
employee_id, first_name, last_name, job_title, department, w2_box1_wages, hire_date, and termination_date.
- Time-Tracking/Project Data: Ingests CSV or JSON logs containing
record_id, employee_id, project_name, task_description, hours_spent, and date_logged.
Page 2: Python ETL Logic & Qualification Engine
The processing engine applies qualifying tax logic to raw ledger data to isolate eligible hours using vectorized matching.
1. The IRS Four-Part Test Mapping Engine
import re
import pandas as pd
def apply_irs_four_part_test(time_logs_df: pd.DataFrame, task_column: str) -> pd.DataFrame:
"""Evaluates time logs for Section 41 R&D credit eligibility using string matching."""
PERMITTED_PURPOSE_PATTERN = re.compile(
r"\b(architecture|prototype|algorithm|refactor|machine\s*learning|ai|"
r"backend|compiler|database\s*schema|cryptography|api\s*design|"
r"proof\s*of\s*concept|poc|testing\s*automation|ci/cd|infrastructure|"
r"neural\s*network)\b", re.IGNORECASE
)
STATUTORY_EXCLUSIONS_PATTERN = re.compile(
r"\b(bug\s*fix|maintenance|marketing|sales|website\s*style|"
r"customer\s*support|wordpress|seo|content|social\s*media|internal\s*it|"
r"data\s*entry|administrative|meeting|overhead)\b", re.IGNORECASE
)
descriptions = time_logs_df[task_column].fillna("").astype(str)
is_permitted = descriptions.str.contains(PERMITTED_PURPOSE_PATTERN, regex=True)
is_excluded = descriptions.str.contains(STATUTORY_EXCLUSIONS_PATTERN, regex=True)
time_logs_df["is_qre_eligible"] = is_permitted & ~is_excluded
return time_logs_df
2. Substantially All (80/20) Allocation Engine
def calculate_w2_qre_allocations(processed_logs_df: pd.DataFrame, payroll_df: pd.DataFrame) -> pd.DataFrame:
"""Applies the IRS 'Substantially All' (80/20) rule to allocate W-2 wages into QREs."""
processed_logs_df["hours_spent"] = processed_logs_df["hours_spent"].astype(float).fillna(0.0)
hourly_summary = processed_logs_df.groupby("employee_id").agg(
total_hours=("hours_spent", "sum"),
qualified_hours=("hours_spent", lambda x: x[processed_logs_df.loc[x.index, "is_qre_eligible"]].sum())
).reset_index()
hourly_summary["rd_time_percentage"] = (hourly_summary["qualified_hours"] / hourly_summary["total_hours"]).fillna(0.0)
def apply_irs_statutory_thresholds(pct: float) -> float:
if pct >= 0.80:
return 1.00
if pct < 0.20:
return 0.00
return pct
hourly_summary["statutory_allocation_percentage"] = hourly_summary["rd_time_percentage"].apply(apply_irs_statutory_thresholds)
allocated_wages = pd.merge(payroll_df, hourly_summary, on="employee_id", how="inner")
return allocated_wages
Page 3: Star Schema Architecture & Storage Layer
The analytics layer uses a Star Schema design with a central Fact Table recording individual time logs, linked to surrounding dimension tables.
┌────────────────┐
│ dim_employees │
│────────────────│
│ PK employee_key│
└───────┬────────┘
│
┌──────────────┐ │ ┌──────────────┐
│ dim_projects │ │ │ dim_date │
│──────────────│ ▼ │──────────────│
│ PK project_key──►fact_qre_logs◄──PK date_key │
└──────────────┘ │ └──────────────┘
│
[hours_spent, qre_dollar]
Star Schema Table Specifications
| Table Name |
Type |
Key Columns |
Description |
fact_qre_logs |
Fact Table |
employee_key, project_key, date_key |
Stores daily hours logged, is_qre_eligible flag, and allocated_qre_amount. |
dim_employees |
Dimension |
employee_key (PK) |
Contains employee names, titles, departments, and w2_box1_wages. |
dim_projects |
Dimension |
project_key (PK) |
Stores project metadata, descriptions, and innovation classification. |
dim_date |
Dimension |
date_key (PK) |
Calendar breakdown for tax-year time-period querying. |
Page 4: Strategy, Market Viability & Pricing Models
Section 174 expense capitalization rules and Form 6765 changes have increased compliance demands. The platform builds a long-term asset by combining SaaS subscriptions, CPA referral revenue, and audit retainers.
Pricing Strategy Evaluation
| Monetization Model |
Legacy Concept Risk |
Recommended Strategic Adjustment |
| Pricing Model |
$250–$2,000 flat subscriptions underprice value compared to industry averages. |
Hybrid model: $99/mo base SaaS platform + $3,500 flat or 15% credit yield. |
| Go-To-Market (GTM) |
Direct B2B sales have high acquisition costs. |
Partner directly with CPA firms via white-label software distribution. |
| Substantiation Risk |
Static analysis lacks contemporaneous proof. |
Automate deep integrations with Git, Jira, and payroll systems for ongoing evidence extraction. |
3-Year Multi-Year Client Lifetime Value (LTV)
| Revenue Stream |
Year 1 |
Year 2 |
Year 3 |
Total 3-Yr Value |
| Tier 3 Data Tracking SaaS |
$2,000 |
$2,000 |
$2,000 |
$6,000 |
| CPA Referral / Participation |
$3,500 |
$3,500 |
$3,500 |
$10,500 |
Page 5: IRC § 7216 Compliance & CPA Agreements
Under IRC § 7216 and Treas. Reg. § 301.7216-3, providers processing tax information must obtain explicit standalone written consent before utilizing data for non-preparation purposes.
Statistical Compilation & 10-Entity Minimum Rule
Treas. Reg. § 301.7216-2(h) allows commercializing aggregated benchmarks if data is converted into statistical compilations. Every cohort or exported index must aggregate a minimum of 10 separate taxpayer entities to prevent individual re-identification.
Key Contractual Pillars (CPA Network Agreement)
- Liability Partitioning: Separates the data software platform from professional tax services provided by CPA partners.
- Database Retention: Platform provider retains ownership of system usage metadata and user profiles.
- Circular 230 / AICPA Compliance: Payments are structured as software licensing fees rather than fee-splitting arrangements.
Page 6: Tax Software Integration Architecture
Integrating R&D engines into CCH Axcess, UltraTax CS, and Drake Tax requires distinct approaches due to system architecture differences.
Integration Matrix
| Platform |
Integration Method |
Authentication / Protocol |
Workflow Highlights |
| CCH Axcess |
Native REST API (OIK Kit) |
OAuth 2.0 + Integrator Key |
POST JSON payload directly to Tax API endpoint for Form 6765 / Schedule G data. |
| UltraTax CS |
Structured File Exchange |
Pre-mapped .XLSX / .CSV |
Import via Utilities using custom column mapping (.MAP) files. |
| Drake Tax |
ASCII/CSV Field Import |
Drake Data Import Utility |
Generates custom CSVs mapped directly to Drake Screen 6765. |
Page 7: Lead Generation Schema & Prospecting Engine
To identify target non-claimant companies, the platform ingests external signals (LinkedIn, GitHub GraphQL, OpenCorporates) to estimate QREs and lost tax credits.
Prospect Database PostgreSQL Schema
CREATE TABLE rd_prospect_database (
prospect_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
company_name VARCHAR(255) NOT NULL,
ein_hash VARCHAR(64) UNIQUE, -- Anonymized identifier
naics_code VARCHAR(6) NOT NULL,
estimated_annual_revenue NUMERIC(15, 2),
us_headcount INT,
engineering_headcount INT,
est_qre_wages NUMERIC(15, 2),
est_unclaimed_fed_credit NUMERIC(15, 2), -- ~10% ASC Baseline
total_3yr_lost_opportunity NUMERIC(15, 2),
assigned_cpa_partner_id UUID,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Opportunity Estimator Script
def calculate_lost_rd_opportunity(
eng_headcount: int,
avg_eng_salary: float = 135000,
cloud_dev_spend: float = 50000
) -> dict:
"""Estimates 1-year and 3-year unclaimed Section 41 credits."""
qre_wages = (eng_headcount * avg_eng_salary) * 0.80
total_qre = qre_wages + cloud_dev_spend
fed_credit_annual = total_qre * 0.10
state_credit_annual = total_qre * 0.05
total_annual_lost = fed_credit_annual + state_credit_annual
return {
"annual_qre": round(total_qre, 2),
"total_annual_lost": round(total_annual_lost, 2),
"three_year_lost_opportunity": round(total_annual_lost * 3.0, 2)
}