Skill: josetseph/firefly-iii-published (version 1.0.5)

# Firefly III Client Skill ## Purpose A production-ready client for Firefly III personal finance management, allowing for programmatic access to transactions, accounts, recurring rules, and automation. ## Security Considerations See `SECURITY.md` for information on token safety, environment variables, and network security when using this skill. ## Setup 1. Ensure Firefly III is running and you have an OAuth Personal Access Token. 2. Set the following environment variables: ```bash FIREFLY_URL=http://your-instance-url FIREFLY_TOKEN=your_personal_access_token ``` 3. Install dependencies: ```bash pip install requests ``` ## CLI Reference The `api.py` script serves as a generic wrapper for the Firefly III REST API. - `python3 api.py request <METHOD> <ENDPOINT> [-d <JSON_DATA>]` ### Examples - **List Accounts:** `python3 api.py request GET api/v1/accounts` - **Create Transaction:** `python3 api.py request POST api/v1/transactions -d '{"amount": "10.00", "description": "Coffee", "source_id": 1, "destination_id": 2, "date": "2026-04-06", "type": "withdrawal"}'` - **Create Recurring Transaction:** `python3 api.py request POST api/v1/recurrences -d '{"type": "withdrawal", "title": "Example", "first_date": "2026-04-30", "repeat_freq": "monthly", "nr_of_repetitions": 12, "repetitions": [{"type": "monthly", "moment": "30"}], "transactions": [{"description": "Item", "amount": "100.00", "source_id": 1, "destination_id": 2}]}'` - **Create Rule:** `python3 api.py request POST api/v1/rules -d '{"title": "Example Rule", "trigger": "destination_account_is", "value": "Netflix", "action": "link_to_bill", "action_value": "Netflix Bill"}'`

path: SECURITY.md
content: # Security Considerations

This skill communicates with the Firefly III API using your personal OAuth token and URL. 

- **Token Safety**: Your token provides full programmatic access to your financial data. Keep it secure and avoid sharing it.
- **Environment Variables**: Never hardcode credentials in your script. Use secure environment variables as documented in the Setup section.
- **Network Access**: If you are using this skill in an environment where you are unsure of network security, consider using an isolated network or VPN.
- **Data Privacy**: All data processing happens on your local machine and your Firefly III instance. No data is shared with external services by this skill beyond your own Firefly III API.


path: manifest.json
content: {
  "name": "firefly-iii",
  "version": "1.0.3",
  "description": "API client for Firefly III personal finance management. View accounts and transactions.",
  "main": "src/api.py",
  "capabilities": [
    "network.external"
  ],
  "env": {
    "FIREFLY_URL": {
      "description": "URL of your Firefly III instance"
    },
    "FIREFLY_TOKEN": {
      "description": "Personal Access Token for Firefly III API"
    }
  },
  "dependencies": {
    "requests": "^2.31.0"
  },
  "keywords": ["api", "firefly-iii", "productivity"],
  "author": "Kris (OpenClaw Assistant)"
}

path: src/api.py
content: import os
import requests
import json
import sys
import argparse

class FireflyClient:
    def __init__(self):
        # Allow override via environment variables
        self.url = os.environ.get("FIREFLY_URL")
        self.[REDACTED_SECRET]"FIREFLY_TOKEN")
        
        # Validate configuration
        if not self.url or not self.[REDACTED_SECRET]"Error: FIREFLY_URL and FIREFLY_TOKEN must be set.", file=sys.stderr)
            sys.exit(1)
            
        self.headers = {
            "Authorization": f"Bearer {self.token}",
            "Accept": "application/vnd.api+json",
            "Content-Type": "application/json"
        }

    def request(self, method, endpoint, data=None):
        """Generic request method with error handling."""
        url = f"{self.url.rstrip('/')}/{endpoint.lstrip('/')}"
        
        # Safely handle JSON data
        try:
            json_data = json.loads(data) if isinstance(data, str) else data
        except json.JSONDecodeError as e:
            print(f"Error decoding JSON data: {e}", file=sys.stderr)
            sys.exit(1)
            
        try:
            response = requests.request(method, url, headers=self.headers, json=json_data)
            # Raise exception for 4xx/5xx errors
            response.raise_for_status()
            # Handle empty responses
            return response.json() if response.text.strip() else {}
        except requests.exceptions.HTTPError as e:
            # Provide detailed error info from API
            try:
                error_detail = response.json()
                print(f"API Error ({response.status_code}): {json.dumps(error_detail, indent=2)}", file=sys.stderr)
            except:
                print(f"HTTP Error: {e}", file=sys.stderr)
            sys.exit(1)
        except Exception as e:
            print(f"Request Error: {e}", file=sys.stderr)
            sys.exit(1)

if __name__ == "__main__":
    client = FireflyClient()
    parser = argparse.ArgumentParser(description="Firefly III Generic API Client")
    subparsers = parser.add_subparsers(dest="command")

    # Request command
    req_parser = subparsers.add_parser("request")
    req_parser.add_argument("method", choices=["GET", "POST", "PUT", "PATCH", "DELETE"])
    req_parser.add_argument("endpoint", help="API endpoint (e.g., api/v1/accounts)")
    req_parser.add_argument("-d", "--data", help="JSON data for POST/PUT/PATCH requests")

    args = parser.parse_args()

    if args.command == "request":
        result = client.request(args.method, args.endpoint, args.data)
        print(json.dumps(result, indent=2))
    else:
        parser.print_help()

Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)