# BILLX API – fejlesztői dokumentáció

A natív BILLX REST API ügyfelek, számlák és termékek kezelésére. Ez **nem** a Billingo-kompatibilis BLNG API.

## Kapcsolódás

- Bázis URL: `https://billx.hu/api/billx`
- Formátum: JSON, UTF-8
- Hitelesítés: `Authorization: Bearer SAJAT_API_KULCS`
- API-kulcs: bejelentkezés után a BILLX Dashboardon

```http
GET https://billx.hu/api/billx/customers
Authorization: Bearer SAJAT_API_KULCS
Accept: application/json
```

Az API-kulcsot csak szerveroldalon tárold. Ne tedd böngészőben futó JavaScriptbe, verziókezelőbe vagy URL-paraméterbe.

## Tesztmód

Fejlesztői kulcs: `test_billx_api_key_demo_12345678`

A tesztkulcs nem hoz létre számlát és nem küld adatot a NAV-nak. Jelenleg a partnerlista és a számlalétrehozás szimulálható vele. A válaszban `"test_mode": true` és a feloldott ÁFA-adatokat tartalmazó `vat_summary` szerepel. A tesztkulccsal a `GET /vat-profiles` válaszában kapott rendszerprofilok – például az AAM – azonosítója használható. Fiókspecifikus profil és számlatömb-alapérték csak saját API-kulccsal érhető el.

```bash
curl -X POST 'https://billx.hu/api/billx/invoices' \
  -H 'Authorization: Bearer test_billx_api_key_demo_12345678' \
  -H 'Content-Type: application/json' \
  -d '{
    "customer_id": 1,
    "invoice_type": "invoice",
    "fulfillment_date": "2026-08-27",
    "due_date": "2026-09-03",
    "payment_method": "transfer",
    "currency": "HUF",
    "send_email": false,
    "submit_to_nav": false,
    "items": [{"name":"Teszt szolgáltatás","unit":"db","quantity":1,"unit_price":10000,"vat_rate":27}]
  }'
```

## Végpontok

A nyugták fejlesztői előzetesét és aktuális korlátait a lentebbi „Nyugták” fejezet ismerteti.

| Metódus | Útvonal | Funkció |
|---|---|---|
| `GET` | `/customers?page=1&per_page=20` | Partnerek listája (maximum 100/oldal) |
| `POST` | `/customers` | Partner létrehozása |
| `GET` | `/customers/{id}` | Partner lekérése |
| `PUT` | `/customers/{id}` | Partner módosítása |
| `DELETE` | `/customers/{id}` | Partner törlése |
| `GET` | `/invoices?page=1&per_page=20` | Számlák listája |
| `POST` | `/invoices` | Számla, díjbekérő vagy piszkozat létrehozása |
| `GET` | `/invoices/{id}` | Bizonylat és tételeinek lekérése |
| `DELETE` | `/invoices/{id}` | Bizonylat `cancelled` állapotba helyezése |
| `GET` | `/products` | Termékek listája |
| `POST` | `/products` | Termék létrehozása |
| `GET` | `/vat-profiles` | Használható ÁFA-profilok |
| `GET` | `/bank-accounts` | A cég bankszámlája |
| `GET` | `/receipt-series` | Nyugtasorozatok (fejlesztői előzetes) |
| `POST` | `/receipts` | Customer nélküli nyugta (staging / tesztmód) |
| `GET` | `/receipts?page=1&per_page=20` | Nyugták listája |
| `GET` | `/receipts/{id}` | Nyugta és snapshot tételek |
| `GET` | `/receipts/{id}/pdf` | Nyugta PDF, Bearer-hitelesítéssel |

A `/partners` a `/customers`, a `/documents` pedig az `/invoices` kompatibilitási álneve. Új integrációban a `customers` és `invoices` útvonalakat használd.

## Partner létrehozása

Csak a `name` kötelező; számlázáshoz add meg a teljes címet és – ha van – az adószámot is.

```json
{
  "name": "Minta Kft.",
  "email": "[email protected]",
  "tax_number": "12345678-1-42",
  "phone": "+36301234567",
  "postal_code": "1111",
  "city": "Budapest",
  "address": "Minta utca 1."
}
```

