# Konvenciók

> Alapcím, tartalomtípusok, pénznem, lapozás, időbélyegek, országkódok, demóadatok jelölése és más konvenciók, amelyek a Haszno minden API-végpontjára vonatkoznak.

## Alapcím

Minden végpont ugyanarról az alapcímről érhető el:

```http
https://haszno.com
```

Az útvonalakban nincs verzióelőtag. Az API verziószámozás nélküli fejlesztői előzetes; a verziószámozási szabályzatot a [Változásnapló](changelog.md) tartalmazza.

## Tartalomtípusok

| Irány | Fejléc | Érték |
|---|---|---|
| Kéréstest | `Content-Type` | `application/json` |
| JSON-válaszok | `Content-Type` | `application/json` |
| `/api/blog/rss.xml` | `Content-Type` | `application/rss+xml; charset=utf-8` |
| `/api/blog/feed.json` | `Content-Type` | `application/feed+json; charset=utf-8` |
| `/api/blog/:slug.md` | `Content-Type` | `text/markdown; charset=utf-8` |
| `/llms.txt` | `Content-Type` | `text/plain` |

Minden kérésnél, amelynek törzse van, add meg a `Content-Type: application/json` fejlécet. Ha a kéréstest nem érvényes JSON, vagy nem felel meg az ellenőrzésnek, `400`-at kapsz. A fiókvégpontok (`POST /api/auth/register`, `POST /api/auth/login`, `POST /api/auth/tokens`) akkor is `400`-at adnak, ha a fejléc hiányzik.

## Pénznem és árak

Minden pénzügyi értéket **magyar forintban (HUF, ISO 4217) egész számként** adunk meg. Törtfilléres ár nincs.

| Utótag | Jelentés | Példamező |
|---|---|---|
| `Net` | ÁFA nélkül | `costNet`, `marginNet` |
| `Gross` | ÁFÁ-val | `priceGross`, `rrpGross`, `floorGross` |

Az alapértelmezett áfakulcs 27 % (`vatPct: 27`), ami megfelel a magyarországi általános kulcsnak. A demó-végpontokon a `vatPct` paraméterrel egyetlen kérés erejéig módosíthatod ezt; a beállítás nem marad meg.

A haszon- és árpadlóképletek, ahol `net = priceGross / (1 + vatPct / 100)`:

- `marginNet = round(net − costNet)`: a nettó eladási ár mínusz a nettó bekerülési ár, egész forintra kerekítve.
- `marginPct = (net − costNet) / net × 100`, egy tizedesjegyre kerekítve; `0`, ha a `net` nem pozitív.
- A `marginNet` és a `marginPct` is `null`, ha a `costNet` értéke `null`.
- `floorGross = costNet / (1 − minMarginPct / 100) × (1 + vatPct / 100)`, felfelé kerekítve a következő 90 Ft-ra végződő árra (például 72 431 → 72 490).

A `marginPct` legfeljebb egy tizedesjegyű százalékérték, például `31.9`. Minden más ár-, bekerülés- és haszonmező egész szám.

## Null-szemantika

A `null` ár- vagy bekerülésmező azt jelenti, hogy az adat **ismeretlen vagy nem elérhető** — nem azt, hogy nulla. A `null` és a `0` eltérő eset:

| Mező | A `null` jelentése |
|---|---|
| `costNet: null` | A nettó bekerülési ár ismeretlen; a `marginNet` és a `marginPct` értéke `null`, a `recommended.status` pedig `nincs_besz_ar`. |
| `marketMinGross: null` | A kért szállítási piacokon nem találtunk piaci árat. |
| `rrpGross: null` | A beszállító nem adott meg ajánlott fogyasztói árat. |

Árhasonlításban és haszonszámításban ne helyettesítsd a `null` értéket nullával.

## Időbélyegek

Minden időbélyeg **ISO 8601 formátumú, UTC-idős sztring**, például `2026-11-10T00:00:00.000Z`.

Az API nem használ Unix epoch egész számokat. A demó időbélyegei, például a `fetchedAt` és az `addedAt`, rögzített, szintetikus értékek, amelyek nem valódi eseményeket tükröznek; a `GET /api/impact` `from`–`to` időablakát viszont az aktuális időpontból számolja a rendszer.

## Országkódok és piaci paraméterek

Az országkódok az **ISO 3166-1 alpha-2** szabványt követik (két nagybetű, például `HU`, `SK`).

A `shippingMarkets` lekérdezési paraméter egy vagy több, vesszővel elválasztott országkódot fogad el. Érvényes értékek:

| Kód | Ország |
|---|---|
| `HU` | Magyarország |
| `SK` | Szlovákia |
| `DE` | Németország |
| `AT` | Ausztria |
| `CZ` | Csehország |
| `PL` | Lengyelország |
| `RO` | Románia |

Ha kihagyod, az alapértelmezett: `HU`.

