---
type: skill
agent_ready: true
schema_version: 1
bundle_version: "0.28.0"
name: getmyinvoices
description: "GetMyInvoices API v3 — generischer Client und Workflows für Rechnungen, Lieferanten, Bank-Transaktionen, Workflow-Approvals und Stammdaten. Trigger wenn der Nutzer Belege, Rechnungen, Companies oder Bankdaten aus GetMyInvoices abrufen, filtern, anlegen oder ändern will, API-Fragen stellt oder nach GMI-Status fragt. Für Batch-Buchhaltung stattdessen gmi-accountant."
tags:
  - skill
  - api
  - buchhaltung
  - business
  - getmyinvoices
  - integration
  - bundle-eval-hub
scope: vault
bundle_skill: true
default_active:
  allgemein: false
  business: true
depends_on: []
conflicts_with: []
created: 2026-04-08
updated: 2026-05-16
---

# GetMyInvoices Skill

Dieser Skill kapselt 73 API-Operationen in 16 Gruppen hinter einem schlanken Python-Client und elf Workflow-Skripten. Authentifizierung läuft ausschließlich über die Windows-Env-Var `GETMYINVOICES_API_KEY` — der Key fließt nie in den LLM-Kontext.

## Abgrenzung

**Nutze diesen Skill** für einzelne API-Calls, Ad-hoc-Abfragen, Wissensfragen zur Spec, Stammdaten-Pflege (Companies, Tags), manuelles Rechnungs-Handling und Bank-Transaktions-Einsicht. Deckt Trigger wie „GMI", „Rechnungen abrufen", „Rechnung herunterladen", „Lieferant anlegen", „Company ändern", „Bank-Transaktionen", „Workflow-Approvals", „Payment Runs", „API-Status" ab.

**Nimm stattdessen einen anderen Skill** bei: automatischer Batch-Verarbeitung von Belegen (→ [gmi-accountant](../gmi-accountant/SKILL.md)), Health-Score, Anomalie-Erkennung, Compliance-Prüfung, StB-Übergabepaket, allgemeiner Steuerberatung, DATEV-Export, SKR-Buchungslogik oder BI-Analyse-Tools.

## Sicherheit

- Der API-Key darf nicht in den LLM-Kontext gelangen, weil er sonst in Logs, Outputs oder im Gesprächsverlauf sichtbar würde. Kein `echo`, `cat`, `print`, `Read` auf Dateien, die ihn enthalten könnten.
- Logs, Tracebacks oder Maskierungen mit Key-Anteilen können den Schlüssel rekonstruierbar machen, deshalb weglassen.
- Validierung nur per Länge: `python -c "import os; print(len(os.environ.get('GETMYINVOICES_API_KEY','')))"`
- Bei 401/403: nur "Unauthorized" zurückgeben. Header-Dumps könnten den Key offenlegen.
- `.env`-Dateien weder anlegen noch lesen, weil sie unverschlüsselt im Dateisystem liegen und leicht in Kontexte geladen werden.
- Auth-Header: `X-API-KEY` (wird vom Client intern gesetzt).

## Quickstart

```python
import sys
sys.path.insert(0, ".skills/getmyinvoices/scripts")
from gmi_client import GmiClient

client = GmiClient()
status = client.call("APIStatus")
docs = list(client.paginate("getDocumentList", items_key="records", page_size=50, startDateFilter="2026-01-01"))
```

**Wichtige API-Realitäten (nicht aus der Spec lesbar):**
- `getDocumentList` liefert `{records, totalCount, maxPages, ...}` — items_key ist **`records`**, nicht `documents`. Betrag steht in `grossAmount`, nicht `amount`.
- `getCompanyList` liefert eine Liste direkt (kein Wrapper). ID-Feld ist `companyUid`.
- `getOneDocument` liefert `{meta_data, fileContent: <base64>}`. PDF-Bytes sind hier schon enthalten — `downloadDocument` ist nur nötig, wenn man den reinen Byte-Stream will.
- `getVatRates` liefert `{success, data: [5, 7, 16, 19]}` — einziger Stammdaten-Endpoint mit Wrapper.
- `listBankAccounts` liefert 403 ohne Banking-Paket. Vor dem Call `getAccountDetails` lesen und `hasBankingAccess` prüfen.
- Stammdaten-operationIds heißen `getCountryList`, `getCurrencyList`, `getTagList` (nicht `-s`).
- **`getOneCompany` existiert** (Handoff war veraltet). GET /companies/{companyUid}. Eigenheit: liefert dict wenn Treffer, leere `[]` wenn UID nicht existiert — gibt keinen 404.
- **Content-Type: application/json auf allen Methoden Pflicht**, auch auf DELETE ohne Body. Fehlender Header → HTTP 415 "Request content-type is not valid". Der Client setzt den Header global auf der Session — nicht entfernen.
- **Tag-Writes** haben keinen eigenen Endpoint. Tags können nur indirekt über `addCustomCompany.tags`, `updateCustomCompany.tags`, `setDocumentTags` und `setTransactionTags` gesetzt werden. Kein `createTag`/`deleteTag`.

