Skill: alexwoo-awso/ksef-accountant-en (version 2.2.6)

--- name: ksef-accountant-en description: "National e-Invoice System (KSeF) accounting assistant in English. Use when working with KSeF 2.0 API, FA(3) invoices, Polish VAT compliance, e-invoice processing, payment matching, VAT registers (JPK_V7), corrective invoices, split payment mechanism (MPP) or Polish accounting workflows. Provides domain knowledge for invoice issuance, purchase processing, expense classification, fraud detection and cash flow forecasting in the KSeF ecosystem." license: MIT homepage: https://github.com/alexwoo-awso/skills source: https://github.com/alexwoo-awso/skills/tree/main/ksef-accountant-en disableModelInvocation: true disable-model-invocation: true allowModelInvocation: false instruction_only: true has_executable_code: false credential_scope: "optional-user-provided" env: KSEF_TOKEN: description: "KSeF API token for session authentication. Provided by the user — the skill does not generate, store or transmit tokens. Configure ONLY after verifying that the platform enforces the disableModelInvocation flag (see Security Model section and skill.json)." required: false secret: true KSEF_ENCRYPTION_KEY: description: "Fernet encryption key for secure token storage. Usage is optional — an example of a security pattern described in the reference documentation. Configure ONLY after verifying that the platform enforces the disableModelInvocation flag." required: false secret: true KSEF_BASE_URL: description: "KSeF API base URL. Defaults to https://ksef-demo.mf.gov.pl (DEMO). Production: https://ksef.mf.gov.pl — requires explicit user consent. Use production ONLY after full platform security verification." required: false default: "https://ksef-demo.mf.gov.pl" --- # KSeF Accounting Agent Specialized knowledge for operating the National e-Invoice System (KSeF) in the KSeF 2.0 environment with the FA(3) structure. Supports accounting tasks related to electronic invoicing in Poland. ## Security Model This skill is **instruction-only** — it consists of Markdown files containing domain knowledge, architectural patterns and code examples. It does not contain any executable code, binaries, installation scripts or runtime dependencies. **Skill-side guarantees:** - `disableModelInvocation: true` / `disable-model-invocation: true` — declared in both the frontmatter metadata (both formats: camelCase and kebab-case) and in the dedicated manifest [`skill.json`](skill.json). The skill should not be invoked autonomously by the model. - `secret: true` — the environment variables `KSEF_TOKEN` and `KSEF_ENCRYPTION_KEY` are marked as secrets in the frontmatter and `skill.json`, signaling to the platform that they must be isolated and must not be logged or displayed. - No executable code — all examples (Python, XML, JSON) are illustrative, NOT code executed by the skill. - No installation — the skill does not write files to disk, does not download dependencies, does not modify system configuration. - Dedicated manifest [`skill.json`](skill.json) — a machine-readable file with security metadata, environment variable declarations and constraints. If the platform does not parse the SKILL.md frontmatter correctly, it should read metadata from `skill.json`. **NOTE — registry metadata verification before installation:** Security flags are declared in two sources: the SKILL.md frontmatter and [`skill.json`](skill.json). Nevertheless, the hosting platform may not read or enforce these flags. **Before installation you MUST perform the following steps:** 1. **Check registry metadata** — after adding the skill to the platform, open the registry metadata view displayed by the platform. Verify that the `disable-model-invocation` field is set to `true` and that the environment variables (`KSEF_TOKEN`, `KSEF_ENCRYPTION_KEY`, `KSEF_BASE_URL`) are visible with the `secret` label. If the platform shows `not set`, `false` or does not display these fields — the flags are NOT enforced. 2. **If registry metadata does not match frontmatter/skill.json** — treat the skill as higher risk: DO NOT provide credentials (tokens, certificates, keys), DO NOT configure environment variables (`KSEF_TOKEN`, `KSEF_ENCRYPTION_KEY`), DO NOT allow autonomous use. 3. **Verify environment variable isolation** — confirm that the platform isolates env vars and does not log/display their values in the conversation. 4. **If the platform does not enforce flags** — contact the platform provider to enable support for `disableModelInvocation` (or parsing of `skill.json`) or do not install the skill with access to any credentials. **Platform-dependent guarantees:** - Enforcement of the `disableModelInvocation` flag depends on the hosting platform. The frontmatter alone does not provide protection — it requires platform-side support. - Environment variable (env vars) isolation depends on the platform. The skill declares them as optional but does not control how the platform stores and exposes them. - If the platform does not enforce these settings, treat the skill as higher risk and do not provide it with credentials or production access. ## Constraints - **Knowledge only — no code execution** - Provides domain knowledge, architectural patterns and guidance. All code examples (including ML/AI) are educational and illustrative. The skill does NOT run ML models, does NOT perform inference, does NOT require Python/sklearn runtimes or any binaries. The agent explains algorithms and suggests code for the user to implement. - **Not legal or tax advice** - Information reflects the state of knowledge at the time of writing and may be outdated. Always recommend consulting a tax advisor before implementation. - **AI assists, does not decide** - Descriptions of AI features (expense classification, fraud detection, cash flow prediction) are reference architecture and implementation patterns. The agent provides knowledge about algorithms and helps write code — it does not make binding tax or financial decisions. - **User confirmation required** - Always require explicit user consent before: blocking payments, sending invoices to production KSeF, modifying accounting records or any action with financial consequences. - **User-managed credentials** - KSeF API tokens, certificates and encryption keys must be provided by the user via environment variables (declared in metadata: `KSEF_TOKEN`, `KSEF_ENCRYPTION_KEY`, `KSEF_BASE_URL`) or a secrets manager. The skill never stores, generates, transmits or implicitly requests credentials. **NEVER paste credentials (tokens, keys, certificates) directly in the conversation with the agent** — use environment variables or the platform's secrets manager. Vault/Fernet usage examples in the reference documentation are architectural patterns for user implementation. - **Use DEMO for testing** - Production (`https://ksef.mf.gov.pl`) issues legally binding invoices. Use DEMO (`https://ksef-demo.mf.gov.pl`) for development and testing. - **Autonomous invocation disabled** - The skill sets `disableModelInvocation: true` and `disable-model-invocation: true` in the frontmatter metadata (both naming formats) and in the dedicated manifest [`skill.json`](skill.json). This means the model should not invoke this skill autonomously — it requires explicit user action. **NOTE:** The frontmatter and `skill.json` are declarations — not guarantees. Enforcement depends on the platform. Before use, verify that the registry metadata displayed by the platform also shows `disable-model-invocation: true`. If the platform shows `not set` or `false`, the flag is not enforced and the skill may be invoked autonomously (see "Security Model" section above). ## Pre-installation Checklist Before installing the skill and configuring environment variables, perform the following steps: - [ ] Verify platform registry metadata — the `disable-model-invocation` field must show `true` - [ ] Verify that the platform has read env var declarations from the frontmatter or [`skill.json`](skill.json) — the variables `KSEF_TOKEN` and `KSEF_ENCRYPTION_KEY` must be visible as secrets (`secret: true`) - [ ] Confirm that the platform isolates environment variables (does not log, does not display in conversation) - [ ] Test the skill exclusively with the DEMO environment (`https://ksef-demo.mf.gov.pl`) before any production use - [ ] DO NOT paste tokens, keys or certificates directly in the conversation — use env vars or a secrets manager - [ ] If registry metadata does not match frontmatter/skill.json — DO NOT configure credentials and report the issue to the platform provider ## Core Competencies ### 1. KSeF 2.0 API Operations Issuing FA(3) invoices, downloading purchase invoices, managing sessions/tokens, handling Offline24 mode (emergency), downloading UPO (Official Acknowledgement of Receipt). Key endpoints: ```http POST /api/online/Session/InitToken # Session initialization POST /api/online/Invoice/Send # Send invoice GET /api/online/Invoice/Status/{ref} # Check status POST /api/online/Query/Invoice/Sync # Query purchase invoices ``` See [references/ksef-api-reference.md](references/ksef-api-reference.md) - full API documentation with authentication, error codes and rate limiting. ### 2. FA(3) Structure FA(3) vs FA(2) differences: invoice attachments, EMPLOYEE contractor type, extended bank account formats, 50,000 line item limit for corrections, JST and VAT group identifiers. See [references/ksef-fa3-examples.md](references/ksef-fa3-examples.md) - XML examples (basic invoice, multiple VAT rates, corrections, MPP, Offline24, attachments). ### 3. Accounting Workflows **Sales:** Data -> Generate FA(3) -> Send to KSeF -> Get KSeF number -> Post `Dr 300 (Receivables) | Cr 700 (Sales) + Cr 220 (Output VAT)` **Purchases:** Query KSeF -> Download XML -> AI Classification -> Post `Dr 400-500 (Expenses) + Dr 221 (VAT) | Cr 201 (Payables)` See [references/ksef-accounting-workflows.md](references/ksef-accounting-workflows.md) - detailed workflows with payment matching, MPP, corrections, VAT registers and month-end closing. ### 4. AI-Assisted Features (Reference Architecture) The descriptions below are implementation patterns and reference architecture. The skill does NOT run ML models — it provides knowledge about algorithms, helps design pipelines and write code for implementation in the user's system. Code examples in reference files (Python, sklearn, pandas) are illustrative pseudocode — the skill does not contain trained models, ML artifacts or executable files. - **Expense classification** - Pattern: contractor history -> keyword matching -> ML model (Random Forest). Flag for review if confidence < 0.8. - **Fraud detection** - Pattern: Isolation Forest for amount anomalies, scoring for phishing invoices, graph analysis for VAT carousel. - **Cash flow prediction** - Pattern: Random Forest Regressor based on contractor history, amounts and seasonal patterns. See [references/ksef-ai-features.md](references/ksef-ai-features.md) - conceptual algorithms and implementation patterns (require sklearn, pandas — not dependencies of this skill). ### 5. Compliance and Security (Implementation Patterns) The following are recommended security patterns for implementation in the user's system. The skill provides knowledge and code examples — it does not implement these mechanisms itself. - VAT White List verification before payments - Encrypted token storage (Fernet/Vault patterns — for user implementation) - Audit trail of all operations - 3-2-1 backup strategy - GDPR compliance (anonymization after retention period) - RBAC (role-based access control) See [references/ksef-security-compliance.md](references/ksef-security-compliance.md) - implementation patterns and security checklist. ### 6. Corrective Invoices Download original from KSeF -> Create FA(3) correction -> Link to original KSeF number -> Send to KSeF -> Post reversal or differential entry. ### 7. VAT Registers and JPK_V7 Generating sales/purchase registers (Excel/PDF), JPK_V7M (monthly), JPK_V7K (quarterly). ## Troubleshooting - Quick Help | Problem | Cause | Solution | |---------|-------|----------| | Invoice rejected (400/422) | Invalid XML, NIP, date, missing fields | Check UTF-8, validate FA(3) schema, verify NIP | | API timeout | KSeF outage, network, peak hours | Check KSeF status, retry with exponential backoff | | Cannot match payment | Amount mismatch, missing data, split payment | Extended search (+/-2%, +/-14 days), check MPP | See [references/ksef-troubleshooting.md](references/ksef-troubleshooting.md) - full troubleshooting guide. ## Reference Files Load depending on the task: | File | When to read | |------|-------------| | [skill.json](skill.json) | Metadata manifest — security flags, env var declarations, constraints. Source of truth for registries and scanners. | | [ksef-api-reference.md](references/ksef-api-reference.md) | KSeF API endpoints, authentication, sending/downloading invoices | | [ksef-legal-status.md](references/ksef-legal-status.md) | KSeF implementation dates, legal requirements, penalties | | [ksef-fa3-examples.md](references/ksef-fa3-examples.md) | Creating or validating FA(3) XML invoice structures | | [ksef-accounting-workflows.md](references/ksef-accounting-workflows.md) | Accounting entries, payment matching, MPP, corrections, VAT registers | | [ksef-ai-features.md](references/ksef-ai-features.md) | Expense classification, fraud detection, cash flow prediction algorithms | | [ksef-security-compliance.md](references/ksef-security-compliance.md) | VAT White List, token security, audit trail, GDPR, backup | | [ksef-troubleshooting.md](references/ksef-troubleshooting.md) | API errors, validation issues, performance | ## Official Resources - KSeF Portal: https://ksef.podatki.gov.pl - KSeF DEMO: https://ksef-demo.mf.gov.pl - KSeF Production: https://ksef.mf.gov.pl - VAT White List API: https://wl-api.mf.gov.pl - KSeF Latarnia (status): https://github.com/CIRFMF/ksef-latarnia