A válaszokban a `marketByCountry` három rögzített kulcsot tartalmaz: `hu` (Magyarország), `sk` (Szlovákia) és `eu` (az összes többi támogatott piac — DE, AT, CZ, PL, RO). Egy slot `null`, ha az adott piacra nincs piaci ár.

## Lapozás

A lapozás csak négy végpontnál alkalmazható:

| Végpont | Stílus |
|---|---|
| `GET /api/catalogue/products` | Oldalszámalapú: `?page=` (1-től kezdve, alapértelmezés 1) + `?pageSize=` (1–100, alapértelmezés 24). Visszaadja: `{ items, total, facets }`. |
| `GET /api/rule-runs` | Limit: `?limit=N` (alapértelmezés 30, maximum 200). |
| `GET /api/proposals` | Limit: `?limit=N` (alapértelmezés 50, maximum 200). |
| `GET /api/blog` | Oldalszámalapú: `?page=` (1-től kezdve, alapértelmezés 1), rögzített 10-es oldalmérettel. Visszaadja: `{ posts, total, page, perPage }`. |

Minden más listavégpont **egyetlen válaszban adja vissza az összes egyező sort**. Ezeken a végpontokon nincs kurzor, `Link` fejléc vagy `nextPage` mező. Az esetlegesen megadott lapozási paramétereket figyelmen kívül hagyják.

> [!NOTE]
> `GET /api/orders` legfeljebb 50 rendelést ad vissza, és nincs mód a továbboldalozásra.

## Demó- és szintetikus adatok

A `https://haszno.com` nyilvános demója szintetikus mintaadatokat szolgáltat a hitelesítés nélküli GET-végpontokon. Legfelső szintű mezőt csak a `GET /api/settings` és a `GET /api/impact` objektumválasza tartalmaz:

```json
"demo": true
```

A tömbválaszokban nincs ilyen mező. Ezekről a jelölésekről ismered fel őket:

| Jelölés | Hol | Jelentés |
|---|---|---|
| `"sku": "DEMO-{id}"` | Termékek | Helyőrző SKU, nem valódi beszállítói kód. |
| `"source": "mock"` | Piaci áradatok | Nem ellenőrzött versenytársi ár. |
| `"partnerCount": 0` | Kategóriák | Valódi csatlakoztatott partnerek száma; a demóban mindig 0. |

> [!WARNING]
> `GET /api/orders` és `GET /api/proposals` a nyilvános demóban `[]`-t ad vissza. Ez szándékos; a demó nem szimulálja a rendeléselőzményeket. Az üres tömböt ne értelmezd úgy, hogy a végpont éles adattal is így viselkedik.

## Az API-üzenetek nyelve

Az API hibaüzenetei és az állapot-enumerációs sztringek jelenleg **csak magyarul** érhetők el, függetlenül az `Accept-Language` fejléctől vagy a dokumentáció megjelenítési nyelvétől.

Példák:

| Sztring | Kontextus |
|---|---|
| `"error": "Ismeretlen cikkszám: DEMO-99"` | 400-as hiba `POST /api/webhooks/order` esetén |
| `recommended.status: "versenykepes"` | Az ajánlott ár a legolcsóbb piaci ár alá megy, és nem esik az árpadló alá |
| `recommended.status: "nem_versenykepes"` | A piaci ár alá menni sértené a minimális haszonkulcsot; az ajánlott ár az árpadló |
| `recommended.status: "nincs_piaci_ar"` | Nincs ismert piaci ár; az ajánlott ár az ajánlott fogyasztói ár vagy az árpadló |
| `recommended.status: "nincs_besz_ar"` | A beszerzési ár ismeretlen; árjavaslat nem készíthető |

Ez az általános elérhetőség előtt megváltozik. A `recommended.status` értékeit a [GET /api/products](api-reference.md#get-products) leírása részletezi; a HTTP-státuszkódokat a [Hibák](errors.md) oldalon találod.

## CORS

Az API **szigorú azonos eredetű** CORS-t alkalmaz. Az engedélyezett eredetet a szerveren a `CORS_ORIGIN` környezeti változó állítja be; ha ez nincs megadva, a `PUBLIC_URL`, majd a `http://localhost:5180` az alap. A `haszno.com`-on az engedélyezett eredet az éles előlap.

| CORS-beállítás | Érték |
|---|---|
| Engedélyezett metódusok | `GET POST PUT PATCH DELETE OPTIONS` |
| Engedélyezett kérésfejlécek | `Content-Type`, `Authorization`, `x-api-key`, `x-support-token` |
| Hitelesítési adatok (sütik) | Átküldve (`credentials: true`) |
| Előzetes kérés gyorsítótára | 600 másodperc |

A nem engedélyezett eredetről érkező kérések el vannak utasítva. Alapcímként használd a `https://haszno.com` címet.

## Kéréstestméret-korlát

| Végpont | Korlát |
|---|---|
| `POST /api/feeds/import` | 10 MB |
| Minden más végpont | 1 MB |

A korlátot meghaladó kérések `413 Content Too Large` választ kapnak. Lásd: [Hibák](errors.md#status-codes).