## Endpoint-Übersicht

73 Operationen in 16 Gruppen. Vollständige Tabelle mit `operationId`, Methode, Pfad und
Summary in `references/endpoint-catalog.md`. **Nur lesen, wenn der konkrete
operationId-Name gebraucht wird.**

Wichtigste Operationen:

| operationId | Was |
|---|---|
| `APIStatus` | Healthcheck |
| `getAccountDetails` | Account-Info |
| `getDocumentList` | Rechnungen listen (paginiert) |
| `getOneDocument` | Eine Rechnung lesen |
| `downloadDocument` | PDF-Bytes einer Rechnung |
| `getAttachmentList` | Anhänge zu einer Rechnung |
| `getCompanyList` | Lieferanten/Unternehmen (Liste, `companyUid`) |
| `getCountryList` | Länder (240, mit `countryUid`, Code, EU-Flag) |
| `getCurrencyList` | Währungen (54, `currencyCode`) |
| `getVatRates` | MwSt-Sätze (Wrapper `{data: [...]}`) |
| `getTagList` | Tags |
| `listBankAccounts` | Bank-Konten — nur mit `hasBankingAccess` |
| `getDocumentList` mit `companyFilter` | Rechnungen eines Lieferanten |

## Phase-1 Workflows

### 1) Rechnungen filtern (Lieferant + Zeitraum + Aggregation)
```
# Tabelle mit Summen
python .skills/getmyinvoices/scripts/workflows/filter_invoices.py \
  --supplier "Telekom" --from 2026-01-01 --to 2026-04-06 --min 100 --format markdown

# Nur Summen ("wieviel an Amazon dieses Jahr?")
python .skills/getmyinvoices/scripts/workflows/filter_invoices.py \
  --supplier "Amazon" --from 2026-01-01 --summary

# Plus Monatsaufschlüsselung
python .skills/getmyinvoices/scripts/workflows/filter_invoices.py \
  --supplier "Amazon" --by-month

# Betragsfilter (min/max)
python .skills/getmyinvoices/scripts/workflows/filter_invoices.py \
  --supplier "Amazon" --min 50 --max 500
```
Aggregationen: Summe + Anzahl pro Currency, optional pro Monat. Im JSON-Output unter `totals` und `by_month`. Importierbare Helper: `aggregate_totals(docs)` und `aggregate_by_month(docs)`.

### 2) Dokumente downloaden
```
python .skills/getmyinvoices/scripts/workflows/download_documents.py \
  --uids uid1,uid2 --target "07 Anhänge/GMI/2026-04/" --with-attachments
```

### 4) Bank-Transaktionen (Phase 2)
```
# Konten listen (benötigt hasBankingAccess)
python .skills/getmyinvoices/scripts/workflows/bank_transactions.py accounts

# Transaktionen eines Kontos mit Filtern
python .skills/getmyinvoices/scripts/workflows/bank_transactions.py list --account 123 --from 2026-01-01 --to 2026-04-30
python .skills/getmyinvoices/scripts/workflows/bank_transactions.py list --account 123 --text Amazon --max 50

# Einzelne Transaktion lesen
python .skills/getmyinvoices/scripts/workflows/bank_transactions.py get --account 123 --tx 456

# Transaktion einer Rechnung zuweisen
python .skills/getmyinvoices/scripts/workflows/bank_transactions.py assign --account 123 --tx 456 --document 789

# Ignorieren / Ignorieren aufheben
python .skills/getmyinvoices/scripts/workflows/bank_transactions.py ignore --account 123 --tx 456 --comment "Privatbuchung"
python .skills/getmyinvoices/scripts/workflows/bank_transactions.py unignore --account 123 --tx 456

# Tags setzen (ersetzt bestehende)
python .skills/getmyinvoices/scripts/workflows/bank_transactions.py tag --account 123 --tx 456 --tags "reise,2026"
```
Alle Subcommands prüfen zuerst `getAccountDetails.hasBankingAccess`. Ohne Banking-Paket: sauberer `locked`-Return, es wird kein Banking-Endpoint aufgerufen. Sobald das Paket aktiv ist, laufen alle Befehle ohne Code-Änderung.

