Alchemy RecipeAdvancedautomation

Personal Finance Dashboard from Bank Statements and Investment Reports

Published

Your bank statements and investment reports arrive in different formats, different schedules, and different places. One month your broker sends a PDF, the next month it's a CSV file attached to an email. Your bank's statement comes as another PDF. By the time you sit down to understand your actual financial position, you're manually copying figures into a spreadsheet, cross-referencing accounts, and hoping you haven't missed anything. For more on this, see Investment pitch deck generation from financial statements.

Most people give up and just check their bank balance when they need to know where they stand. The rest spend Friday evenings doing manual reconciliation work that a computer could finish in minutes. Neither option is ideal.

This workflow automates the entire process: PDFs and statements flow in, get extracted and analysed by AI, then feed directly into a personalised dashboard that updates itself. You'll have a single source of truth for your net worth, spending patterns, and investment performance. No copy-paste. No manual calculations. No checking three different apps to answer a simple question about your financial health.

The Automated Workflow

Overall Architecture

The workflow chains four distinct operations together using an orchestration platform. Pick one: Zapier for simplicity, n8n for self-hosting, or Make for a balance between flexibility and ease of use. Claude Code also works well if you want a programmatic approach.

Here's what happens at each stage:

  1. Bank statements and investment reports arrive (email attachment, file upload, or cloud storage).
  2. ChatGPT-Writer extracts text and structured data from PDFs.
  3. Finster-AI analyses the extracted data, categorises transactions, and calculates financial metrics.
  4. Terrakotta-AI organises the results into a standardised database format.
  5. Text2Infographic generates a visual dashboard from the final dataset.

The entire process completes within 2-3 minutes of a file arriving.

Step 1: Trigger and File Ingestion

Start with a webhook trigger that listens for new files. Most orchestration tools let you create a unique webhook URL that receives POST requests when events occur.

In Zapier, you'd create a "Catch Raw Hook" step. In n8n, use a "Webhook" node. In Make, it's a "Custom Webhook" trigger.

Your banking app or email service sends a payload containing the file. Example structure:

{
  "event": "statement_uploaded",
  "filename": "bank_statement_2024_01.pdf",
  "file_url": "https://api.yourbank.com/statements/12345",
  "account_type": "checking",
  "date_period": "2024-01",
  "user_id": "user_456"
}

If you're pulling from email, use n8n's Gmail integration or Make's Email trigger instead. Zapier also has native Gmail support.

Step 2: Extract Text with ChatGPT-Writer

ChatGPT-Writer's API endpoint accepts document content and returns structured text. You'll need your API key from the ChatGPT-Writer dashboard.

The call looks like this:

curl -X POST https://api.chatgpt-writer.com/v1/extract \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "document_url": "https://api.yourbank.com/statements/12345",
    "document_type": "bank_statement",
    "extraction_fields": [
      "account_number",
      "statement_period",
      "opening_balance",
      "closing_balance",
      "transactions",
      "fees"
    ]
  }'

The response contains structured JSON:

{
  "account_number": "****5678",
  "statement_period": {
    "start": "2024-01-01",
    "end": "2024-01-31"
  },
  "opening_balance": 15000.50,
  "closing_balance": 18234.75,
  "transactions": [
    {
      "date": "2024-01-05",
      "description": "TESCO STORE #2341",
      "amount": -45.99,
      "type": "debit"
    },
    {
      "date": "2024-01-10",
      "description": "SALARY DEPOSIT",
      "amount": 3500.00,
      "type": "credit"
    }
  ],
  "fees": [
    {
      "description": "Monthly maintenance fee",
      "amount": -10.00
    }
  ]
}

In your orchestration tool, map the file_url from Step 1 into the document_url parameter. Store the returned JSON for the next step.

Step 3: Analyse with Finster-AI

Finster-AI categorises transactions, identifies spending patterns, and calculates financial metrics. Its API accepts the extracted data from ChatGPT-Writer.

curl -X POST https://api.finster.ai/v1/analyse \
  -H "Authorization: Bearer FINSTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "account_data": {
      "account_number": "****5678",
      "opening_balance": 15000.50,
      "closing_balance": 18234.75,
      "transactions": [
        {
          "date": "2024-01-05",
          "description": "TESCO STORE #2341",
          "amount": -45.99,
          "type": "debit"
        },
        {
          "date": "2024-01-10",
          "description": "SALARY DEPOSIT",
          "amount": 3500.00,
          "type": "credit"
        }
      ]
    },
    "analysis_options": {
      "categorise_transactions": true,
      "identify_recurring": true,
      "calculate_metrics": true
    }
  }'

Finster returns categorised data:

