All user-facing output (confirmations, summaries, error messages) is in Hungarian because the end user is Hungarian and the invoice itself is a Hungarian legal document. Internal reasoning and this file are in English so external reviewers can audit the logic.
Technical foundations
- CLI entry point:
python "${CLAUDE_PLUGIN_ROOT}/scripts/szamlazz_cli.py" <subcommand> - Subcommands:
doctor,init,invoice,storno,proforma,pdf-fetch,xml-fetch,taxpayer - Config lookup: the CLI resolves config from these locations in order:
$SZAMLAZZ_CONFIG_DIRenv var./.szamlazz/in the current working directory- OS default (
~/Library/Application Support/szamlazz/on macOS,~/.config/szamlazz/on Linux,%APPDATA%\szamlazz\on Windows) ~/.claude/szamlazz/(legacy fallback)
- API key: loaded from
$SZAMLAZZ_AGENT_KEYenv var, or from<config_dir>/.env - PDF output: default
./invoices/YYYY-MM/<invoice_number>.pdf(current working directory)
Reference files (read these if you need details)
${CLAUDE_PLUGIN_ROOT}/skills/szamlazz/references/api-reference.md— Agent API endpoints, headers, error codes, VAT rates${CLAUDE_PLUGIN_ROOT}/skills/szamlazz/references/xml-templates.md— XML templates for every operation
Critical calculation rule
szamlazz.hu does NOT compute totals — the client must send exact values:
nettoErtek = mennyiseg × nettoEgysegar (2 decimals, ROUND_HALF_UP)
afaErtek = nettoErtek × afakulcs / 100 (2 decimals, ROUND_HALF_UP)
bruttoErtek = nettoErtek + afaErtek
The build_invoice_xml.py script uses decimal.Decimal internally. If you
manually provide values, use 2-decimal precision. Mismatches produce error
codes 57 / 259-264.
szamlazz.hu API limits
- No seller data endpoint — we store it in
seller.yaml. - No partner list endpoint — we build
partners.yamlcache locally. - Tax number → NAV data: use
szamlazz_cli.py taxpayer <tax_id> --json. - Old invoice → customer data: use
szamlazz_cli.py xml-fetch --invoice-number <num>.
Canonical JSON schema (for invoice/proforma)
The underlying build_invoice_xml.py expects these exact field names:
{
"beallitasok": {
"szamlaagentkulcs": "<injected by CLI from env>",
"eszamla": false,
"szamlaLetoltes": true
},
"fejlec": {
"szamlaszamElotag": "SOC",
"keltDatum": "2026-04-12",
"teljesitesDatum": "2026-04-12",
"fizetesiHataridoDatum": "2026-04-19",
"fizmod": "Átutalás",
"penznem": "HUF",
"szamlaNyelve": "hu"
},
"elado": {
"bank": "<from seller.yaml>",
"bankszamlaszam": "<from seller.yaml>",
"emailReplyto": "<from seller.yaml>"
},
"vevo": {
"nev": "Example Kft.",
"irsz": "1111",
"telepules": "Budapest",
"cim": "Example utca 1.",
"adoszam": "12345678-2-42",
"email": "info@example.hu"
},
"tetelek": [
{
"megnevezes": "Item description",
"mennyiseg": "1",
"mennyisegiEgyseg": "db",
"nettoEgysegar": "100000",
"afakulcs": "27"
}
]
}
Use keltDatum / teljesitesDatum / fizetesiHataridoDatum (with the
Datum suffix) — NOT kelt / teljesites / fizetesiHatarido.
User request: $ARGUMENTS
Decide which sub-command matches the request and execute the appropriate flow:
- New invoice (default, when no explicit sub-command): follow "New invoice flow" below.
- Storno: user says "sztornó", "sztornózd", or provides
storno <invoice_number>. - Proforma (díjbekérő): user says "díjbekérő", "proforma", "előlegbekérő".
- PDF re-download: user says "töltsd le", "pdf ", or similar.
- Tax number / partner lookup: user says "ki ez a cég", "adószám ", "partner ".
- Sync customer data from an old invoice: user says "hozd be a régi vevőt".
STEP 0 — First-run detection
Before doing anything else, run doctor and parse the JSON output:
python "${CLAUDE_PLUGIN_ROOT}/scripts/szamlazz_cli.py" doctor --json
Parse the resulting JSON and branch:
status == "ready"
→ Skip to STEP 1.
status == "no_config_dir"
→ Run the init flow (STEP 0.A).
status == "missing_seller"
→ Run the seller setup flow (STEP 0.B).
status == "missing_key"
→ Run the API key flow (STEP 0.C).
status == "error"
→ Show the error message to the user and suggest manual fix.
→ Do NOT proceed.
STEP 0.A — New user first-run init flow
Say to the user:
"Szia! Látom ez az első futás ezen a gépen. 3 gyors kérdéssel beállítom a szamlazz.hu pluginodat. Minden adatot a saját gépedre mentek, nincs felhőbe feltöltés."
AskUserQuestion #1 — "Szamlazz.hu Agent API kulcsod?" Options:
- "beírom most"
- "már beállítottam SZAMLAZZ_AGENT_KEY-t a shell-ben"
- "hogyan szerezzek ilyet?"
If the user chooses "hogyan szerezzek ilyet?", show:
"1. Jelentkezz be szamlazz.hu-ra 2. Beállítások → Számla Agent kulcsok 3. 'Új kulcs' gomb → 'Számla kiállítás' jogosultság → generálás 4. Másold ki a kulcsot (csak most látod, utána el lesz rejtve)"
Then re-ask question #1.
If "beírom most", use AskUserQuestion with freeform input. Store the value in a local variable — never echo it to stdout or into a shell command that could be logged.
If "már beállítottam", check $SZAMLAZZ_AGENT_KEY. If empty, warn and re-ask.
AskUserQuestion #2 — "A saját céged adószáma?"
Freeform input. Validate against the pattern \d{8} or \d{8}-\d-\d{2}. Strip
whitespace. On mismatch, re-ask with a format hint.
Auto-fetch taxpayer info via NAV:
python "${CLAUDE_PLUGIN_ROOT}/scripts/szamlazz_cli.py" taxpayer <TAX_ID> --json
If the response status is found, show the user:
"NAV-ból ezt találtam: Cégnév: Irányítószám: Település: Cím:
Helyes?"
AskUserQuestion #3 — "Helyes?"
- "igen, minden stimmel"
- "egy mezőt javítok"
- "teljesen manuálisan adom meg"
If "egy mezőt javítok", AskUserQuestion which field, then re-ask the value, then show the updated block again.
If "manuálisan", AskUserQuestion for each field individually.
AskUserQuestion #4 — "Bankszámlaszámod (a számlán fog szerepelni)?"
Freeform input. Validate pattern \d{8}-\d{8}-\d{8} (accept with or without dashes).
Auto-detect bank name from the giro prefix:
python -c "import sys; sys.path.insert(0, r'${CLAUDE_PLUGIN_ROOT}/scripts'); from lib.bank_prefixes import detect_bank; print(detect_bank('<ACCOUNT_NUMBER>'))"
If the result is not None, show:
"Ez egy számla, ugye?"
AskUserQuestion: "igen" / "nem, más bank" (with freeform input fallback).
AskUserQuestion #5 (optional) — "KATA-s / Kisadózó vagy?" [igen / nem]
Finally, write everything to disk by calling the init subcommand:
python "${CLAUDE_PLUGIN_ROOT}/scripts/szamlazz_cli.py" init \
--cegnev "<name>" \
--irszam "<postal>" \
--telepules "<city>" \
--cim "<street_line>" \
--adoszam "<tax_id>" \
--bank-nev "<bank_name>" \
--bank-szamlaszam "<account>" \
--email "<email>" \
--kisadozo "<true|false>" \
--agent-key "<api_key>" \
--json
After success, say:
"✓ Beállítás kész. 3 fájl készült a mappában. Most folytatom az eredeti kéréseddel: "
Then proceed to STEP 1.
STEP 0.B — Seller-only setup (partial)
Same as 0.A but skip questions whose fields are already populated in seller.yaml.
STEP 0.C — API key only
Run only AskUserQuestion #1 from 0.A and write via:
python "${CLAUDE_PLUGIN_ROOT}/scripts/szamlazz_cli.py" init --agent-key "<key>" [...existing seller flags...]
STEP 1 — New invoice flow
1.1 Load seller + settings
Read the seller YAML via:
python -c "import sys, yaml; sys.path.insert(0, r'${CLAUDE_PLUGIN_ROOT}/scripts'); from lib.config import resolve_config_dir; print(yaml.safe_dump(yaml.safe_load((resolve_config_dir() / 'seller.yaml').read_text()), allow_unicode=True))"
Parse the YAML into your internal representation of the seller.
1.2 Identify the customer
If the user mentioned only a name → search partners.yaml by substring.
If only a tax number → search by tax_id, fallback to taxpayer subcommand.
If nothing → AskUserQuestion for name, postal code, city, address, email, tax_id.
1.3 Collect line items
Parse $ARGUMENTS. If ambiguous → AskUserQuestion per field (description,
quantity, unit, net unit price, VAT rate — 27/18/5/0/AAM).
1.4 Confirmation (MANDATORY)
Show a Hungarian summary and ask for confirmation:
Számla kiállítás megerősítése
Vevő: <name>
<postal> <city>, <street>
Adószám: <tax_id>
Tételek:
1. <item_1> — <qty> × <unit_price> = <net_total> (<vat>% ÁFA)
2. ...
Nettó: <sum_net>
ÁFA: <sum_vat>
Bruttó: <sum_gross>
Fizetési határidő: <date> (<days> nap)
Fizetési mód: <method>
Kiállítsam?
AskUserQuestion: "igen" / "nem" / "módosítsam".
1.5 Write JSON config + issue invoice
Write a JSON config to a temp file (use the Bash tool), then call the CLI:
python "${CLAUDE_PLUGIN_ROOT}/scripts/szamlazz_cli.py" invoice \
--config "$TMP_JSON" \
--output-dir "./invoices" \
--json
1.6 Error handling
If szamlazz_cli.py exits with non-zero, parse the stderr formatted error
block and present the recovery options to the user via AskUserQuestion.
See STEP 2 below for the mapping.
1.7 Partner cache update
After success, upsert the customer into partners.yaml. You can do this
inline with a short Python invocation:
python -c "import sys; sys.path.insert(0, r'${CLAUDE_PLUGIN_ROOT}/scripts'); from lib.partner_cache import PartnerCache, Partner; from lib.config import resolve_config_dir; cd = resolve_config_dir(); pc = PartnerCache.load(cd / 'partners.yaml'); pc.upsert(Partner(nev='<name>', adoszam='<tax_id>', irszam='<postal>', telepules='<city>', cim='<street>', email='<email>')); pc.save(cd / 'partners.yaml')"
1.8 Final output
✓ Számla kiállítva
Számlaszám: <invoice_number>
Vevő: <name>
Összeg: <net> / <vat> / <gross> HUF
PDF: <path>
Partner cache: frissítve
STEP 2 — Error recovery mapping
| Error code | Meaning | User options |
|---|---|---|
| 3 | Auth failed | "Új kulcsot írok be" (→ STEP 0.C), "Meglévő kulcsot ellenőrzöm", "Kilépek" |
| 54, 55 | e-Számla cert error | "Próbáld eszamla=false-szal", "Kilépek" |
| 57, 259-264 | Calculation mismatch | "Újraszámolom Decimallel", "Mutasd a részleteket", "Kilépek" |
| 136 | Billing balance | "Nyisd meg szamlazz.hu-t böngészőben", "Kilépek" |
Storno flow
- Ask for the invoice number to cancel.
- Optionally:
xml-fetchthe original for confirmation display. - Call:
python "${CLAUDE_PLUGIN_ROOT}/scripts/szamlazz_cli.py" storno \ --invoice-number "<num>" --reason "<reason>" --json - Show the storno number + PDF path to the user.
Proforma flow
Same as STEP 1 but use the proforma subcommand (which internally passes
--mode proforma to build_invoice_xml.py).
PDF re-download
python "${CLAUDE_PLUGIN_ROOT}/scripts/szamlazz_cli.py" pdf-fetch \
--invoice-number "<num>" --output-dir "./invoices" --json
Tax number lookup
python "${CLAUDE_PLUGIN_ROOT}/scripts/szamlazz_cli.py" taxpayer <tax_id> --json
On success:
✓ [Művelet típus] kiállítva / letöltve
- Számlaszám: [szamlaszam]
- Vevő: [név]
- Összeg: [nettó] / [áfa] / [bruttó] [pénznem]
- PDF: [relatív útvonal]
On error:
✗ Hiba a szamlazz.hu művelet során
Hibakód: [kód]
Üzenet: [szöveg]
Ok: [magyarázat]
Mit szeretnél?
1. [opció 1]
2. [opció 2]
3. Kilépek
- API key: NEVER print it to stdout. NEVER write a Bash command that
echoes
$SZAMLAZZ_AGENT_KEY. The Python scripts read it from env directly. - Mandatory confirmation: before issuing a REAL invoice, always show the summary and ask for confirmation. An invoice is a legal document and szamlazz.hu has no draft mode — what you send is real.
- No retry loops on errors: max 5 attempts per session. If an error repeats, explain it to the user and ask for guidance.
- Rounding: always 2 decimals, ROUND_HALF_UP. Decimal in Python, never float for money.
- Foreign currency: if
penznem != HUF, passarfolyamBankandarfolyamfields.arfolyam=0.0means "use today's MNB rate". - Date format: strictly
YYYY-MM-DD. Hungarian-style "2026. április 12." is NOT accepted. - Partner identifier caution: do not set
<azonosito>unless you know what you are doing — duplicate identifiers overwrite each other. - partners.yaml protection: never commit this file to any git repo.
The plugin's own
.gitignorealready excludes it. - Test mode: szamlazz.hu does NOT support client-side test mode. If the user says "teszt", "próba", warn them: a real invoice will be issued. Alternative: issue a 1 HUF invoice to yourself and immediately cancel it.
- Batch: one invoice per run. Do NOT loop unless the user explicitly requests it and confirms each invoice.