### 3) Stammdaten lesen (Phase 2)
```
# Einzelabfragen
python .skills/getmyinvoices/scripts/workflows/lookup_masterdata.py --kind account
python .skills/getmyinvoices/scripts/workflows/lookup_masterdata.py --kind companies --filter Amazon
python .skills/getmyinvoices/scripts/workflows/lookup_masterdata.py --kind countries --filter DE
python .skills/getmyinvoices/scripts/workflows/lookup_masterdata.py --kind currencies --filter EUR
python .skills/getmyinvoices/scripts/workflows/lookup_masterdata.py --kind vatrates
python .skills/getmyinvoices/scripts/workflows/lookup_masterdata.py --kind tags
python .skills/getmyinvoices/scripts/workflows/lookup_masterdata.py --kind bankaccounts

# Alles auf einmal (Banking-Gate greift automatisch)
python .skills/getmyinvoices/scripts/workflows/lookup_masterdata.py --kind all --format markdown
python .skills/getmyinvoices/scripts/workflows/lookup_masterdata.py --kind all --format json
```
Read-only. `--kind bankaccounts` und `--kind all` prüfen vorher `hasBankingAccess` und brechen sauber ab, wenn das Banking-Paket nicht aktiv ist (kein API-Fehler, sondern ein expliziter `locked`-Return).

### 5) Stammdaten-Pflege: Companies (Phase 2)
```
# Lieferanten listen (optional filtern)
python .skills/getmyinvoices/scripts/workflows/companies.py list
python .skills/getmyinvoices/scripts/workflows/companies.py list --filter Amazon --type CUSTOM_SUPPLIER

# Neuen Custom-Supplier anlegen
python .skills/getmyinvoices/scripts/workflows/companies.py add \
  --name "Acme GmbH" --country 81 --city Berlin --iban DE... --tags "eu,preferred"

# Updaten (PUT, Name ist Pflicht)
python .skills/getmyinvoices/scripts/workflows/companies.py update \
  --uid 12345 --name "Acme AG" --city München

# Löschen — Confirm-Flow Pflicht
# Interaktiv: tippt den exakten Namen
python .skills/getmyinvoices/scripts/workflows/companies.py delete --uid 12345
# Non-interaktiv: beide Flags nötig
python .skills/getmyinvoices/scripts/workflows/companies.py delete --uid 12345 --confirm --confirm-name "Acme AG"

# Portal vorschlagen (Feature-Request an GMI, kein Custom-Supplier-Create)
python .skills/getmyinvoices/scripts/workflows/companies.py suggest \
  --portal-name "Server4You" --portal-url https://... --download-url https://...
```
`add`/`update` lesen nach dem Schreiben neu per `getOneCompany` und warnen, wenn die API übergebene Felder (z.B. `city`, `iban`) still verschluckt hat. `delete` macht erst einen Preview über `getOneCompany` und verlangt einen exakten Namens-Match zur Bestätigung. Jeder erfolgreiche DELETE schreibt eine Audit-Zeile auf stderr.

### 6) Email-Accounts verwalten (Phase 2)
```
python .skills/getmyinvoices/scripts/workflows/email_accounts.py list
python .skills/getmyinvoices/scripts/workflows/email_accounts.py get --id 42
python .skills/getmyinvoices/scripts/workflows/email_accounts.py create --device "Claude" --email user@example.com --folders "INBOX,Archiv"
python .skills/getmyinvoices/scripts/workflows/email_accounts.py update --id 42 --email new@test.de --folders "INBOX"
python .skills/getmyinvoices/scripts/workflows/email_accounts.py delete --id 42 --confirm --confirm-email "user@example.com"
```
`update` nutzt PATCH (nicht PUT) und lehnt leere Updates ab. `delete` verlangt den exakten Email-Match. `create` legt tatsächlich ein Konto mit OAuth/IMAP-Setup an — normalerweise über das Webinterface erledigen, dieser Workflow ist die programmatic Escape-Hatch.