Siker: HTTP `201`; a válasz `id` mezője használható a számla `customer_id` mezőjében.

## Számla létrehozása

Kötelező mezők: `customer_id`, `invoice_type`, `fulfillment_date`, `due_date`, `payment_method`, `items`.

```json
{
  "customer_id": 123,
  "invoice_type": "invoice",
  "fulfillment_date": "2026-08-27",
  "due_date": "2026-09-03",
  "payment_method": "transfer",
  "currency": "HUF",
  "notes": "Köszönjük a vásárlást!",
  "send_email": true,
  "submit_to_nav": true,
  "items": [{
    "name": "Webfejlesztés",
    "description": "Augusztusi munkák",
    "unit": "óra",
    "quantity": 10,
    "unit_price": 15000,
    "vat_profile_id": 1,
    "is_intermediated_service": false
  }]
}
```

- `invoice_type`: `invoice`, `proforma` vagy `draft` (kötelező).
- `unit_price`: nettó egységár.
- `currency`: alapértéke `HUF`; devizánál szükség lehet `exchange_rate` értékre.
- `block_id`: opcionális számlatömb-azonosító. Hiányában alapértelmezett számlatömb szükséges.
- `send_email`: számlánál és díjbekérőnél alapértelmezetten `true`, piszkozatnál tiltott. Ha a számlatömbben a tulajdonosi értesítés be van kapcsolva, az ügyfélértesítés és a tulajdonosi másolat `send_email: false` esetén is kimegy. Kikapcsolt tulajdonosi értesítésnél az API-paraméter határozza meg az ügyfélnek történő küldést.
- `submit_to_nav`: csak `invoice` esetén értelmezett, alapértelmezetten `false`.

Siker: HTTP `201`, az `id`, `invoice_number`, összegek, pénznem és NAV-státusz mezőkkel.

### ÁFA megadása

Éles integrációban először kérd le a `GET /vat-profiles` listát, majd küldd az azonosítót az `items[].vat_profile_id` mezőben. A régi `items[].vat_rate` mező visszafelé kompatibilis. Prioritás: `vat_profile_id` → `vat_rate` → számlatömb alapértéke → céges alapérték.

AAM jogállású cégnél a keret túllépését az API `AAM_THRESHOLD_WILL_BE_EXCEEDED` hibával megakadályozhatja.

## Lapozás és hibák

A lista-válasz mezői: `data`, `total`, `per_page`, `current_page`, `last_page`.

Gyakori státuszok: `201` létrehozva, `204` sikeres törlés választest nélkül, `400` hibás kérés, `401` hitelesítési hiba, `404` nem található, `422` érvénytelen ÁFA-adat, `500` szerverhiba. Hiba esetén az `error`, esetenként a `message` mezőt is naplózd.

## Példakódok

PHP, Python és Node.js minták: `https://billx.hu/api/billx/examples/`

## Nyugták – fejlesztői előzetes, v0.2 (2026-09-06)

A számítógéppel előállított nyugta modul tartalmazza a kiállítást, módosítást/sztornót, napi NAV M2M feldolgozást, PDF-kézbesítést és admin kezelést. **Az éles kiállítás cégenként engedélyezett**: a GFDMM éles NAV-kapcsolata ellenőrzött, nyugtakiállítása aktív. Más céghez külön ellenőrzés és aktiválás szükséges. Beállítás: `/admin/receipt-nav`; bizonylatok: `/admin/receipts`. Ez nem e-pénztárgépes e-nyugta. Üzemeltetés: `/docs/RECEIPTS.md`.

### Nyugtatömbök kezelése az adminban

Az **Admin → Számlázás → Nyugtatömbök** oldalon (`/admin/receipt-series`) létrehozhatsz és szerkeszthetsz nyugtatömböket, kijelölheted az alapértelmezettet, illetve kikapcsolhatod vagy aktiválhatod őket. Az első létrehozott tömb automatikusan alapértelmezett. A tömb létrehozása önmagában nem engedélyezi az éles nyugtakiállítást.