{
  "transactions_analysed": 47,
  "categorised_transactions": [
    {
      "date": "2024-01-05",
      "description": "TESCO STORE #2341",
      "amount": -45.99,
      "category": "groceries",
      "confidence": 0.98
    },
    {
      "date": "2024-01-10",
      "description": "SALARY DEPOSIT",
      "amount": 3500.00,
      "category": "income",
      "subcategory": "salary"
    }
  ],
  "metrics": {
    "total_income": 3500.00,
    "total_expenses": -1847.32,
    "net_change": 1652.68,
    "spending_by_category": {
      "groceries": -284.50,
      "dining": -156.75,
      "transport": -89.99,
      "utilities": -120.00,
      "other": -1196.08
    },
    "recurring_transactions": [
      {
        "description": "NETFLIX SUBSCRIPTION",
        "amount": -12.99,
        "frequency": "monthly",
        "next_date": "2024-02-10"
      }
    ]
  }
}

Pass this entire response to the next step.

Step 4: Standardise with Terrakotta-AI

Terrakotta-AI normalises the data into your chosen database schema. This is where you enforce consistency across multiple sources (bank statements, investment reports, credit cards).

curl -X POST https://api.terrakotta.ai/v1/normalise \
  -H "Authorization: Bearer TERRAKOTTA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "source_data": {
      "transactions_analysed": 47,
      "categorised_transactions": [
        {
          "date": "2024-01-05",
          "description": "TESCO STORE #2341",
          "amount": -45.99,
          "category": "groceries"
        }
      ],
      "metrics": {
        "total_income": 3500.00,
        "total_expenses": -1847.32
      }
    },
    "target_schema": "personal_finance_v2",
    "database_config": {
      "type": "postgresql",
      "connection_string": "postgresql://user:pass@localhost:5432/finance_db"
    }
  }'

Terrakotta inserts the normalised records directly into your database (or returns a structured payload if you prefer):

{
  "status": "success",
  "records_inserted": 47,
  "metrics_record_id": "metric_2024_01_005678",
  "database_tables_updated": [
    "transactions",
    "account_metrics",
    "spending_summary"
  ]
}

Step 5: Generate Dashboard with Text2Infographic

Text2Infographic converts your financial data into visual charts and graphs. Its API accepts the structured data and returns an HTML dashboard or image files.

curl -X POST https://api.text2infographic.com/v1/generate \
  -H "Authorization: Bearer TEXT2INFOGRAPHIC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "data": {
      "metrics": {
        "total_income": 3500.00,
        "total_expenses": 1847.32,
        "net_change": 1652.68,
        "spending_by_category": {
          "groceries": 284.50,
          "dining": 156.75,
          "transport": 89.99,
          "utilities": 120.00,
          "other": 1196.08
        }
      },
      "period": "2024-01"
    },
    "chart_types": [
      "pie_chart",
      "line_graph",
      "bar_chart"
    ],
    "output_format": "html",
    "theme": "dark"
  }'

Response includes the dashboard URL and embedded HTML:

{
  "status": "success",
  "dashboard_url": "https://dashboards.text2infographic.com/finance_2024_01_005678",
  "html_output": "<div class=\"dashboard\">...</div>",
  "charts": [
    {
      "type": "pie_chart",
      "title": "Spending by Category",
      "image_url": "https://cdn.text2infographic.com/chart_001.png"
    },
    {
      "type": "line_graph",
      "title": "Balance Over Time",
      "image_url": "https://cdn.text2infographic.com/chart_002.png"
    }
  ]
}

Store the dashboard_url in your database and send it to your email or Slack channel.

Complete n8n Workflow Example

Here's how it looks in n8n (closest to code):

  1. Webhook node receives the file notification.
  2. HTTP Request node calls ChatGPT-Writer API with the file_url.
  3. Data transformer node extracts the transaction array from the response.
  4. HTTP Request node calls Finster-AI API with the transactions.
  5. HTTP Request node calls Terrakotta-AI API to insert into database.
  6. HTTP Request node calls Text2Infographic API with the metrics.
  7. Email node sends the dashboard link to you.

Set each node's error handling to "Continue on error" so a single API timeout doesn't stop the entire workflow. Add retry logic: most orchestration tools support automatic retries with exponential backoff.

The Manual Alternative

If you prefer direct control over each step, run the API calls locally using Python or Node.js. This makes sense if you want to inspect data before it reaches your database, or if you need custom logic that the orchestration tools don't support.

Here's a Python example that chains the APIs:

import requests
import json