### 7) Document-Schreib-Operationen (Phase 2)
```
# Rechnung hochladen (JSON + base64, kein multipart)
python .skills/getmyinvoices/scripts/workflows/documents.py upload \
  --file "path/to/Rechnung.pdf" --type INCOMING_INVOICE \
  --number INV-2026-001 --date 2026-04-06 --gross 119.00 --currency EUR \
  --company 1234 --tags "eu,ops" --note "..." [--ocr]

# Metadaten aktualisieren (PUT, alle Felder optional, aber mindestens eins)
python .skills/getmyinvoices/scripts/workflows/documents.py update \
  --uid 12345 --number INV-UPD --gross 129.00 --note "..." [--archived]

# Löschen — Confirm-Flow Pflicht
# Interaktiv: tippt documentNumber ODER filename exakt
python .skills/getmyinvoices/scripts/workflows/documents.py delete --uid 12345
# Non-interaktiv: --confirm plus einer der beiden Identifiers
python .skills/getmyinvoices/scripts/workflows/documents.py delete --uid 12345 --confirm --confirm-number "INV-2026-001"
python .skills/getmyinvoices/scripts/workflows/documents.py delete --uid 12345 --confirm --confirm-filename "Rechnung.pdf"
```
`upload` liest die Datei lokal, kodiert sie base64 und schickt sie im JSON-Body (kein multipart). Pflichtfelder: `--file`, `--type`. `upload` und `update` lesen nach dem Write per `getOneDocument` neu und warnen bei `silently_dropped`-Feldern. `delete` zeigt vor dem Löschen einen schlanken Preview (meta_data only, kein fileContent) und verlangt exakten Match auf `documentNumber` oder `filename`. Audit-Zeile auf stderr. `fileContent` (base64 der PDF) taucht nie in stdout/Logs auf.

### 8) Einzelne Rechnung lesen (Phase 2)
```
# Detail-Ansicht einer Rechnung (ohne fileContent)
python .skills/getmyinvoices/scripts/workflows/get_document.py --uid 12345

# JSON statt Markdown
python .skills/getmyinvoices/scripts/workflows/get_document.py --uid 12345 --format json

# Mit Line-Items, verknüpften Transaktionen oder OCR-Text
python .skills/getmyinvoices/scripts/workflows/get_document.py --uid 12345 --line-items --transactions
python .skills/getmyinvoices/scripts/workflows/get_document.py --uid 12345 --readable-text
```
Sicherheits-Riegel: `fileContent` (base64-PDF) wird nicht angefordert (`includeDocument` ist nicht setzbar) und wird zusätzlich rekursiv aus jedem Response herausgefiltert. Wer das PDF braucht, nutzt `download_documents.py`.

### 9) Erweiterte Rechnungs-Suche (Phase 2)
```
# Volltextsuche
python .skills/getmyinvoices/scripts/workflows/search_documents.py --text "Hosting" --from 2026-01-01

# Status- und Zeitraum-Filter mit Monats-Aggregation
python .skills/getmyinvoices/scripts/workflows/search_documents.py --status open --from 2026-01-01 --by-month

# Mehrfach-Tags + Notizfilter
python .skills/getmyinvoices/scripts/workflows/search_documents.py --tag eu --tag preferred --note "Server"

# Belege ohne Transaktions-Zuordnung
python .skills/getmyinvoices/scripts/workflows/search_documents.py --no-transaction-assigned --max-items 50

# Document-Type-Filter (kommagetrennte IDs)
python .skills/getmyinvoices/scripts/workflows/search_documents.py --type 1,2 --from 2026-01-01

# Updated-Since-Filter (was wurde seit dem geändert oder neu hinzugefügt?)
python .skills/getmyinvoices/scripts/workflows/search_documents.py --updated-since 2026-04-01
```
Volle Filter-Palette von `getDocumentList`: `searchQuery`, `paymentStatusFilter`, `documentNumberFilter`, `noteFilter`, `tagFilter[]`, `archivedFilter`, `documentTypeFilter`, `transactionAssigned`, `noTransactionAssigned`, `updatedOrNewSinceFilter`, `companyFilter` plus die Datums-Filter. Client-seitige Betrags-Filter via `--min`/`--max`. Pagination cap via `--max-items`. Aggregationen (`--summary`, `--by-month`) wiederverwendet aus `filter_invoices`.