Az API-kérés `series_id` mezőjében a kiválasztott **nyugtatömb** azonosítóját add meg, amelyet az admin listája vagy a `GET /receipt-series` ad vissza. Számlatömb-azonosítót ne használj. Ha nem küldesz `series_id`-t, a cég alapértelmezett nyugtatömbjébe kerül a bizonylat. Hibás, idegen vagy inaktív tömb megadásakor a kérés hibával leáll; nem választunk helyette másik tömböt.

Például `"series_id": 12` esetén a 12-es nyugtatömbbe készül a nyugta. A sikeres válasz `series_id` mezőjében a ténylegesen használt tömb szerepel. Az alapértelmezett megváltoztatása csak az új kéréseket érinti: idempotens ismétlés az eredeti nyugtát és tömböt adja vissza.

Használt tömb előtagja és számozása rögzített; neve és ÁFA-alapértéke módosítható. Az utóbbi csak az új nyugtákra hat. Törlés helyett kikapcsolás van. Az alapértelmezett tömb kikapcsolása előtt másik aktív tömböt kell kijelölni, ha van ilyen.

### Customer nélküli létrehozás

`POST /receipts`, `Content-Type: application/json`, opcionális `Idempotency-Key` fejléc.
A szokásos demo kulcs itt is használható: `test_billx_api_key_demo_12345678`.
Az ÁFA-azonosítót mindig a `GET /vat-profiles` válaszából válaszd ki; az alábbi `1` csak példa.

```json
{
  "external_id": "web-order-84729",
  "fulfillment_date": "2026-09-06",
  "fulfillment_at": "2026-09-06T10:32:11+02:00",
  "paid_at": "2026-09-06T10:32:11+02:00",
  "payment_completed": true,
  "customer_type": "individual",
  "invoice_requested": false,
  "payment_method": "card",
  "delivery_email": "[email protected]",
  "currency": "EUR",
  "exchange_rate": "392.41",
  "items": [
    {
      "name": "Szolgáltatás",
      "quantity": 1,
      "gross_unit_price": "9.99",
      "vat_profile_id": 1
    }
  ]
}
```

Kötelező: `external_id`, `fulfillment_date`, `paid_at`, `payment_completed`, `customer_type`, `invoice_requested`, `payment_method`, `currency`, `items`. Tételenként `name`, pozitív `quantity`, nem negatív `gross_unit_price`. 1–100 tétel adható meg, a végösszeg pozitív. Pénzügyi adatok legfeljebb 6 tizedesjegyű decimális stringként vagy JSON számként küldhetők. A válasz pénzügyi értékei decimális stringek, például `"gross_total": "9.99"`.

A `payment_completed: true`, `customer_type: "individual"`, `invoice_requested: false` kifejezett kliensnyilatkozat kötelező. Nem készül nyugta teljes fizetés nélkül, céges vásárláshoz vagy számlakérés esetén. A payment provider ellenőrzése a kliens feladata.

A dátum `YYYY-MM-DD`, az időpont RFC3339 másodperc és explicit időzóna. Jövőbeni fizetés/teljesítés elutasított. `fulfillment_at` megadásakor pontos időbeli sorrendet ellenőrzünk; ha csak `fulfillment_date` van, a napot Europe/Budapest időzónában vizsgáljuk, a napon belüli sorrend igazolása a kliens felelőssége. Webhook esetén add át a teljesítés tényleges időpontját is.

Opcionális: `series_id`, `delivery_email`, `notes`, `payment_reference`, `items[].description`, `items[].unit` (alapérték: `db`). Sorozat elhagyásakor egyetlen alapértelmezett sorozat szükséges. ÁFA-prioritás: tétel → sorozat → cég; minden esetben aktív és engedélyezett profil kell. Demo módban csak rendszerprofil és az `1` demo sorozat használható, profilazonosító szükséges. Kezdetben százalékos ÁFA, AAM és TAM támogatott.