path: references/ksef-accounting-workflows.md
content: # KSeF Accounting Workflows

**NOTE:** Examples are illustrative and may require adaptation to the specifics of your business.

---

## Sales Invoice Workflow

### Complete Process

```mermaid
graph LR
    A[Invoice data] --> B[Generate FA3 XML]
    B --> C[Send to KSeF]
    C --> D[Wait for status]
    D --> E{Accepted?}
    E -->|Yes| F[Get KSeF number]
    E -->|No| G[Error analysis]
    F --> H[Accounting entry]
    H --> I[Generate PDF/email]
    G --> J[Fix XML]
    J --> B
```

### Accounting Entry

**Example: Service sales invoice**
```
FV/2026/02/0008 (KSeF: 1234567890-20260208-ABCDEF1234567890-12)
Sale date: 08.02.2026
Buyer: Klient Sp. z o.o. (NIP: 0987654321)

Dr 300 Receivables                   12,300.00 PLN
Cr 700 Service sales                 10,000.00 PLN
Cr 220 Output VAT                     2,300.00 PLN

Description: Programming services - 100 hrs @ 100 PLN
```

**Example: Goods sales invoice**
```
FV/2026/02/0009 (KSeF: 1234567890-20260209-BCDEFG2345678901-13)
Sale date: 09.02.2026

Dr 300 Receivables                   24,600.00 PLN
Cr 701 Goods sales                   20,000.00 PLN
Cr 220 Output VAT (23%)              4,600.00 PLN

Dr 701 Goods sales                   15,000.00 PLN
Cr 330 Cost of goods sold            15,000.00 PLN
(Inventory release)
```

---

## Purchase Invoice Workflow

### Complete Process

```mermaid
graph LR
    A[Cron: every 15 min] --> B[Query KSeF]
    B --> C{New invoices?}
    C -->|Yes| D[Download XML]
    C -->|No| A
    D --> E[Parse data]
    E --> F[AI expense classification]
    F --> G{Confidence > 0.8?}
    G -->|Yes| H[Auto-post]
    G -->|No| I[Flag for review]
    H --> J[Save to database]
    I --> J
```

### Accounting Entry

**Example: IT services purchase**
```
PURCHASE/123/2026 (KSeF: 9876543210-20260205-ZYXWVU9876543210-01)
Receipt date: 05.02.2026
Seller: IT Solutions Sp. z o.o. (NIP: 1122334455)
Category: 402 (IT services) - AI confidence: 0.95

Dr 402 IT services                    5,000.00 PLN
Dr 221 Input VAT (23%)               1,150.00 PLN
Cr 201 Payables                      6,150.00 PLN

Description: Hosting and technical support - January 2026
```

**Example: Office supplies purchase**
```
PURCHASE/124/2026 (KSeF: 9876543210-20260206-ZYXWVU9876543211-02)
Category: 502 (Office supplies) - AI confidence: 0.88

Dr 502 Office supplies                 500.00 PLN
Dr 221 Input VAT (23%)                 115.00 PLN
Cr 201 Payables                        615.00 PLN
```

---

## Payment Matching

### Scoring Algorithm

```python
def match_payment_to_invoice(payment, open_invoices):
    matches = []

    for invoice in open_invoices:
        score = 0

        # 1. Exact gross amount (+/- 0.01 PLN)
        if abs(payment.amount - invoice.total_gross) < 0.01:
            score += 40

        # 2. NIP in transfer title
        if invoice.seller_nip in payment.title.replace(' ', ''):
            score += 30

        # 3. Invoice number in title
        if invoice.number in payment.title:
            score += 20

        # 4. Date within range (payment due date +/- 7 days)
        days_diff = abs((payment.date - invoice.payment_due).days)
        if days_diff <= 7:
            score += 10 - days_diff

        # 5. KSeF number in title
        if invoice.ksef_number and invoice.ksef_number in payment.title:
            score += 25

        if score >= 70:
            matches.append({
                'invoice': invoice,
                'score': score,
                'confidence': score / 100
            })

    return sorted(matches, key=lambda x: x['score'], reverse=True)
```

### Accounting Entry - Payment

**Example: Purchase invoice payment**
```
Payment: 6,150.00 PLN
Title: "PURCHASE/123/2026 NIP 1122334455"
Date: 15.02.2026
Matched: PURCHASE/123/2026 (score: 90, confidence: 0.90)

Dr 201 Payables                      6,150.00 PLN
Cr 130 Current account              6,150.00 PLN

Description: Payment for invoice PURCHASE/123/2026
```

**Example: Sales invoice payment**
```
Receipt: 12,300.00 PLN
Title: "FV/2026/02/0008"
Date: 20.02.2026
Matched: FV/2026/02/0008 (score: 85)

Dr 130 Current account              12,300.00 PLN
Cr 300 Receivables                  12,300.00 PLN

Description: Payment received for FV/2026/02/0008
```

---

## Split Payment Mechanism (MPP)

### Conditions (per current regulations)
- Invoices >15,000 PLN gross
- Goods/services from Annex 15 to the VAT Act

### Implementation

```python
def handle_split_payment(invoice):
    if invoice.total_gross > 15000 and invoice.has_attachment_15_goods:
        net_payment = invoice.total_net
        vat_payment = invoice.total_vat

        # Transfer 1: Net portion
        bank_transfer_1 = {
            'to_account': invoice.seller_account,
            'amount': net_payment,
            'title': f'Invoice {invoice.number} (net portion)'
        }

        # Transfer 2: VAT to dedicated VAT account
        bank_transfer_2 = {
            'to_account': invoice.seller_vat_account,
            'amount': vat_payment,
            'title': f'Invoice {invoice.number} (VAT - split payment)'
        }

        return [bank_transfer_1, bank_transfer_2]
```

### Accounting Entry

**Example: MPP - net portion**
```
Dr 201 Payables                     20,000.00 PLN
Cr 130 Current account             20,000.00 PLN

Description: Net payment - MPP - PURCHASE/200/2026
```

**Example: MPP - VAT portion**
```
Dr 201 Payables                      4,600.00 PLN
Cr 130 Current account              4,600.00 PLN

Description: VAT payment to VAT account - MPP - PURCHASE/200/2026
```

---

## Corrective Invoices

### Correction Process

```mermaid
graph TD
    A[Need for correction] --> B[Download original from KSeF]
    B --> C[Create FA3 correction]
    C --> D[Link to original KSeF number]
    D --> E[Send to KSeF]
    E --> F[Receive correction KSeF number]
    F --> G{Posting method?}
    G -->|Reversal| H[Reversal + New entry]
    G -->|Differential| I[Difference only]
```

### Reversal Method

**Original:**
```
FV/2026/02/0005 (KSeF: 1234567890-20260205-ORIGINAL123456-12)
Amount: 12,300.00 PLN (net: 10,000.00, VAT: 2,300.00)

Dr 300 Receivables    12,300.00
Cr 700 Sales          10,000.00
Cr 220 Output VAT      2,300.00
```

**Reversal of original:**
```
FV/2026/02/0005/K01 (Correction - reversal)
Correction date: 08.02.2026
Reason: Error in unit price

Dr 700 Sales          10,000.00 (red storno)
Dr 220 Output VAT      2,300.00 (red storno)
Cr 300 Receivables    12,300.00 (red storno)
```

**New value:**
```
FV/2026/02/0005/K01 (Correction - new value)

Dr 300 Receivables     6,150.00
Cr 700 Sales           5,000.00
Cr 220 Output VAT      1,150.00
```

**Net effect:** Sales decreased by 5,000 PLN net, VAT by 1,150 PLN

---

### Differential Method

**Correction (difference only):**
```
FV/2026/02/0005/K01 (KSeF: 1234567890-20260208-CORRECT234567-13)
Difference: -6,150.00 PLN

Dr 700 Sales           5,000.00 (red storno)
Dr 220 Output VAT      1,150.00 (red storno)
Cr 300 Receivables     6,150.00 (red storno)
```

---

## VAT Registers

### Sales Register (Generation)

```python
def generate_sales_register(period_start, period_end):
    invoices = ksef_client.query_invoices(
        date_from=period_start,
        date_to=period_end,
        subject_type='subject1'  # seller
    )

    register = []
    for invoice in invoices:
        register.append({
            'lp': invoice.ordinal,
            'issue_date': invoice.issue_date,
            'sale_date': invoice.sale_date,
            'invoice_number': invoice.number,
            'ksef_number': invoice.ksef_number,
            'buyer_name': invoice.buyer_name,
            'buyer_nip': invoice.buyer_nip,
            'net_23': invoice.net_23,
            'vat_23': invoice.vat_23,
            'net_8': invoice.net_8,
            'vat_8': invoice.vat_8,
            'net_5': invoice.net_5,
            'vat_5': invoice.vat_5,
            'net_0': invoice.net_0,
            'net_exempt': invoice.net_exempt,
            'gross': invoice.total_gross
        })

    export_to_excel(register, f'Sales_register_{period_start}_{period_end}.xlsx')
    return register
```

### Purchase Register (Generation)

```python
def generate_purchase_register(period_start, period_end):
    invoices = ksef_client.query_invoices(
        date_from=period_start,
        date_to=period_end,
        subject_type='subject2'  # buyer
    )

    register = []
    for invoice in invoices:
        register.append({
            'lp': invoice.ordinal,
            'receipt_date': invoice.acquisition_date,
            'issue_date': invoice.issue_date,
            'invoice_number': invoice.number,
            'ksef_number': invoice.ksef_number,
            'seller_name': invoice.seller_name,
            'seller_nip': invoice.seller_nip,
            'category': invoice.category,  # AI classification
            'net': invoice.net_amount,
            'vat': invoice.vat_amount,
            'gross': invoice.total_gross
        })

    export_to_excel(register, f'Purchase_register_{period_start}_{period_end}.xlsx')
    return register
```

---

## Month-End Closing

### Checklist

**Before closing:**
- [ ] All sales invoices sent to KSeF
- [ ] All purchase invoices downloaded from KSeF
- [ ] All payments matched
- [ ] Corrections posted
- [ ] Sales register generated
- [ ] Purchase register generated
- [ ] Opening/closing balance of receivables/payables accounts
- [ ] JPK_V7M generated
- [ ] VAT-7 declaration prepared