### 10) Attachments verwalten (Phase 2)
```
# Anhänge einer Rechnung auflisten
python .skills/getmyinvoices/scripts/workflows/attachments.py list --document-uid 12345

# Meta-Daten eines einzelnen Anhangs (ohne Download, ohne fileContent)
python .skills/getmyinvoices/scripts/workflows/attachments.py inspect \
  --document-uid 12345 --attachment-uid 678

# Anhang herunterladen (dekodiert fileContent direkt auf Disk)
python .skills/getmyinvoices/scripts/workflows/attachments.py get \
  --document-uid 12345 --attachment-uid 678 --target ./belege [--filename beleg.pdf]

# Einzelnen Anhang hochladen
python .skills/getmyinvoices/scripts/workflows/attachments.py upload \
  --document-uid 12345 --file path/to/beleg.pdf

# Bulk-Upload: mehrere Dateien in einem Rutsch
python .skills/getmyinvoices/scripts/workflows/attachments.py bulk-upload \
  --document-uid 12345 --file a.pdf --file b.pdf --file c.pdf
python .skills/getmyinvoices/scripts/workflows/attachments.py bulk-upload \
  --document-uid 12345 --dir ./belege --pattern "*.pdf"

# Löschen — Confirm-Flow Pflicht (exakter fileName)
python .skills/getmyinvoices/scripts/workflows/attachments.py delete \
  --document-uid 12345 --attachment-uid 678
python .skills/getmyinvoices/scripts/workflows/attachments.py delete \
  --document-uid 12345 --attachment-uid 678 --confirm --confirm-filename "beleg.pdf"
```
Deckt alle vier Attachment-Endpoints ab: `getAttachmentList`, `getOneAttachment`, `uploadOneAttachment`, `deleteOneAttachment`. **Sicherheits-Riegel:** `fileContent` (base64) aus `getOneAttachment` wird rekursiv via `_safety._strip_forbidden` aus jedem Return entfernt; `get` dekodiert direkt auf Disk ohne Umweg über stdout. Upload-Body sind nur `{fileName, fileContent}` (kein `documentType`, kein `description`). **API-Inkonsistenz:** `getAttachmentList` nutzt `fileName` (camelCase), `getOneAttachment` nutzt `filename` (lowercase) — der Workflow normalisiert auf `fileName`. Attachment-Metadaten (description) sind read-only; die API bietet keinen `updateAttachment`-Endpoint.

### 11) Duplikat-Check und Papierkorb (Phase 2)
```
# MD5-basierter Check, ob eine Datei schon in GMI existiert
python .skills/getmyinvoices/scripts/workflows/documents.py check-hash --file path/to/beleg.pdf

# Upload mit integriertem Duplikat-Schutz (bricht ab, wenn schon vorhanden)
python .skills/getmyinvoices/scripts/workflows/documents.py upload \
  --file beleg.pdf --type RECEIPT --skip-if-exists

# Papierkorb-Sicht: gelöschte Dokumente auflisten
python .skills/getmyinvoices/scripts/workflows/documents.py list-deleted
python .skills/getmyinvoices/scripts/workflows/documents.py list-deleted --since 2026-01-01 --max-items 50
```
`check-hash` berechnet MD5 lokal (nicht SHA1/SHA256 — die API nutzt MD5) und ruft `checkDocumentHash` auf. Response liefert nur `{exists: bool}`, keine `documentUid`. `--skip-if-exists` ist Opt-in; ohne das Flag lädt `upload` wie bisher hoch. `list-deleted` nutzt `getDeletedDocumentList` mit Pagination; pro Record gibt es nur `documentUid` und `deletedAt`, keine weiteren Meta-Daten.

### 12) Payment Runs lesen (Phase 2)
```
# Alle Payment Runs listen
python .skills/getmyinvoices/scripts/workflows/payment_runs.py list

# Mit Status-Filter (UPCOMING, DRAFT, EXECUTED, SCHEDULED ...)
python .skills/getmyinvoices/scripts/workflows/payment_runs.py list --status UPCOMING

# JSON statt Markdown
python .skills/getmyinvoices/scripts/workflows/payment_runs.py list --format json
```
Read-only Workflow für den `getPaymentRuns`-Endpoint (Spec: `[Draft]`). Kein Banking-Gate nötig — der Endpoint antwortet ohne Banking-Permission mit 200/leerer Liste. Spec garantiert pro Item nur `primUid` + `date`, der Formatter ist tolerant gegen Extras (`status`, `amount`, `currency`). Beim Test-Account ist die Liste leer, der Workflow ist live verifiziert.

## Nicht implementiert / offene Endpoints

Diese Endpoints der GetMyInvoices API v3 sind bewusst **nicht** vom Skill abgedeckt. Wer sie braucht, muss sie entweder manuell über `GmiClient.call(...)` aufrufen oder einen passenden Workflow ergänzen.

| Endpoint | Grund | Aufwand |
|---|---|---|
| `getThumbOfDocument`, `updateThumbOfDocument`, `deleteThumbOfDocument` | Spec-Anomalie: der PUT-Endpoint hat einen Body identisch zu `updateDocument` (`companyId`, `grossAmount`, `documentNumber` etc.) ohne Thumb-Image-Feld. Semantik unklar, nicht live verifiziert. `getThumbOfDocument` liefert `{url: <signed-url>}`, kein Binärinhalt. | Klein, wenn die Semantik klar ist. Vor Implementation manueller Test im Web-UI nötig. |
| `getWorkflowApprovals`, `approve`, `return`, `reject`, `comment`, `approvePaymentStep` | Workflow-Paket wird bei GetMyInvoices bewusst nicht abonniert (Preis-Nutzen). | Größer, nur sinnvoll mit aktivem Workflow-Paket. |
| `createNewEmailAccount` | Endpoint gebaut, aber als Workflow-Subcommand nur spec-basiert, nicht live getestet — ein echter Aufruf legt OAuth/IMAP-Verbindungen an. | Klein, braucht aber bewusste Live-Tests mit echten Accounts. |
| Tag-Writes als eigenständige Endpoints | Existieren nicht in der API. Tags werden über `setDocumentTags`, `setTransactionTags` und die `tags`-Felder in `addCustomCompany`/`updateCustomCompany` mitgeschrieben. | — (API-Limitierung) |

Wer einen dieser Endpoints braucht, kann ihn direkt über `GmiClient.call(operationId, **params)` aufrufen — der Client validiert gegen die OpenAPI-Spec.

## Healthcheck

```
python .skills/getmyinvoices/scripts/healthcheck.py
```

## Fehler-Cheatsheet

| HTTP | Bedeutung | Reaktion |
|---|---|---|
| 400 | Validation-Fehler | Detail aus Response durchreichen |
| 401 | Auth fehlt/falsch | "Unauthorized" — den User informieren, Env-Var prüfen |
| 403 | Verboten | "Forbidden" |
| 404 | Nicht gefunden | Pfad nennen (ohne Header) |
| 415 | Content-Type-Fehler | Implementations-Bug |
| 429 | Rate-Limit | Retry mit Backoff (max 3x) |
| 503 | Temporäres Problem | Retry mit Backoff (max 3x) |

## Phase 2 Fortschritt

Erledigt:
- ✅ Stammdaten-Lesetoolkit (`lookup_masterdata.py`) für Account, Companies, Countries, Currencies, VAT, Tags, Bankkonten
- ✅ Bank-Transaktions-Workflows (`bank_transactions.py`) — listen, lesen, zuweisen, ignorieren, taggen. Permission-Gate via `hasBankingAccess`. Code-komplett, live-aktiv sobald Banking-Paket gebucht ist.
- ✅ Notifications-Workflow (`notifications.py`) — list, mark-all-read.
- ✅ Stammdaten-Pflege Companies (`companies.py`) — list, add, update, delete, suggest. Confirm-Flow für DELETE, Re-Read-Warnung für still verschluckte Felder.
- ✅ Stammdaten-Pflege Email-Accounts (`email_accounts.py`) — list, get, create, update, delete. PATCH für update, Confirm-Flow für DELETE.
- ✅ Document-Schreib-Operationen (`documents.py`) — upload (JSON+base64, kein multipart), update (PUT), delete mit Confirm-Flow (documentNumber ODER filename). Live verifiziert.
- ✅ Read-Workflows ausgebaut (Punkt 6): `filter_invoices.py` um Aggregationen erweitert (`--summary`, `--by-month`, `--max`); `get_document.py` neu (Detail-Read mit hartem fileContent-Filter); `search_documents.py` neu (volle `getDocumentList`-Filter-Palette: Volltext, Status, Type, Tags, Note, archived, transaction-assigned/-unassigned, updated-since, plus client-seitige Betrags-Filter und `--max-items` Cap). Alles live verifiziert.
- ✅ Document-Lücken geschlossen (Punkt 7): `attachments.py` neu (list, inspect, get, upload, bulk-upload, delete) mit vollem Attachment-CRUD und hartem fileContent-Filter; `documents.py` erweitert um `check-hash` (MD5-basierter Duplikat-Check), `list-deleted` (Papierkorb-Sicht) und `upload --skip-if-exists` (Opt-in Duplikat-Schutz vor Upload). Gemeinsames Safety-Modul `_safety.py` für `_strip_forbidden`. Alles live verifiziert. **Banking-Paket jetzt aktiv** (`hasBankingAccess=true`), Bank-Transaktions-Workflows bereit für Live-Einsatz.
- ✅ Payment Runs lesen (Punkt 8): `payment_runs.py` neu (`list` mit `--status`-Filter, `--format json|markdown`). Read-only, kein Banking-Gate, defensiv gegen Wrapper-Varianten. Live verifiziert (leere Liste beim Test-Account, kein Fehler). **Damit ist das Phase-2-Backlog inhaltlich abgeschlossen** — offen bleibt nur noch die Live-Verifikation der Bank-Writes (wartet auf verbundenes Bankkonto im Web-UI).

Offen:
- Lokaler SQLite-Cache (zurückgestellt)
- Bank-Reads (`list`/`get`) und Bank-Writes (`assign`/`ignore`/`tag`) live verifizieren — wartet auf verbundenes Bankkonto im GMI Web-UI

Workflow-Approvals sind im Paket "Workflow" von GetMyInvoices enthalten und werden nicht abonniert. Diese Endpoints werden im Skill nicht aktiv gebaut. **Tag-Writes** gibt es nicht als eigenen Endpoint — Tags werden über die Company-/Document-/Transaction-Writes mitgeschrieben.

## Konfiguration

`skill.config.example.yaml` enthält drei Feature-Flags:
- `include_banking_features` — schaltet Banking-Workflows (`bank_transactions.py`) komplett aus, ohne API-Probe.
- `include_write_operations` — Read-only-Modus: blockiert alle Schreib-Operationen (Upload/Update/Delete für Documents, Companies, Attachments, Email-Accounts).
- `include_payment_runs` — schaltet den `payment_runs.py`-Read aus.

Default für alle drei: `true` (volles Feature-Set). Die Flags sind reine Sicherheitsriegel — die API-Permission-Gates (`hasBankingAccess` etc.) bleiben unabhängig davon aktiv. Die Code-Verdrahtung ist Tech-Debt für v0.7; aktuell existieren die Flags nur deklarativ (Symmetrie-Pflicht).

## Evals

```bash
PYTHONUTF8=1 python .skills/getmyinvoices/run_evals.py --verbose
```

Pass-Rate muss 100 % sein. Die Eval-Pipeline besteht aus Lint-Assertions (Frontmatter, Eigennamen-Check, Umlaute, Feature-Flag-Symmetrie), Schema/Loader-Check (`skill.config.example.yaml`) und einer Subprocess-Delegation an die pytest-Suite unter `tests/`. Die volle Verhaltens-Coverage liegt in der pytest-Suite (~190 Tests), die der Hub nur orchestriert.

Hinweis: Die pytest-Suite setzt `requests` voraus. Im Bundle-venv ist `requests` opt-in über die `gmi`-Extra (`uv sync --extra gmi`). Ohne installierte Extra werden die zwei Behavior-Assertions (`pytest_suite_passes`, `healthcheck_script_imports_cleanly`) als `[SKIP]` markiert (zählen als PASS) — der Lint- und Loader-Teil des Hubs läuft trotzdem grün.

## Verweise

Detail-Spec, Implementierungsplan und Projektsteckbrief liegen im Maintainer-Vault unter dem jeweiligen Projektordner für diesen Skill.