HUF/EUR/USD támogatott. Az `exchange_rate` opcionális: EUR/USD esetén hiányzó vagy null értéknél a BillX a legutóbbi érvényes MNB-cache árfolyamát használja. Az összes devizát naponta 00:05-kor (budapesti idő) frissítjük a legutóbb közzétett MNB-jegyzésből, hiba esetén a következő órában újrapróbáljuk; a kiállítás a régi cache-ből folytatódik. Az aznap már rögzített automatikus árfolyamot a későbbi sikeres frissítés sem változtatja meg. Explicit pozitív árfolyam felülírja az automatikus lekérdezést. HUF-hoz `1` az alapérték és az egyetlen elfogadott érték. A válaszban az alkalmazott `exchange_rate` és az `exchange_rate_source` (`mnb`, `provided`, `huf`) szerepel. Az ismétlés a mentett árfolyamot használja; a korrekció is ezt örökli. Csak akkor érkezik `503 RECEIPT_EXCHANGE_RATE_UNAVAILABLE`, ha az adott devizához sem MNB-adat, sem érvényes cache nincs; ilyenkor explicit árfolyam szükséges. A 900 000 HUF-ot elérő ügylet elutasított. Az AAM-ellenőrzés a BillX számla- és nyugtaadatait együtt veszi figyelembe.

`customer_id`, `customer_name`, `customer_address`, `customer_tax_number`, nettó ár és minden ismeretlen mező elutasított. Név, cím, adószám, telefon vagy e-mail nem szükséges. Az opcionális e-mail nem hoz létre customer rekordot; a háttérfeldolgozó PDF-kézbesítési feladatot készít belőle.

### Idempotencia és sorszámozás

Azonos cégen belül az `external_id` egyedi. Azonos normalizált kérés ismétlése `200` státusszal a meglévő nyugtát adja, eltérő tartalom `409 RECEIPT_EXTERNAL_ID_CONFLICT`. Ugyanez érvényes az opcionális, 1–191 látható ASCII karakteres `Idempotency-Key` fejlécére. Az új nyugta válasza `201`.

A sorrendben kiosztott `NYUGTA-IDC-2026/00000001` formájú sorszám a nyugtával, tételekkel és audittal közös adatbázis-tranzakcióban készül. Évváltáskor új éves számsor indul. A fix `NYUGTA-` jelölés megkülönbözteti a nyugtákat a `BILLX-` számláktól, azonos `IDC` tömbelőtag mellett is. A tömbelőtag és a kitöltési hossz sorozatonként konfigurálható. Az idempotens újrakérés nem foglal sorszámot.

### Megtekintés és PDF

A válasz tartalmazza az `id`, `series_id`, `receipt_number`, `status`, `issue_date`, `fulfillment_date`, `paid_at`, `currency`, `gross_total`, `huf_gross_total`, `items`, `vat_summary`, `external_id`, `receipt_url`, `pdf_url` és `nav_report_status` mezőket. A publikus `/r/{token}` oldal biztonságos véletlen linkkel érhető el; PDF letöltés az oldalon. A `pdf_url` API-végpont Bearer-hitelesítést kér. A kibocsátó és az ÁFA kiállításkori snapshotból jelenik meg; későbbi profilváltozás nem írja át a bizonylatot.

A tesztmód determinisztikus `TEST-NY-...` számot ad, de nem ment nyugtát, nem foglal éles sorszámot, nem érinti az AAM-keretet és nem küld a NAV-hoz. `receipt_url` és `pdf_url` ilyenkor `null`, az állapot `not_submitted_test_mode`. A demo nyugta később azonosítóval nem kérhető le.

### Módosítás, sztornó és NAV

`POST /receipts/{id}/cancel`: `{"external_id":"order-cancel","reason":"Teljes visszatérítés"}`.
A sztornó a korábbi korrekciók után fennmaradó teljes összeget fordítja vissza.

`POST /receipts/{id}/modify`: egytételes nyugtához `{"external_id":"order-refund-1","reason":"Részleges visszatérítés","gross_delta":"-3.00"}`.
Többtételes nyugtához `gross_delta` helyett `"items":[{"position":0,"gross_delta":"-3.00"}]`; a pozíció az eredeti tétel nullától induló indexe. Összeg legfeljebb két tizedesjeggyel, az eredeti pénznemben. Pozitív tételkorrekcióhoz `payment_completed:true` és tényleges, RFC3339 `paid_at` is szükséges.