**Month-end closing entries:**
```
# VAT settlement
Dr 220 Output VAT                   28,750.00
Cr 221 Input VAT                    17,250.00
Cr 225 VAT payable                  11,500.00

# VAT payment
Dr 225 VAT payable                  11,500.00
Cr 130 Current account             11,500.00
```

---

**See also:**
- [AI Features - Classification](ksef-ai-features.md#classification)
- [JPK_V7 Examples](ksef-jpk-examples.md)


path: references/ksef-ai-features.md
content: # AI Features for KSeF

**GENERAL NOTE:** All code in this document is **conceptual reference architecture** — implementation patterns for the user to adapt in their own system. This skill does NOT run ML models, does NOT perform inference and does NOT require Python, sklearn, pandas or any other runtime dependencies. The agent uses these patterns solely as a knowledge base for explaining algorithms, designing pipelines and helping write code.

**Dependencies required for implementation (NOT dependencies of this skill):** sklearn, pandas, numpy — to be installed by the user in their environment.

All AI/ML features are supportive in nature and require supervision by accounting staff. Performance metrics are design goals and may vary. AI systems do not make binding tax decisions.

---

## Expense Classification

### Algorithm (High-Level)

```python
def classify_expense(invoice_data):
    """
    Expense classification based on multiple data sources
    """
    features = {
        'seller_name': invoice_data.seller_name,
        'item_names': [item.name for item in invoice_data.items],
        'pkwiu_codes': [item.pkwiu for item in invoice_data.items if item.pkwiu],
        'total_amount': invoice_data.total_gross,
        'seller_nip': invoice_data.seller_nip
    }

    # 1. Contractor history (highest priority)
    historical = get_historical_category(features['seller_nip'])
    if historical and historical.confidence > 0.9:
        return historical.category, historical.confidence

    # 2. Keyword matching
    keyword_match = match_keywords(features['item_names'])
    if keyword_match and keyword_match.confidence > 0.85:
        return keyword_match.category, keyword_match.confidence

    # 3. ML model (Random Forest / Neural Network)
    ml_prediction = ml_model.predict(features)

    # 4. Flag for review if low confidence
    if ml_prediction.confidence < 0.8:
        flag_for_manual_review(invoice_data)

    return ml_prediction.category, ml_prediction.confidence
```

### Expense Categories (Examples)

```python
COST_CATEGORIES = {
    # External services
    400: "External services (general)",
    401: "Transport services",
    402: "IT services (hosting, development, IT support)",
    403: "Legal and advisory services",
    404: "Rental and lease services",
    405: "Marketing and advertising services",
    406: "Accounting services",
    407: "Consulting services",

    # Materials and raw materials
    500: "Materials and raw materials (general)",
    501: "Energy, water, fuel",
    502: "Office supplies",
    503: "Spare parts",

    # Other
    600: "Salaries and related costs",
    700: "Depreciation",
}
```

### Keywords (Examples)

```python
KEYWORDS = {
    402: ["hosting", "server", "cloud", "AWS", "Azure", "development",
          "programming", "IT support", "software", "license"],
    405: ["advertising", "marketing", "Google Ads", "Facebook Ads",
          "social media", "SEO", "content"],
    501: ["energy", "electricity", "gas", "water", "fuel", "gasoline"],
    502: ["paper", "pen", "toner", "office", "supplies"],
}
```

### ML Model Training

```python
from sklearn.ensemble import RandomForestClassifier
from sklearn.feature_extraction.text import TfidfVectorizer

class ExpenseClassifier:
    def __init__(self):
        self.model = RandomForestClassifier(n_estimators=200, random_state=42)
        self.vectorizer = TfidfVectorizer(max_features=500)

    def train(self, historical_invoices):
        """
        Training on historical data
        """
        # Prepare data
        X_text = [
            f"{inv.seller_name} {' '.join(inv.item_names)}"
            for inv in historical_invoices
        ]
        X_vectors = self.vectorizer.fit_transform(X_text)

        y = [inv.category for inv in historical_invoices]

        # Train model
        self.model.fit(X_vectors, y)

    def predict(self, invoice):
        """
        Category prediction
        """
        X_text = f"{invoice.seller_name} {' '.join(invoice.item_names)}"
        X_vector = self.vectorizer.transform([X_text])

        prediction = self.model.predict(X_vector)[0]
        probabilities = self.model.predict_proba(X_vector)[0]
        confidence = max(probabilities)

        return {
            'category': prediction,
            'confidence': confidence,
            'alternatives': self._get_alternatives(probabilities)
        }
```

---

## Anomaly and Fraud Detection

### Anomaly Detection (Isolation Forest)

```python
from sklearn.ensemble import IsolationForest
import numpy as np

class FraudDetector:
    def __init__(self):
        self.model = IsolationForest(contamination=0.05, random_state=42)
        self.is_trained = False

    def extract_features(self, invoice):
        """Feature extraction for analysis"""
        return np.array([
            invoice.total_gross,
            len(invoice.items),
            invoice.items_avg_price,
            invoice.payment_term_days,
            invoice.hour_of_day,  # 0-23
            int(invoice.is_weekend),  # 0 or 1
            invoice.seller_transaction_count,
            invoice.seller_avg_amount,
            invoice.amount_vs_avg_ratio  # current / average
        ])

    def train(self, historical_invoices):
        """Training on historical data"""
        features = [self.extract_features(inv) for inv in historical_invoices]
        self.model.fit(features)
        self.is_trained = True

    def detect(self, invoice):
        """Anomaly detection"""
        if not self.is_trained:
            return {'anomaly': False, 'reason': 'Model not trained'}

        features = self.extract_features(invoice)
        prediction = self.model.predict([features])[0]

        if prediction == -1:  # Anomaly
            return {
                'anomaly': True,
                'risk_level': 'HIGH',
                'reasons': self._analyze_reasons(invoice),
                'action': 'MANUAL_REVIEW_REQUIRED'
            }

        return {'anomaly': False}

    def _analyze_reasons(self, invoice):
        """Analyze anomaly reasons"""
        reasons = []

        if invoice.total_gross > invoice.seller_avg_amount * 3:
            reasons.append("Amount 3x greater than average from this seller")

        if invoice.is_weekend and invoice.hour_of_day < 6:
            reasons.append("Issued at night on weekend (unusual)")

        if invoice.seller_transaction_count == 1:
            reasons.append("First contact with this seller")

        if invoice.payment_term_days < 3:
            reasons.append("Very short payment term (possible phishing)")

        return reasons
```

### Phishing Invoice Detection

```python
def detect_phishing_invoice(invoice):
    """
    Detects potential phishing invoices
    """
    score = 0
    reasons = []

    # 1. Similar name to known contractor
    similar = find_similar_contractor_names(invoice.seller_name)
    for known_contractor in similar:
        if known_contractor.nip != invoice.seller_nip:
            score += 30
            reasons.append(f"Similar name to {known_contractor.name} but different NIP")

        if known_contractor.bank_account != invoice.bank_account:
            score += 40
            reasons.append("Different bank account than known contractor")

    # 2. Short payment term
    if invoice.payment_term_days <= 2:
        score += 20
        reasons.append("Very short payment term (typical for phishing)")

    # 3. First contact
    if get_transaction_count(invoice.seller_nip) == 0:
        score += 10
        reasons.append("First time from this seller")

    # 4. High amount on first contact
    if score > 0 and invoice.total_gross > 10000:
        score += 15
        reasons.append("High amount on first/suspicious contact")

    if score >= 50:
        return {
            'phishing_detected': True,
            'risk': 'CRITICAL',
            'score': score,
            'reasons': reasons,
            'action': 'BLOCK_PAYMENT_AND_VERIFY'
        }

    return {'phishing_detected': False}
```

### VAT Carousel Detection

```python
def detect_vat_carousel(invoices, time_window_days=30):
    """
    Detects potential VAT carousel patterns
    """
    # Build transaction graph
    graph = build_transaction_graph(invoices)

    # Look for cycles (A -> B -> C -> A)
    cycles = find_cycles(graph)

    suspicious = []
    for cycle in cycles:
        # Check suspicious characteristics
        if is_suspicious_cycle(cycle):
            suspicious.append({
                'cycle': cycle,
                'risk': 'CRITICAL',
                'participants': [node.nip for node in cycle],
                'total_value': sum(edge.amount for edge in cycle),
                'time_span_days': get_cycle_duration(cycle),
                'action': 'REPORT_TO_TAX_OFFICE'
            })

    return suspicious

def is_suspicious_cycle(cycle):
    """Is the cycle suspicious?"""
    # 1. Cycle closes within <30 days
    if get_cycle_duration(cycle) > 30:
        return False

    # 2. Similar amounts (+/-10%)
    amounts = [edge.amount for edge in cycle]
    if max(amounts) / min(amounts) > 1.1:
        return False

    # 3. Same goods/services
    items = [edge.item_description for edge in cycle]
    if not all_similar(items):
        return False

    return True
```

---

## Cash Flow Prediction

### Predictive Model

```python
from sklearn.ensemble import RandomForestRegressor
import pandas as pd

class CashFlowPredictor:
    def __init__(self):
        self.model = RandomForestRegressor(n_estimators=100, random_state=42)

    def prepare_training_data(self, historical_data):
        """
        Training data preparation
        DataFrame with columns:
        - invoice_due_date, invoice_amount, contractor_nip
        - payment_term_days, actual_payment_date, days_late
        """
        X = historical_data[[
            'invoice_amount',
            'payment_term_days',
            'contractor_avg_days_late',
            'contractor_payment_reliability',  # % on time
            'month',
            'is_end_of_quarter'
        ]]

        y = historical_data['days_late']

        return X, y

    def train(self, historical_data):
        X, y = self.prepare_training_data(historical_data)
        self.model.fit(X, y)

    def predict_payment_date(self, invoice):
        """Predict actual payment date"""
        contractor_stats = get_contractor_stats(invoice.buyer_nip)

        features = pd.DataFrame([{
            'invoice_amount': invoice.total_gross,
            'payment_term_days': invoice.payment_term_days,
            'contractor_avg_days_late': contractor_stats['avg_days_late'],
            'contractor_payment_reliability': contractor_stats['reliability'],
            'month': invoice.issue_date.month,
            'is_end_of_quarter': invoice.issue_date.month in [3, 6, 9, 12]
        }])

        predicted_days_late = self.model.predict(features)[0]
        predicted_date = invoice.payment_due_date + timedelta(days=int(predicted_days_late))

        return {
            'predicted_payment_date': predicted_date,
            'expected_days_late': int(predicted_days_late),
            'confidence': self._calculate_confidence(features)
        }

    def predict_monthly_cash_flow(self, year, month):
        """Monthly forecast"""
        # Sales invoices due in this month
        sales_invoices = get_invoices_due_in_month(year, month, type='sales')

        predicted_income = 0
        for invoice in sales_invoices:
            prediction = self.predict_payment_date(invoice)
            # Only if predicted payment is in this month
            if prediction['predicted_payment_date'].month == month:
                predicted_income += invoice.total_gross

        # Purchase invoices
        purchase_invoices = get_invoices_due_in_month(year, month, type='purchases')
        predicted_expenses = sum(inv.total_gross for inv in purchase_invoices)

        return {
            'month': f"{year}-{month:02d}",
            'predicted_income': predicted_income,
            'predicted_expenses': predicted_expenses,
            'net_cash_flow': predicted_income - predicted_expenses,
            'note': 'Prediction is an estimate'
        }
```

### Contractor Statistics

```python
def get_contractor_stats(nip):
    """
    Calculate payment statistics for a contractor
    """
    invoices = get_all_invoices_for_contractor(nip)

    if not invoices:
        return {
            'avg_days_late': 0,
            'reliability': 1.0,  # No history = optimistic assumption
            'total_invoices': 0
        }

    days_late_list = []
    paid_on_time = 0

    for inv in invoices:
        if inv.payment_date:
            days_late = (inv.payment_date - inv.payment_due_date).days
            days_late_list.append(max(0, days_late))  # Only positive

            if days_late <= 0:
                paid_on_time += 1

    return {
        'avg_days_late': sum(days_late_list) / len(days_late_list),
        'reliability': paid_on_time / len(invoices),
        'total_invoices': len(invoices),
        'max_days_late': max(days_late_list) if days_late_list else 0
    }
```

---

## AI Best Practices

### 1. Continuous Learning

```python
def retrain_models_monthly():
    """
    Retrain models monthly on fresh data
    """
    # Get data from last 12 months
    end_date = datetime.now()
    start_date = end_date - timedelta(days=365)

    historical_data = get_invoices(start_date, end_date)

    # Retrain expense classifier
    expense_classifier.train(historical_data)

    # Retrain anomaly detector
    fraud_detector.train(historical_data)

    # Retrain cash flow predictor
    cash_flow_predictor.train(historical_data)

    save_models()  # Save to disk
```

### 2. Human-in-the-Loop

```python
def classify_with_review(invoice):
    """
    Classification with flagging for review
    """
    prediction = expense_classifier.predict(invoice)

    if prediction['confidence'] < 0.8:
        # Low confidence -> human review
        task = create_review_task(
            invoice=invoice,
            suggested_category=prediction['category'],
            confidence=prediction['confidence'],
            alternatives=prediction['alternatives']
        )
        return {
            'category': None,  # Wait for review
            'status': 'PENDING_REVIEW',
            'task_id': task.id
        }

    # High confidence -> auto-classify
    return {
        'category': prediction['category'],
        'status': 'AUTO_CLASSIFIED',
        'confidence': prediction['confidence']
    }
```

### 3. Audit Trail for AI

```python
def log_ai_decision(invoice, prediction, action):
    """
    Log AI decisions for audit
    """
    ai_audit_log.insert({
        'timestamp': datetime.now(),
        'invoice_id': invoice.id,
        'model_name': 'ExpenseClassifier',
        'model_version': '2.1',
        'prediction': prediction,
        'confidence': prediction['confidence'],
        'action_taken': action,
        'reviewed_by_human': action == 'MANUAL_REVIEW'
    })
```

---

**Final warning:** All AI features require regular monitoring, validation and supervision by qualified staff. Do not rely solely on automated decisions in tax and accounting matters.

**Reminder:** The code examples above are reference architecture. This skill does not contain trained models, ML artifacts or executable files. Implementation requires installing dependencies (sklearn, pandas, numpy) and training models on user data in their own environment.


path: references/ksef-api-reference.md
content: # KSeF API 2.0 - Reference

**NOTE:** Actual endpoints and formats may change. Always refer to the official KSeF API documentation.

---

## Authentication

### 1. Session Initialization (Token)

**Endpoint:**
```http
POST /api/online/Session/InitToken
Content-Type: application/json
```

**Request:**
```json
{
  "context": {
    "token": "YOUR_KSEF_TOKEN_HERE"
  }
}
```

**Response (200 OK):**
```json
{
  "referenceNumber": "20260208-SE-1234567890AB-CD",
  "timestamp": "2026-02-08T23:40:00.000Z",
  "sessionToken": {
    "token": "SESSION_TOKEN_VALUE",
    "validity": "2026-02-09T00:10:00.000Z"
  }
}
```

**Token validity:** Typically 30 minutes

---

### 2. Session Initialization (Certificate)

**Endpoint:**
```http
POST /api/online/Session/InitSigned
Content-Type: application/octet-stream
```

**Request:** Signed XML Session Request (CAdES)

---

### 3. Session Status

**Endpoint:**
```http
GET /api/online/Session/Status/{ReferenceNumber}
[REDACTED_SECRET] {token}
```

**Response (200 OK):**
```json
{
  "referenceNumber": "20260208-SE-1234567890AB-CD",
  "timestamp": "2026-02-08T23:45:00.000Z",
  "processingCode": 200,
  "processingDescription": "Session active"
}
```

---

### 4. Session Termination

**Endpoint:**
```http
DELETE /api/online/Session/Terminate
[REDACTED_SECRET] {token}
```

**Response (200 OK):**
```json
{
  "referenceNumber": "20260208-ST-1234567890AB-CD",
  "timestamp": "2026-02-08T23:50:00.000Z",
  "processingCode": 200,
  "processingDescription": "Session terminated"
}
```

---

## Sending Invoices

### 5. Send Invoice

**Endpoint:**
```http
POST /api/online/Invoice/Send
[REDACTED_SECRET] {token}
Content-Type: application/octet-stream
```

**Request Body:** FA(3) XML Content (raw bytes)

**Response (200 OK - Accepted for processing):**
```json
{
  "referenceNumber": "20260208-IV-1234567890AB-CD",
  "timestamp": "2026-02-08T23:41:00.000Z",
  "processingCode": 200,
  "processingDescription": "Invoice has been accepted for processing"
}
```

---

### 6. Invoice Status

**Endpoint:**
```http
GET /api/online/Invoice/Status/{InvoiceElementReferenceNumber}
[REDACTED_SECRET] {token}
```

**Possible statuses:**
- `200` - Processing in progress
- `202` - Invoice accepted
- `400` - Invoice rejected (validation errors)

**Response (202 - Accepted):**
```json
{
  "referenceNumber": "20260208-IV-1234567890AB-CD",
  "timestamp": "2026-02-08T23:41:30.000Z",
  "processingCode": 202,
  "ksefReferenceNumber": "1234567890-20260208-ABCDEF1234567890-12",
  "invoiceNumber": "FV/2026/02/0008",
  "acquisitionTimestamp": "2026-02-08T23:41:25.000Z"
}
```

**Response (400 - Rejected):**
```json
{
  "referenceNumber": "20260208-IV-1234567890AB-CD",
  "timestamp": "2026-02-08T23:41:15.000Z",
  "processingCode": 400,
  "exception": {
    "exceptionDetailList": [
      {
        "exceptionCode": "101",
        "exceptionDescription": "XSD schema validation error"
      }
    ]
  }
}
```

---

### 7. Downloading UPO

**Endpoint:**
```http
GET /api/online/Invoice/Upo/{KsefReferenceNumber}
[REDACTED_SECRET] {token}
Accept: application/xml
```

**Response (200 OK):** XML with Official Acknowledgement of Receipt

---

## Downloading Invoices

### 8. Invoice Search (Synchronous)

**Endpoint:**
```http
POST /api/online/Query/Invoice/Sync
[REDACTED_SECRET] {token}
Content-Type: application/json
```

**Request (purchase invoices, date range):**
```json
{
  "queryCriteria": {
    "type": "range",
    "invoicingDateFrom": "2026-02-01",
    "invoicingDateTo": "2026-02-08",
    "subjectType": "subject2"
  },
  "pageSize": 100,
  "pageOffset": 0
}
```

**QueryCriteria - types:**
- `subjectType: "subject1"` - sales invoices (as seller)
- `subjectType: "subject2"` - purchase invoices (as buyer)

**Response (200 OK):**
```json
{
  "referenceNumber": "20260208-QS-1234567890AB-CD",
  "timestamp": "2026-02-08T23:42:00.000Z",
  "invoiceHeaderList": [
    {
      "ksefReferenceNumber": "9876543210-20260205-ZYXWVU9876543210-01",
      "invoiceNumber": "PURCHASE/123/2026",
      "acquisitionTimestamp": "2026-02-05T10:30:00.000Z",
      "netAmount": "5000.00",
      "vatAmount": "1150.00",
      "grossAmount": "6150.00",
      "currencyCode": "PLN"
    }
  ],
  "numberOfElements": 1,
  "pageSize": 100,
  "pageOffset": 0,
  "totalPages": 1,
  "totalElements": 1
}
```

---

### 9. Invoice Search (Asynchronous)

**Endpoint:**
```http
POST /api/online/Query/Invoice/Async/Init
[REDACTED_SECRET] {token}
Content-Type: application/json
```

**Usage:** For large datasets (>100 invoices)

**Workflow:**
1. `POST /api/online/Query/Invoice/Async/Init` - initialization
2. `GET /api/online/Query/Invoice/Async/Status/{QueryElementReferenceNumber}` - check status
3. `GET /api/online/Query/Invoice/Async/Fetch/{QueryElementReferenceNumber}` - fetch results

---

### 10. Downloading Full Invoice

**Endpoint:**
```http
GET /api/online/Invoice/Get/{KsefReferenceNumber}
[REDACTED_SECRET] {token}
Accept: application/xml
```

**Response (200 OK):** Full FA(3) XML

---

## Offline Mode

### 11. Sending Offline Invoice

**Endpoint:**
```http
POST /api/online/Invoice/Send
[REDACTED_SECRET] {token}
Content-Type: application/octet-stream
```

**FA(3) XML with Offline24 designation:**
```xml
<Faktura>
  <Naglowek>
    <SystemInfo>Offline24</SystemInfo>
  </Naglowek>
  <!-- ... -->
</Faktura>
```

**Submission deadline:** 24h after connectivity is restored

---

## Error Codes

### Most Common

| Code | Description | Solution |
|------|-------------|----------|
| 100 | Invalid XML format | Check UTF-8 encoding |
| 101 | Schema validation error | Make sure you are using FA(3) |
| 102 | Invalid NIP | Check in VAT White List |
| 103 | Future date | Correct DataWytworzeniaFa |
| 104 | Duplicate invoice number | Check uniqueness |
| 401 | Unauthorized | Session expired, refresh token |
| 403 | Forbidden | Check token permissions |
| 500 | Server error | Retry with exponential backoff |
| 503 | Service unavailable | Check KSeF status (Latarnia) |

---

## Rate Limiting

**NOTE:** Details may vary. Check the current documentation.

**Typical limits (estimated):**
- Sessions: ~100 sessions/hour per token
- Invoices: ~1000 invoices/hour per session
- Queries: ~100 queries/hour per session

**Best practices:**
- Use a single session for multiple invoices
- Implement exponential backoff on 429/503
- Cache query results (do not query every second)

---

## Environments

### DEMO (test)
```
Base URL: https://ksef-demo.mf.gov.pl
Purpose: Integration testing, development
Data: Test (non-production)
```

### PRODUCTION
```
Base URL: https://ksef.mf.gov.pl
Purpose: Production invoices
Data: Legally binding
```

**WARNING:** Do NOT test on production! Always use DEMO for development.

---

## Example Workflow

```python
# 1. Initialize session
session = ksef_client.init_session([REDACTED_SECRET]")

# 2. Send invoice
invoice_xml = generate_fa3_xml(invoice_data)
ref = ksef_client.send_invoice(session, invoice_xml)

# 3. Check status (with retry)
for i in range(10):
    status = ksef_client.get_invoice_status(session, ref)
    if status.code == 202:
        ksef_number = status.ksefReferenceNumber
        break
    elif status.code == 400:
        handle_rejection(status.exception)
        break
    time.sleep(2)  # Wait 2s before next check

# 4. Download UPO
upo_xml = ksef_client.get_upo(session, ksef_number)

# 5. Terminate session
ksef_client.terminate_session(session)
```

---

**Official documentation:** https://ksef.mf.gov.pl/api/docs


path: references/ksef-fa3-examples.md
content: # FA(3) XML Examples

**NOTE:** All examples are purely illustrative and simplified. They do not guarantee correct validation against the XSD schema. Before using in a production environment, verify compliance with the current FA(3) technical specification and perform validation tests.

---

## Basic VAT Invoice

```xml
<?xml version="1.0" encoding="UTF-8"?>
<Faktura xmlns="http://crd.gov.pl/wzor/2023/06/29/12648/"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <Naglowek>
        <KodFormularza kodSystemowy="FA(3)" wersjaSchemy="1-0E">FA</KodFormularza>
        <WariantFormularza>3</WariantFormularza>
        <DataWytworzeniaFa>2026-02-08T14:30:00</DataWytworzeniaFa>
        <SystemInfo>Autonomous KSeF Agent v2.1</SystemInfo>
    </Naglowek>

    <!-- Seller -->
    <Podmiot1>
        <DaneIdentyfikacyjne>
            <NIP>1234567890</NIP>
            <Nazwa>Moja Firma Sp. z o.o.</Nazwa>
        </DaneIdentyfikacyjne>
        <Adres>
            <KodKraju>PL</KodKraju>
            <AdresL1>ul. Przykladowa 1</AdresL1>
            <AdresL2>00-001 Warszawa</AdresL2>
        </Adres>
    </Podmiot1>

    <!-- Buyer -->
    <Podmiot2>
        <DaneIdentyfikacyjne>
            <NIP>0987654321</NIP>
            <Nazwa>Klient Sp. z o.o.</Nazwa>
        </DaneIdentyfikacyjne>
        <Adres>
            <KodKraju>PL</KodKraju>
            <AdresL1>ul. Testowa 10</AdresL1>
            <AdresL2>02-222 Krakow</AdresL2>
        </Adres>
    </Podmiot2>

    <!-- Invoice data -->
    <Fa>
        <KodWaluty>PLN</KodWaluty>
        <P_1>2026-02-08</P_1> <!-- Issue date -->
        <P_2>FV/2026/02/0008</P_2> <!-- Invoice number -->
        <P_6>2026-02-08</P_6> <!-- Sale date -->

        <!-- Invoice line items -->
        <FaWiersz>
            <NrWierszaFa>1</NrWierszaFa>
            <P_7>Programming service</P_7>
            <P_8A>hrs</P_8A>
            <P_8B>100</P_8B>
            <P_9A>100.00</P_9A>
            <P_11>10000.00</P_11> <!-- Net value -->
            <P_12>23</P_12> <!-- VAT rate -->
        </FaWiersz>

        <!-- Summary -->
        <P_13_1>10000.00</P_13_1> <!-- Net 23% -->
        <P_14_1>2300.00</P_14_1> <!-- VAT 23% -->
        <P_15>12300.00</P_15> <!-- Gross -->

        <RodzajFaktury>VAT</RodzajFaktury>

        <!-- Payment -->
        <Platnosc>
            <Zaplacono>0</Zaplacono>
            <DataZaplaty>2026-02-22</DataZaplaty>
            <FormaPlatnosci>6</FormaPlatnosci> <!-- Bank transfer -->
            <RachunekBankowy>12 3456 7890 1234 5678 9012 3456</RachunekBankowy>
        </Platnosc>
    </Fa>
</Faktura>
```

---

## Invoice with Multiple Items and VAT Rates

```xml
<Fa>
    <KodWaluty>PLN</KodWaluty>
    <P_1>2026-02-09</P_1>
    <P_2>FV/2026/02/0009</P_2>
    <P_6>2026-02-09</P_6>

    <!-- Item 1: VAT 23% -->
    <FaWiersz>
        <NrWierszaFa>1</NrWierszaFa>
        <P_7>Laptop Dell XPS 15</P_7>
        <P_8A>pcs</P_8A>
        <P_8B>2</P_8B>
        <P_9A>5000.00</P_9A>
        <P_11>10000.00</P_11>
        <P_12>23</P_12>
    </FaWiersz>

    <!-- Item 2: VAT 8% -->
    <FaWiersz>
        <NrWierszaFa>2</NrWierszaFa>
        <P_7>Technical books</P_7>
        <P_8A>pcs</P_8A>
        <P_8B>10</P_8B>
        <P_9A>50.00</P_9A>
        <P_11>500.00</P_11>
        <P_12>8</P_12>
    </FaWiersz>

    <!-- Item 3: VAT 0% (export) -->
    <FaWiersz>
        <NrWierszaFa>3</NrWierszaFa>
        <P_7>Consulting services (export)</P_7>
        <P_8A>hrs</P_8A>
        <P_8B>40</P_8B>
        <P_9A>200.00</P_9A>
        <P_11>8000.00</P_11>
        <P_12>0</P_12>
    </FaWiersz>

    <!-- Summary -->
    <P_13_1>10000.00</P_13_1> <!-- Net 23% -->
    <P_14_1>2300.00</P_14_1> <!-- VAT 23% -->
    <P_13_2>500.00</P_13_2> <!-- Net 8% -->
    <P_14_2>40.00</P_14_2> <!-- VAT 8% -->
    <P_13_3>8000.00</P_13_3> <!-- Net 0% -->
    <P_15>20840.00</P_15> <!-- Gross -->

    <RodzajFaktury>VAT</RodzajFaktury>
</Fa>
```

---

## Corrective Invoice

```xml
<Fa>
    <P_2>FV/2026/02/0005/K01</P_2> <!-- Correction number -->
    <RodzajFaktury>KOREKTA</RodzajFaktury>
    <PrzyczynaKorekty>Error in unit price</PrzyczynaKorekty>

    <!-- Corrected invoice data -->
    <DaneFaKorygowanej>
        <DataWystFaKorygowanej>2026-02-05</DataWystFaKorygowanej>
        <NrFaKorygowanej>FV/2026/02/0005</NrFaKorygowanej>
        <NrKSeFFaKorygowanej>1234567890-20260205-ORIGINAL123456-12</NrKSeFFaKorygowanej>
    </DaneFaKorygowanej>

    <!-- Data BEFORE correction -->
    <FaWierszCtrl>
        <LiczbaWierszyFa>1</LiczbaWierszyFa>
        <WartoscWierszyFa>12300.00</WartoscWierszyFa>
    </FaWierszCtrl>

    <!-- Data AFTER correction -->
    <FaWiersz>
        <NrWierszaFa>1</NrWierszaFa>
        <P_7>Programming service (price corrected)</P_7>
        <P_8A>hrs</P_8A>
        <P_8B>100</P_8B>
        <P_9A>50.00</P_9A> <!-- New price: 50 instead of 100 -->
        <P_11>5000.00</P_11>
        <P_12>23</P_12>
    </FaWiersz>

    <!-- New summary -->
    <P_13_1>5000.00</P_13_1>
    <P_14_1>1150.00</P_14_1>
    <P_15>6150.00</P_15>
</Fa>
```

---

## Invoice with EMPLOYEE Contractor Type (Business Trip)

**New in FA(3):** New contractor type for business trip settlements

```xml
<Podmiot2>
    <DaneIdentyfikacyjne>
        <TypPodmiotu>PRACOWNIK</TypPodmiotu>
        <PESEL>85010112345</PESEL>
        <ImieNazwisko>Jan Kowalski</ImieNazwisko>
    </DaneIdentyfikacyjne>
    <Adres>
        <KodKraju>PL</KodKraju>
        <AdresL1>ul. Pracownicza 5</AdresL1>
        <AdresL2>03-333 Warszawa</AdresL2>
    </Adres>
</Podmiot2>

<Fa>
    <P_2>DEL/2026/02/001</P_2>
    <FaWiersz>
        <P_7>Business trip settlement - February 2026</P_7>
        <P_8A>days</P_8A>
        <P_8B>5</P_8B>
        <P_9A>150.00</P_9A>
        <P_11>750.00</P_11>
        <P_12>zw</P_12> <!-- VAT exempt -->
    </FaWiersz>
    <RodzajFaktury>VAT</RodzajFaktury>
</Fa>
```

---

## Offline24 Mode Invoice

```xml
<Naglowek>
    <KodFormularza kodSystemowy="FA(3)" wersjaSchemy="1-0E">FA</KodFormularza>
    <WariantFormularza>3</WariantFormularza>
    <DataWytworzeniaFa>2026-02-10T08:00:00</DataWytworzeniaFa>
    <SystemInfo>Offline24</SystemInfo> <!-- Offline designation -->
</Naglowek>
```

**Note:** Must be sent to KSeF within 24h of connectivity restoration. Receipt date = date of KSeF number assignment.

---

## Invoice with Attachments

**New in FA(3):** Ability to include attachments

```xml
<Fa>
    <!-- Standard invoice data -->
    <P_2>FV/2026/02/0010</P_2>

    <!-- Attachments -->
    <Zalaczniki>
        <Zalacznik>
            <NazwaPliku>technical_specification.pdf</NazwaPliku>
            <TypZalacznika>SPECYFIKACJA</TypZalacznika>
            <RozmiarKB>256</RozmiarKB>
            <HashSHA256>a1b2c3d4e5f6...</HashSHA256>
        </Zalacznik>
        <Zalacznik>
            <NazwaPliku>delivery_protocol.pdf</NazwaPliku>
            <TypZalacznika>PROTOKOL</TypZalacznika>
            <RozmiarKB>128</RozmiarKB>
            <HashSHA256>f6e5d4c3b2a1...</HashSHA256>
        </Zalacznik>
    </Zalaczniki>
</Fa>
```

---

## Invoice with MPP (Split Payment Mechanism)

```xml
<Fa>
    <P_2>FV/2026/02/0011</P_2>

    <FaWiersz>
        <P_7>Structural steel (Annex 15)</P_7>
        <P_8A>kg</P_8A>
        <P_8B>1000</P_8B>
        <P_9A>20.00</P_9A>
        <P_11>20000.00</P_11>
        <P_12>23</P_12>
        <OznaczenieMPP>1</OznaczenieMPP> <!-- Requires MPP -->
    </FaWiersz>

    <P_13_1>20000.00</P_13_1>
    <P_14_1>4600.00</P_14_1>
    <P_15>24600.00</P_15>

    <Platnosc>
        <TypPlatnosci>MPP</TypPlatnosci>
        <RachunekBankowy>12 3456 7890 1234 5678 9012 3456</RachunekBankowy>
        <RachunekBankowyVAT>98 7654 3210 9876 5432 1098 7654</RachunekBankowyVAT>
    </Platnosc>
</Fa>
```

---

## Validation before Sending

```python
def validate_fa3_before_send(xml_content):
    """
    Basic validation before sending to KSeF
    """
    checks = []

    # 1. UTF-8 encoding
    try:
        xml_content.encode('utf-8')
        checks.append(('UTF-8 Encoding', True))
    except:
        checks.append(('UTF-8 Encoding', False))

    # 2. XML parsing
    try:
        root = ET.fromstring(xml_content)
        checks.append(('Valid XML', True))
    except:
        checks.append(('Valid XML', False))
        return checks

    # 3. Namespace
    if 'http://crd.gov.pl/wzor/2023/06/29/12648/' in xml_content:
        checks.append(('FA(3) Namespace', True))
    else:
        checks.append(('FA(3) Namespace', False))

    # 4. Schema version
    if 'wersjaSchemy="1-0E"' in xml_content:
        checks.append(('Schema version 1-0E', True))
    else:
        checks.append(('Schema version 1-0E', False))

    # 5. Required fields
    required = ['KodFormularza', 'P_1', 'P_2', 'P_15', 'NIP']
    for field in required:
        if f'<{field}' in xml_content or f'<{field}>' in xml_content:
            checks.append((f'Field {field}', True))
        else:
            checks.append((f'Field {field}', False))

    return checks
```

---

**Official FA(3) documentation:**
https://ksef.podatki.gov.pl/media/4u1bmhx4/information-sheet-on-the-fa-3-logical-structure.pdf


path: references/ksef-legal-status.md
content: # KSeF Legal Status - Details

**Last updated:** 8 February 2026
**NOTE:** Information may change. Regularly verify current regulations and communications from the Ministry of Finance.

---

## Implementation Schedule (Planned)

### Phase 1: Large Taxpayers (from 1 February 2026)
**Who it applies to:** Turnover >200 million PLN gross in 2024

**Obligations:**
- Issuing invoices through KSeF
- Receiving invoices through KSeF

### Phase 2: Other Taxpayers (from 1 April 2026)
**Who it applies to:** Turnover <=200 million PLN

**Obligations:**
- Receiving invoices through KSeF (from 1 February 2026)
- Issuing invoices through KSeF (from 1 April 2026)
- Until 31 March 2026 they may issue invoices outside KSeF

### Phase 3: Micro-entrepreneurs (from 1 January 2027)
**Who it applies to:** Monthly turnover <10 thousand PLN

**Obligations:**
- Receiving: from 1 February 2026
- Issuing: from 1 January 2027

---

## Transition Period

**NOTE:** Details may change. The information presented is based on currently available communications.

### Planned Grace Period
- **Until 31 December 2026** - anticipated no penalties for errors
- Applies to: errors in FA(3) structure, submission delays
- Does not apply to: evasion of invoice issuance

### Offline24 Mode
- **Status:** Planned as a permanent system feature
- **Purpose:** Emergency situations (no internet, KSeF outage, disasters)
- **Submission deadline:** 24h after connectivity is restored
- **Legal note:** Right to deduct VAT from the date of KSeF number assignment (not from the offline issue date)

### Invoices Outside KSeF
- **Until 31 March 2026:** Allowed for companies <=200 million PLN
- **From 1 April 2026:** Prohibited (with exceptions for special cases)

---

## System Changes

### KSeF 1.0 -> KSeF 2.0
- **KSeF 1.0:** Planned shutdown 26 January 2026
- **Technical break:** 5 days (26-31 January 2026)
- **KSeF 2.0:** Planned launch 1 February 2026

### FA(2) -> FA(3)
- **FA(2):** Planned end of support 1 February 2026
- **FA(3):** Mandatory from 1 February 2026
- **Migration:** All taxpayers must adapt their systems

---

## Legal Consequences (Future)

**NOTE:** The information below relates to the planned penalty system after the grace period ends.

### After 1 January 2027 (Planned)
- No invoice in KSeF -> possible penalty
- Invoice outside KSeF -> possible penalty
- Delayed submission -> possible penalty

**Penalty amounts:** Consult the current VAT Act

---

## Technical Requirements

### Mandatory Structure
- **FA(3) ver. 1-0E**
- Format: XML compliant with schema
- Encoding: UTF-8
- Validation: Automatic upon receipt by KSeF

### Certificates (MCU)
- **MCU:** Certificate and Authorization Module
- **Status:** Per schedule, active from 1.11.2025
- **Purpose:** Alternative authorization method (in addition to tokens)

---

## Information Sources

### Official
- KSeF Portal: https://ksef.podatki.gov.pl
- Ministry of Finance: https://www.gov.pl/web/kas/ksef

### Monitoring Changes
- MF Communications: https://ksef.podatki.gov.pl/web/ksef/aktualnosci
- KSeF Latarnia (status): https://github.com/CIRFMF/ksef-latarnia

---

**Last updated:** 8 February 2026
**Next review:** Recommended every 2 weeks


path: references/ksef-security-compliance.md
content: # Security & Compliance for KSeF

Security requirements, compliance and best practices.

**SECURITY WARNING:**
All code examples in this document are **educational and conceptual** — architectural patterns for the user to implement in their own system. This skill does NOT implement these mechanisms, does NOT store tokens, does NOT connect to Vault and does NOT manage encryption keys.

**Environment variables** mentioned in this document (e.g., `KSEF_TOKEN`, `KSEF_ENCRYPTION_KEY`) are declared in the skill metadata as optional. The skill does not implicitly request them — if the user provides them, the agent can use them in suggested code. All variables are described in the `env` section of the SKILL.md file.

**NEVER paste tokens, encryption keys, certificates or other credentials directly in the conversation with the agent.** Use only:
- Platform environment variables (env vars)
- Secrets manager (e.g., HashiCorp Vault, AWS Secrets Manager)
- Ephemeral session variables (ephemeral env vars)

Before using patterns in a production environment:
1. Conduct a security review
2. Use dedicated, tested tools instead of custom implementations
3. Never run unverified code from external sources
4. Implement principle of least privilege
5. Regularly update dependencies and conduct security audits
6. Test exclusively on the DEMO environment (`https://ksef-demo.mf.gov.pl`) — never on production

---

## Platform vs. Skill Guarantees — Pre-installation Verification

This skill declares security flags in **two sources**:
- **SKILL.md frontmatter** — contains `disableModelInvocation: true` (camelCase) and `disable-model-invocation: true` (kebab-case), as well as env var declarations with `secret: true` for variables containing credentials.
- **Manifest [`skill.json`](../skill.json)** — a dedicated machine-readable file with full security metadata, env var declarations (with `secret` and `scope` fields) and constraints. It is the source of truth for registries and scanners that may not parse YAML frontmatter.

However, **both of these sources are skill declarations, not platform guarantees**. Enforcement of these flags depends solely on the hosting platform.

**Known issue:** Registry metadata displayed by the platform may not reflect values from the frontmatter or `skill.json`. If the platform shows `disable-model-invocation: not set` (or omits this flag), or does not display environment variables as registered — the protection **is not active**, regardless of what the skill files declare.

**Mandatory pre-installation verification:**

1. **Compare registry metadata with frontmatter and `skill.json`** — after adding the skill to the platform, open the registry metadata view. Verify that:
   - `disable-model-invocation` = `true`
   - Environment variables `KSEF_TOKEN` and `KSEF_ENCRYPTION_KEY` are visible as registered secrets
   - Other platform-level security flags (if any) are correctly set
   - If ANY field does not match frontmatter/`skill.json` — treat the skill as higher risk
2. **Confirm environment variable isolation** — variables (`KSEF_TOKEN`, `KSEF_ENCRYPTION_KEY`, `KSEF_BASE_URL`) must not be logged, displayed in conversation or accessible to other skills
3. **If the platform does NOT enforce the `disableModelInvocation` flag:**
   - DO NOT configure any environment variables with credentials
   - DO NOT provide tokens, certificates or encryption keys
   - DO NOT allow autonomous use of the skill
   - Use only in manual mode (explicit user action) and only with the DEMO environment (`https://ksef-demo.mf.gov.pl`)
4. **Report discrepancy** — if registry metadata does not match frontmatter/`skill.json`, report it to the platform provider as a security issue requiring a fix. Provide the `skill.json` filename as an alternative metadata source if the platform does not parse YAML frontmatter

---

## VAT White List

### Automatic Verification

```python
import requests
from datetime import datetime

def verify_contractor_white_list(nip, bank_account, date=None):
    """
    Checks the contractor on the VAT White List
    API: https://wl-api.mf.gov.pl/
    """
    if date is None:
        date = datetime.now().strftime('%Y-%m-%d')

    url = f"https://wl-api.mf.gov.pl/api/search/nip/{nip}?date={date}"

    try:
        response = requests.get(url, timeout=10)
        response.raise_for_status()
        data = response.json()
    except Exception as e:
        return {
            'valid': False,
            'reason': f'API error: {e}',
            'risk': 'UNKNOWN'
        }

    # Check VAT status
    subject = data['result']['subject']

    if subject['statusVat'] != 'Czynny':
        return {
            'valid': False,
            'reason': f"Contractor VAT status: {subject['statusVat']}",
            'risk': 'HIGH',
            'details': subject
        }

    # Check bank account
    accounts = subject.get('accountNumbers', [])

    # Normalize account number (remove spaces)
    bank_account_normalized = bank_account.replace(' ', '')

    for acc in accounts:
        if acc.replace(' ', '') == bank_account_normalized:
            return {
                'valid': True,
                'name': subject['name'],
                'status': subject['statusVat'],
                'verified_account': acc
            }

    # Account not on the list
    return {
        'valid': False,
        'reason': 'Bank account is not on the White List',
        'risk': 'HIGH',
        'valid_accounts': accounts,
        'details': subject
    }
```

### Integration with Payments

```python
def before_payment_check(invoice, payment):
    """
    Verification before executing a transfer
    """
    # 1. Check White List
    verification = verify_contractor_white_list(
        nip=invoice.seller_nip,
        bank_account=payment.to_account,
        date=payment.date.strftime('%Y-%m-%d')
    )

    if not verification['valid']:
        # Hold payment
        payment.status = 'HOLD'
        payment.hold_reason = verification['reason']

        # Send alert
        send_critical_alert(
            level='HIGH',
            title='Payment held - VAT White List',
            message=f"Contractor: {invoice.seller_name} ({invoice.seller_nip})\n"
                   f"Amount: {payment.amount} PLN\n"
                   f"Reason: {verification['reason']}\n"
                   f"Invoice: {invoice.number}",
            invoice=invoice,
            payment=payment
        )

        return False

    # 2. Check if MPP is required
    if invoice.total_gross > 15000 and invoice.has_attachment_15_goods:
        if payment.type != 'MPP':
            send_warning_alert(
                title='Invoice requires MPP',
                message=f"Invoice {invoice.number} requires the split payment mechanism"
            )
            return False

    return True
```

---

## Token Security

### Token Storage

```python
from cryptography.fernet import Fernet
import os

class SecureTokenStorage:
    """
    Encrypted KSeF token storage
    """
    def __init__(self, encryption_key=None):
        if encryption_key is None:
            # Read from environment variable
            encryption_key = os.environ.get('KSEF_ENCRYPTION_KEY')

        if encryption_key is None:
            raise ValueError("Missing encryption key")

        self.cipher = Fernet(encryption_key.encode())

    def store_token(self, token_name, token_value):
        """Store token (encrypted)"""
        encrypted = self.cipher.encrypt(token_value.encode())
        # Save to database or vault
        db.tokens.insert({
            'name': token_name,
            'value': encrypted,
            'created_at': datetime.now()
        })

    def retrieve_token(self, token_name):
        """Retrieve token (decrypt)"""
        record = db.tokens.find_one({'name': token_name})
        if not record:
            raise ValueError(f"Token {token_name} does not exist")

        decrypted = self.cipher.decrypt(record['value'])
        return decrypted.decode()

    def rotate_token(self, token_name, new_token_value):
        """Token rotation (best practice: every 90 days)"""
        # Archive old token
        old_record = db.tokens.find_one({'name': token_name})
        db.tokens_archive.insert({
            **old_record,
            'archived_at': datetime.now()
        })

        # Store new one
        self.store_token(token_name, new_token_value)
```

### Integration with Vault (HashiCorp)

```python
import hvac

class VaultTokenStorage:
    """
    Storage in HashiCorp Vault (production)
    """
    def __init__(self, vault_url, vault_token):
        self.client = hvac.Client(url=vault_url, [REDACTED_SECRET])

    def store_token(self, token_name, token_value):
        self.client.secrets.kv.v2.create_or_update_secret(
            path=f'ksef/tokens/{token_name}',
            secret={'value': token_value}
        )

    def retrieve_token(self, token_name):
        [REDACTED_SECRET]
            path=f'ksef/tokens/{token_name}'
        )
        return secret['data']['data']['value']
```

---

## Audit Trail

### Operation Logging

```python
import logging
from datetime import datetime

class AuditLogger:
    """
    Immutable audit log (append-only)
    """
    def __init__(self):
        self.logger = logging.getLogger('ksef_audit')

    def log_operation(self, operation_type, user, entity_type, entity_id, details=None):
        """
        Every operation MUST be logged
        """
        audit_entry = {
            'timestamp': datetime.now().isoformat(),
            'operation': operation_type,  # CREATE, READ, UPDATE, DELETE
            'user': user,
            'entity_type': entity_type,  # INVOICE, PAYMENT, etc.
            'entity_id': entity_id,
            'details': details or {},
            'ip_address': self._get_client_ip(),
            'user_agent': self._get_user_agent(),
            'session_id': self._get_session_id()
        }

        # Save to immutable storage (append-only)
        audit_db.insert(audit_entry)

        # Log to file (for compliance)
        self.logger.info(json.dumps(audit_entry))

    def log_ai_decision(self, invoice, prediction, action):
        """
        Special logging for AI decisions
        """
        self.log_operation(
            operation_type='AI_CLASSIFICATION',
            user='AI_SYSTEM',
            entity_type='INVOICE',
            entity_id=invoice.id,
            details={
                'model_name': 'ExpenseClassifier',
                'model_version': '2.1',
                'prediction': prediction['category'],
                'confidence': prediction['confidence'],
                'action_taken': action,
                'reviewed_by_human': action == 'MANUAL_REVIEW'
            }
        )
```

### Audit Review

```python
def audit_report(start_date, end_date, user=None):
    """
    Generate audit report
    """
    query = {
        'timestamp': {
            '$gte': start_date.isoformat(),
            '$lte': end_date.isoformat()
        }
    }

    if user:
        query['user'] = user

    entries = audit_db.find(query).sort('timestamp', 1)

    report = {
        'period': f"{start_date} - {end_date}",
        'total_operations': 0,
        'by_type': {},
        'by_user': {},
        'entries': []
    }

    for entry in entries:
        report['total_operations'] += 1
        report['by_type'][entry['operation']] = \
            report['by_type'].get(entry['operation'], 0) + 1
        report['by_user'][entry['user']] = \
            report['by_user'].get(entry['user'], 0) + 1
        report['entries'].append(entry)

    return report
```

---

## Backup and Disaster Recovery

### 3-2-1 Strategy

**Business requirements:** Accounting data must be protected with redundant backups following the 3-2-1 rule:
- **3 copies** of data (production + 2 backups)
- **2 different media types** (e.g., local SSD + external storage)
- **1 copy offsite** (cloud or remote location)

**Implementation approach:**
1. Use built-in backup solutions from your database provider (managed backups, automated snapshots)
2. Schedule daily automatic backups during low-activity hours
3. Store backups in multiple locations (local fast storage + remote cloud)
4. Implement automatic backup verification for data integrity assurance
5. Retain backups per legal requirements (minimum 10 years for accounting data)
6. Document and regularly test disaster recovery procedures

**For production systems:**
- Leverage managed database services (AWS RDS, Azure Database, Google Cloud SQL) with automatic backup features
- Use enterprise backup solutions designed for accounting/financial data
- Implement monitoring and alerting for backup failures
- Ensure backup processes do not disrupt accounting operations

### KSeF Synchronization for Disaster Recovery

**Key principle:** KSeF is the source of truth for all invoices. If local data is lost, it can be reconstructed from KSeF.

**Recovery process:**
1. **Restore from backup** - Use your infrastructure provider's restore procedures
2. **Synchronize with KSeF** - Download invoices from KSeF API for the last 7-30 days (depending on backup age)
3. **Verify data integrity** - Compare local invoice records with KSeF to identify discrepancies
4. **Reconcile differences** - Update local database with authoritative KSeF data
5. **Notify stakeholders** - Inform accounting team when recovery is complete and system is operational

**Important:** Test disaster recovery procedures quarterly to ensure they work when needed.

---

## GDPR

### Personal Data in Invoices

```python
def anonymize_invoice_for_archive(invoice, retention_years=10):
    """
    Anonymization after retention period expires
    """
    retention_date = invoice.issue_date + timedelta(days=365 * retention_years)

    if datetime.now() > retention_date:
        # Data to anonymize (GDPR)
        invoice.buyer_name = "***ANONYMIZED***"
        invoice.buyer_address = "***"
        invoice.buyer_email = None
        invoice.buyer_phone = None

        # Keep NIP (required for fiscal purposes)
        # invoice.buyer_nip - KEEP

        invoice.anonymized_at = datetime.now()
        invoice.save()
```

### Data Deletion Request (Right to be Forgotten)

```python
def handle_gdpr_deletion_request(contractor_nip):
    """
    WARNING: Invoices are subject to mandatory retention (5-10 years)
    They cannot be deleted during the retention period!
    """
    # 1. Check if retention period has passed
    invoices = get_all_invoices_for_contractor(contractor_nip)

    for invoice in invoices:
        retention_date = invoice.issue_date + timedelta(days=365 * 10)

        if datetime.now() < retention_date:
            return {
                'status': 'REJECTED',
                'reason': 'Invoices are subject to mandatory retention',
                'retention_until': retention_date
            }

    # 2. If period has passed - anonymize
    for invoice in invoices:
        anonymize_invoice_for_archive(invoice)

    return {
        'status': 'COMPLETED',
        'anonymized_invoices': len(invoices)
    }
```

---

## Access Control (RBAC)

### Roles and Permissions

```python
ROLES = {
    'ADMIN': [
        'invoice.create', 'invoice.read', 'invoice.update', 'invoice.delete',
        'payment.create', 'payment.read', 'payment.update', 'payment.delete',
        'user.manage', 'settings.manage'
    ],
    'ACCOUNTANT': [
        'invoice.create', 'invoice.read', 'invoice.update',
        'payment.create', 'payment.read', 'payment.update',
        'report.generate'
    ],
    'VIEWER': [
        'invoice.read', 'payment.read', 'report.view'
    ]
}

def check_permission(user, permission):
    """
    Check if user has permission
    """
    user_role = user.role
    allowed_permissions = ROLES.get(user_role, [])

    if permission not in allowed_permissions:
        audit_logger.log_operation(
            operation_type='PERMISSION_DENIED',
            user=user.username,
            entity_type='PERMISSION',
            entity_id=permission
        )
        raise PermissionError(f"User {user.username} does not have permission: {permission}")

    return True
```

---

## SSL/TLS Certificates

### Connections to KSeF

```python
import ssl
import certifi

def secure_ksef_connection():
    """
    Always use HTTPS with certificate verification
    """
    session = requests.Session()

    # 1. Verify certificate (NEVER verify=False)
    session.verify = certifi.where()

    # 2. Use strong cipher suites
    session.mount('https://', requests.adapters.HTTPAdapter(
        max_retries=3,
        pool_connections=10,
        pool_maxsize=20
    ))

    # 3. Set timeout
    session.request = lambda *args, **kwargs: \
        requests.Session.request(session, *args, timeout=30, **kwargs)

    return session
```

---

## Secure Development Practices

### 1. Avoid Dynamic Code Execution

**NEVER use:**
- `eval()` or `exec()` on user input or external data
- Shell command execution with string concatenation
- Dynamic SQL queries built through string concatenation

**INSTEAD use:**
- Parameterized database queries (prevents SQL injection)
- Validated, type-checked input data
- Structured API calls with proper argument handling
- Built-in libraries and frameworks designed for accounting operations

### 2. Input Validation

```python
def validate_invoice_number(number):
    """Validate before using in queries"""
    # Only alphanumeric, hyphens, slashes
    import re
    if not re.match(r'^[A-Z0-9/-]+$', number):
        raise ValueError("Invalid invoice number")
    if len(number) > 50:
        raise ValueError("Invoice number too long")
    return number
```

### 3. Principle of Least Privilege

```python
# Database user with minimum permissions
DB_CONFIG = {
    'user': 'ksef_readonly',  # SELECT only for reports
    'user': 'ksef_app',       # SELECT + INSERT + UPDATE for app
    'user': 'ksef_admin',     # All permissions (admin only)
}
```

### 4. Use Enterprise-Grade Solutions

For production accounting systems:
- **Databases:** Managed database services with automatic backups and point-in-time recovery
- **Monitoring:** Professional monitoring platforms with alerting capabilities
- **Security:** Enterprise identity management and access control systems
- **Compliance:** Audit logging solutions designed for financial data

---

## Security Checklist

- [ ] KSeF tokens encrypted (Fernet/Vault)
- [ ] VAT White List checked before every payment
- [ ] Audit trail of all operations
- [ ] 3-2-1 backup (daily)
- [ ] HTTPS with certificate verification
- [ ] RBAC (access control)
- [ ] Retention policy compliant with law (10 years)
- [ ] GDPR - anonymization after retention period
- [ ] Monitoring and alerts
- [ ] Disaster recovery plan (tested quarterly)

---

**Compliance:** Implementing the above practices supports compliance with:
- VAT Act
- GDPR
- Accounting Act
- ISO 27001 standards (optional)


path: references/ksef-troubleshooting.md
content: # KSeF Troubleshooting

Guide for resolving the most common problems with the KSeF system.

---

## Authentication Problems

### Error 401: Unauthorized

**Symptoms:**
```json
{
  "processingCode": 401,
  "processingDescription": "Unauthorized"
}
```

**Causes:**
- Session expired (token valid only ~30 min)
- Invalid token
- Token does not have required permissions

**Solution:**
```python
def handle_401_error():
    # 1. Check token validity
    if session.is_expired():
        # Refresh session
        session = ksef_client.init_session(token)
        retry_operation()

    # 2. Check token permissions
    # Token must have scope: invoice.read, invoice.write

    # 3. Generate new token in KSeF portal
```

---

### Error 403: Forbidden

**Symptoms:**
```json
{
  "processingCode": 403,
  "processingDescription": "Forbidden"
}
```

**Causes:**
- Token does not have permissions for this operation
- NIP in token does not match NIP in invoice

**Solution:**
1. Check token permissions in KSeF portal
2. Make sure the token is for the correct NIP
3. Check if the token has scope for the given operation (read/write)

---

## Invoice Validation Problems

### Error 100: Invalid XML Format

**Symptoms:**
```json
{
  "exceptionCode": "100",
  "exceptionDescription": "Invalid XML format"
}
```

**Solution:**
```python
# 1. Check encoding
assert xml_content.startswith('<?xml version="1.0" encoding="UTF-8"?>')

# 2. Validate XML parser
try:
    ET.fromstring(xml_content)
except ET.ParseError as e:
    print(f"Parse error: {e}")
    # Check: unclosed tags, invalid characters

# 3. Remove BOM (Byte Order Mark)
xml_content = xml_content.lstrip('\ufeff')
```

---

### Error 101: Schema Validation Error

**Symptoms:**
```json
{
  "exceptionCode": "101",
  "exceptionDescription": "XSD schema validation error"
}
```

**Causes:**
- Using FA(2) instead of FA(3)
- Invalid XML structure
- Missing required fields
- Invalid namespace

**Solution:**
```python
# FA(3) checklist:
checks = {
    'Namespace': 'http://crd.gov.pl/wzor/2023/06/29/12648/',
    'KodFormularza kodSystemowy': 'FA(3)',
    'wersjaSchemy': '1-0E',
    'WariantFormularza': '3'
}

for field, expected in checks.items():
    if expected not in xml_content:
        print(f"ERROR: Missing {field} = {expected}")
```

**Common errors:**
- `kodSystemowy="FA(2)"` -> Change to `FA(3)`
- `wersjaSchemy="1-0"` -> Change to `1-0E`
- Old namespace from 2021 -> Use 2023 namespace

---

### Error 102: Invalid NIP

**Symptoms:**
```json
{
  "exceptionCode": "102",
  "exceptionDescription": "Invalid NIP"
}
```

**Solution:**
```python
def validate_nip(nip):
    # 1. Length
    if len(nip) != 10:
        return False

    # 2. Digits
    if not nip.isdigit():
        return False

    # 3. Checksum
    weights = [6, 5, 7, 2, 3, 4, 5, 6, 7]
    check_sum = sum(int(nip[i]) * weights[i] for i in range(9))
    check_digit = check_sum % 11

    if check_digit == 10:
        return False

    return check_digit == int(nip[9])

# 4. Check in VAT White List
response = requests.get(f"https://wl-api.mf.gov.pl/api/search/nip/{nip}")
if response.status_code != 200:
    print("NIP does not exist in MF database")
```

---

### Error 103: Future Date

**Symptoms:**
```json
{
  "exceptionCode": "103",
  "exceptionDescription": "Future date"
}
```

**Solution:**
```python
from datetime import datetime, timedelta

# DataWytworzeniaFa cannot be in the future
now = datetime.now()
data_wytworzenia = now.strftime('%Y-%m-%dT%H:%M:%S')

# ERROR: Tomorrow's date
tomorrow = now + timedelta(days=1)
data_wytworzenia = tomorrow.strftime(...)  # wrong

# Note on time zones
# KSeF uses Polish time (UTC+1/UTC+2)
```

---

### Error 104: Duplicate Invoice Number

**Symptoms:**
```json
{
  "exceptionCode": "104",
  "exceptionDescription": "Invoice with this number already exists"
}
```

**Solution:**
```python
def generate_unique_invoice_number():
    # Format: FV/YEAR/MONTH/NUMBER
    year = datetime.now().year
    month = datetime.now().month

    # Get last number from database
    last_number = db.get_last_invoice_number(year, month)

    next_number = (last_number or 0) + 1

    return f"FV/{year}/{month:02d}/{next_number:04d}"

# Check if number already exists in KSeF
existing = ksef_client.check_invoice_number(invoice_number)
if existing:
    print("Number already used - generate a new one")
```

---

## Performance Problems

### API Timeout / No Response

**Symptoms:**
- Request timeout (>30s)
- 503 Service Unavailable
- No response

**Diagnosis:**
```python
def diagnose_timeout():
    # 1. Check KSeF status
    try:
        response = requests.get('https://ksef.mf.gov.pl/api/health')
        print(f"KSeF status: {response.status_code}")
    except:
        print("KSeF unavailable")
        return "KSEF_DOWN"

    # 2. Check own network
    try:
        requests.get('https://google.com', timeout=5)
    except:
        print("Internet problem")
        return "NETWORK_ISSUE"

    # 3. Check load (peak hours)
    hour = datetime.now().hour
    if hour in [15, 16, 17]:  # 15:00-18:00
        print("Peak hours - heavy KSeF load")
        return "HIGH_LOAD"

    return "UNKNOWN"
```

**Solution:**
```python
import time

def send_with_retry(invoice_xml, max_retries=3):
    for attempt in range(max_retries):
        try:
            response = ksef_client.send_invoice(invoice_xml, timeout=60)
            return response
        except Timeout:
            if attempt < max_retries - 1:
                # Exponential backoff
                wait_time = 2 ** attempt  # 1s, 2s, 4s
                print(f"Timeout - retry {attempt+1}/{max_retries} in {wait_time}s")
                time.sleep(wait_time)
            else:
                raise

# Best practices:
# - Send outside peak hours (21:00-6:00)
# - Use queue + background worker
# - Implement circuit breaker
```

---

### Rate Limiting (429)

**Symptoms:**
```json
{
  "processingCode": 429,
  "processingDescription": "Too many requests"
}
```

**Solution:**
```python
import time
from functools import wraps

def rate_limit(max_per_hour=100):
    """Decorator limiting the number of requests"""
    calls = []

    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            now = time.time()
            # Remove old calls (from over an hour ago)
            calls[:] = [c for c in calls if c > now - 3600]

            if len(calls) >= max_per_hour:
                wait_time = 3600 - (now - calls[0])
                print(f"Rate limit - wait {wait_time:.0f}s")
                time.sleep(wait_time)

            calls.append(now)
            return func(*args, **kwargs)
        return wrapper
    return decorator

@rate_limit(max_per_hour=100)
def send_invoice(invoice):
    return ksef_client.send_invoice(invoice)
```

---

## Payment Problems

### Cannot Match Payment

**Causes:**
- Amount mismatch (error in transfer amount)
- Missing invoice number in title
- Split payment (MPP) - partial payment
- Batch payment (multiple invoices at once)

**Solution:**
```python
def handle_unmatched_payment(payment):
    # 1. Extended search (tolerance +/-2%)
    matches = search_invoices_extended(
        amount_min=payment.amount * 0.98,
        amount_max=payment.amount * 1.02,
        date_range_days=14  # +/-14 days instead of +/-7
    )

    if matches:
        return present_to_user_for_confirmation(matches)

    # 2. Check if it's a split payment (VAT portion)
    if is_likely_vat_payment(payment):
        net_payment = find_net_payment(payment)
        if net_payment:
            return match_mpp(net_payment, payment)

    # 3. Check batch payment
    invoice_numbers = extract_invoice_numbers_from_title(payment.title)
    if len(invoice_numbers) > 1:
        return split_payment_to_invoices(payment, invoice_numbers)

    # 4. Flag for manual review
    flag_for_review(payment)
```

---

## Environment Problems

### Test Invoices on Production

**Problem:** Test invoices were sent to the production environment

**WARNING:** Invoices on production are legally binding!

**What to do:**
1. DO NOT delete invoices (impossible in KSeF)
2. Issue corrective invoices to zero
3. Contact the counterparty
4. Report to the accounting department

**Prevention:**
```python
# Always check the environment
class KSefClient:
    def __init__(self, environment='demo'):
        if environment == 'production':
            # Force confirmation
            confirm = input("WARNING: PRODUCTION! Continue? (yes/no): ")
            if confirm != 'yes':
                raise Exception("Cancelled - use DEMO for testing")

        self.base_url = {
            'demo': 'https://ksef-demo.mf.gov.pl',
            'production': 'https://ksef.mf.gov.pl'
        }[environment]
```

---

## Monitoring and Alerts

### Alert Configuration

```python
def setup_monitoring():
    monitors = [
        # 1. Alert on high rejection rate
        {
            'name': 'High rejection rate',
            'condition': lambda: get_rejection_rate_last_hour() > 0.2,
            'action': send_alert_email
        },

        # 2. Alert on KSeF unavailability
        {
            'name': 'KSeF down',
            'condition': lambda: not check_ksef_health(),
            'action': send_sms_alert
        },

        # 3. Alert on payment matching problems
        {
            'name': 'Too many unmatched payments',
            'condition': lambda: count_unmatched_payments() > 10,
            'action': notify_accountant
        }
    ]

    # Run monitoring every 5 minutes
    schedule.every(5).minutes.do(run_monitors, monitors)
```

---

## Helpful Tools

### KSeF Latarnia (System Status)

```bash
# Check KSeF status in real time
git clone https://github.com/CIRFMF/ksef-latarnia
cd ksef-latarnia
python check_status.py
```

### XML Validator

```python
from lxml import etree

def validate_fa3_xsd(xml_content):
    """Validation against XSD schema"""
    xsd_url = "https://ksef.podatki.gov.pl/xsd/FA3_1-0E.xsd"

    # Download schema
    xsd_doc = etree.parse(xsd_url)
    schema = etree.XMLSchema(xsd_doc)

    # Validate XML
    xml_doc = etree.fromstring(xml_content.encode('utf-8'))

    try:
        schema.assertValid(xml_doc)
        return True, "OK"
    except etree.DocumentInvalid as e:
        return False, str(e)
```

---

**Need help?**
- KSeF Documentation: https://ksef.podatki.gov.pl
- Forum: https://github.com/CIRFMF/ksef-discussions
- MF Helpdesk: (check KSeF portal)


path: skill.json
content: {
  "name": "ksef-accountant-en",
  "version": "1.0.0",
  "description": "National e-Invoice System (KSeF) accounting assistant — domain knowledge, architectural patterns and code examples. Does not contain executable code.",
  "license": "MIT",
  "homepage": "https://github.com/alexwoo-awso/skills",
  "source": "https://github.com/alexwoo-awso/skills/tree/main/ksef-accountant-en",
  "type": "instruction-only",
  "has_executable_code": false,
  "has_install_script": false,
  "has_binaries": false,
  "has_runtime_dependencies": false,
  "security": {
    "disable-model-invocation": true,
    "disableModelInvocation": true,
    "allow-model-invocation": false,
    "allowModelInvocation": false,
    "requires-user-action": true,
    "instruction-only": true,
    "credential-handling": "user-managed-only",
    "credential-storage": "none",
    "credential-transmission": "none"
  },
  "env": {
    "KSEF_TOKEN": {
      "description": "KSeF API token for session authentication. Provided by the user — the skill does not generate, store or transmit tokens.",
      "required": false,
      "secret": true,
      "scope": "session-auth"
    },
    "KSEF_ENCRYPTION_KEY": {
      "description": "Fernet encryption key for secure token storage. Usage is optional — an example of a security pattern.",
      "required": false,
      "secret": true,
      "scope": "encryption"
    },
    "KSEF_BASE_URL": {
      "description": "KSeF API base URL. Defaults to https://ksef-demo.mf.gov.pl (DEMO). Production requires explicit user consent.",
      "required": false,
      "secret": false,
      "default": "https://ksef-demo.mf.gov.pl",
      "scope": "api-endpoint"
    }
  },
  "constraints": {
    "no-autonomous-execution": true,
    "requires-explicit-user-consent": true,
    "demo-first": true,
    "production-requires-user-approval": true,
    "never-store-credentials": true,
    "never-transmit-credentials": true,
    "never-generate-credentials": true
  }
}


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