class FinanceDashboardBuilder:
    def __init__(self, api_keys):
        self.chatgpt_key = api_keys['chatgpt_writer']
        self.finster_key = api_keys['finster']
        self.terrakotta_key = api_keys['terrakotta']
        self.text2info_key = api_keys['text2infographic']
    
    def extract_statement(self, file_url):
        response = requests.post(
            'https://api.chatgpt-writer.com/v1/extract',
            headers={'Authorization': f'Bearer {self.chatgpt_key}'},
            json={
                'document_url': file_url,
                'document_type': 'bank_statement',
                'extraction_fields': [
                    'account_number', 'transactions', 'opening_balance', 'closing_balance'
                ]
            }
        )
        return response.json()
    
    def analyse_transactions(self, account_data):
        response = requests.post(
            'https://api.finster.ai/v1/analyse',
            headers={'Authorization': f'Bearer {self.finster_key}'},
            json={'account_data': account_data, 'analysis_options': {'categorise_transactions': True}}
        )
        return response.json()
    
    def normalise_data(self, analysis_result):
        response = requests.post(
            'https://api.terrakotta.ai/v1/normalise',
            headers={'Authorization': f'Bearer {self.terrakotta_key}'},
            json={
                'source_data': analysis_result,
                'target_schema': 'personal_finance_v2',
                'database_config': {'type': 'postgresql', 'connection_string': 'your_db_url'}
            }
        )
        return response.json()
    
    def generate_dashboard(self, metrics):
        response = requests.post(
            'https://api.text2infographic.com/v1/generate',
            headers={'Authorization': f'Bearer {self.text2info_key}'},
            json={
                'data': {'metrics': metrics},
                'chart_types': ['pie_chart', 'line_graph'],
                'output_format': 'html'
            }
        )
        return response.json()
    
    def run(self, file_url):
        print("Extracting statement...")
        extracted = self.extract_statement(file_url)
        
        print("Analysing transactions...")
        analysed = self.analyse_transactions(extracted)
        
        print("Normalising data...")
        normalised = self.normalise_data(analysed)
        
        print("Generating dashboard...")
        dashboard = self.generate_dashboard(analysed['metrics'])
        
        return {
            'extracted': extracted,
            'analysed': analysed,
            'normalised': normalised,
            'dashboard': dashboard
        }

keys = {
    'chatgpt_writer': 'your_key_here',
    'finster': 'your_key_here',
    'terrakotta': 'your_key_here',
    'text2infographic': 'your_key_here'
}

builder = FinanceDashboardBuilder(keys)
result = builder.run('https://api.yourbank.com/statements/12345')
print(json.dumps(result, indent=2))

Run this script on a schedule using cron (Linux/macOS) or Task Scheduler (Windows), or call it from your orchestration tool's HTTP node.

Pro Tips

1. Handle PDF Parsing Failures Gracefully

Not all PDFs parse perfectly. ChatGPT-Writer succeeds about 95% of the time, but investment reports with unusual layouts sometimes confuse it. Add a fallback step: if extraction confidence is below 0.80, send the original file to a human reviewer queue (Slack message with a link). Let people fix edge cases manually while automation handles the 95% of clean files.

{
  "extraction_confidence": 0.75,
  "fallback_action": "send_to_review",
  "reviewer_queue": "slack",
  "slack_channel": "#finance_review"
}

2. Rate Limits and Quota Management

Each API has rate limits. ChatGPT-Writer allows 100 requests per minute on most plans, Finster-AI allows 50 per minute, and Text2Infographic allows 20 per minute. If you process multiple statements in one day, you'll hit limits. Stagger requests using delays between API calls. n8n and Make both support built-in delay nodes. Zapier requires a separate Delay step. Add a 2-3 second delay between API calls to stay well under limits.

3. Deduplicate Recurring Transactions

If you upload the same statement twice (or overlapping statements), Finster-AI might categorise identical transactions separately in different runs. Terrakotta-AI can't always catch these duplicates. Add a deduplication step before inserting into your database: check if a transaction with the same amount, date, and description already exists within the last 30 days. If it does, skip insertion.

INSERT INTO transactions (date, description, amount, category, account_id)
VALUES (%s, %s, %s, %s, %s)
ON CONFLICT (account_id, date, amount, description) DO NOTHING;

4. Cost Optimisation

Text2Infographic charges per chart generated. If you're generating 10 charts per statement, that adds up. Generate charts only once monthly instead of after every statement upload. Store the raw metrics data and build the dashboard monthly using a scheduled job instead of event-driven processing.

5. Test with Sample Data First

Before connecting real bank statements, run the workflow with test files and mock API responses. Most of these tools provide sandbox environments or sample endpoints. Use them. You'll catch configuration errors before they process sensitive financial data.

Cost Breakdown

ToolPlan NeededMonthly CostNotes
ChatGPT-WriterPro£15Includes 10,000 document extractions per month. Higher tiers available for larger volumes.
Finster-AIBusiness£25Covers 5,000 transactions per month. Each additional 1,000 transactions costs £5.
Terrakotta-AIStandard£20Includes database normalisation and 100,000 record inserts per month. Overages at £0.01 per 100 records.
Text2InfographicTeams£30Generates 1,000 charts per month. Chart generation costs £0.03 per chart beyond included quota.
ZapierProfessional£49Alternatively: n8n (self-hosted, free) or Make (Free Plan includes 1,000 operations per month).
Total£139 (Zapier route) or £90 (n8n self-hosted)Includes all tools with comfortable usage headroom for 1-2 statements per week.

If you process fewer than 5 statements monthly, the Zapier Free Plan (100 tasks per month) may work, bringing your total to roughly £90 (just the four main tools, no orchestration cost). If you self-host n8n, orchestration is free, reducing your total to £90 per month.

More Recipes