Mindkét művelet kötelező `external_id` és `reason` mezőt vár. Opcionális `effective_date`: eredeti kiállítás és mai nap közötti dátum, alapértéke mai nap. A módosító/sztornó okirat saját sorszámot és PDF-et kap az eredeti tömbben; hivatkozik az eredetire. Az eredeti snapshot nem változik. ÁFA, deviza és árfolyam az eredeti nyugtából származik. A pénz visszatérítése a kliens feladata. HTTP új okirat 201, idempotens ismétlés 200; konfliktus 409. Demo nyugta nem módosítható (404), DELETE nincs.

A `nav_report_status` értékei: `pending` (napi zárás), `queued`, `sending`, `retry`, `uncertain`, `failed`, `accepted`.
A háttérfeldolgozó kizárólag lezárt Budapest szerinti kiállítási napokat összesít tömb, deviza és árfolyam szerint. A NAV elfogadása csak `accepted`. Bizonytalan beküldésnél listából és teljes részletekből egyeztet; egyezés hiányában kézi ellenőrzést kér, nem küld újra vakon.

`delivery_email` megadásakor a PDF automatikusan tartós kézbesítési sorba kerül, az ötpercenként futó feldolgozó küldi az ügyfélnek. Külön `send_email` kapcsoló nem kell. A módosítás és sztornó PDF-je is automatikusan az eredeti címre megy. E-mail-cím nélkül nincs e-mailes kiküldés; a válaszban `delivery_status`: `not_requested`, `pending`, `sending`, `sent`, `uncertain`, `failed`. SMTP-bizonytalanság nem indít automatikus dupla kézbesítést. Az e-mail nem kerül a dokumentumba vagy ügyfélnyilvántartásba.

`GET /receipts` szűrői: `from`, `to` (kiállítási dátum, YYYY-MM-DD), `series_id`, `currency`, `status`, `document_kind`, `external_id` (pontos egyezés), `page`, `per_page` (max. 100).

Éles kiállításhoz ellenőrzött production NAV-konfiguráció, érvényes ÁFA-hozzárendelés és 15 percen belül lefutott háttérfeldolgozó szükséges. Devizaárfolyam: 1–1000, max. négy tizedes. A globális és a céges engedélyezés egyaránt szükséges.

### Receipt hibák

A válasz `{"error":"KOD","message":"Leírás"}` formájú. `400`: hibás JSON; `401`: hitelesítés; `404`: nem található/idegen nyugta vagy nem támogatott útvonal; `409`: idempotenciakonfliktus; `413`: túl nagy kérés; `422`: validáció; `503 RECEIPTS_DISABLED`: nincs telepítve/engedélyezve; `500`: belső hiba, `request_id` hivatkozással.

Üzleti kódok: `RECEIPT_PAYMENT_NOT_COMPLETED`, `RECEIPT_PAYMENT_AFTER_FULFILLMENT`, `RECEIPT_INVOICE_REQUESTED`, `RECEIPT_BUSINESS_CUSTOMER`, `RECEIPT_NOT_ALLOWED_AMOUNT_LIMIT`, `RECEIPT_INVALID_VAT_PROFILE`, `RECEIPT_SERIES_NOT_FOUND`, `RECEIPT_SERIES_INACTIVE`, `AAM_THRESHOLD_WILL_BE_EXCEEDED`. Egyéb mezőhibák: `RECEIPT_INVALID_REQUEST`.

### Consumer checkout

Magánszemély, teljes fizetés és nincs számlakérés: a kliens nyugtát kérhet a modul éles elkészülte után. Név/cím bekérése nem szükséges. „Cégként kérek számlát” vagy „Számlát kérek”: a meglévő customer + `/invoices` folyamat használandó. Egy fizetéshez a kliens egy bizonylattípust válasszon és tároljon. A nyugtás éles checkout pilot még nem része ennek a verziónak.

További hibák: `RECEIPT_ALREADY_CANCELLED` (409), `RECEIPT_CORRECTION_NOT_ORIGINAL` (409), `RECEIPT_CORRECTION_EXCEEDS_REMAINING` (422), `RECEIPT_CORRECTION_ITEMS_REQUIRED` (422), `RECEIPT_NAV_NOT_READY` / `RECEIPT_WORKER_NOT_READY` (503), `RECEIPT_NAV_VAT_MAPPING_INVALID` / `RECEIPT_NAV_CURRENCY_INVALID` (422).
Kód Példák

Letölthető integráció példák különböző nyelveken: