{
  "info": {
    "_postman_id": "3b9d7c52-1e4f-4a86-9c20-7f5e1d3a8b64",
    "name": "PosFix — API public de integrare",
    "description": "API-ul prin care sistemele externe ale unui comerciant (magazin online, ERP, contabilitate) îi citesc și îi scriu datele: catalog, stoc, prețuri, clienți, comenzi, vânzările din rapoartele Z, recunoașterea facturilor și evenimente prin webhook-uri.\n\nPentru API-ul de pe casa de marcat (bonuri fiscale, numerar, rapoarte X/Z tipărite) există colecția separată **PosFix — API terminal**.\n\n## Cheia\n\nModulul **„API Integrare”** e un serviciu contra cost: PosFix îl activează pentru comerciant și emite cheia. Cheia (`rk_…`) se arată **o singură dată**; PosFix păstrează doar amprenta ei, deci o cheie pierdută se înlocuiește, nu se recuperează. După înlocuire, cheia veche mai merge 24 de ore.\n\nSe trimite pe fiecare cerere:\n\n```\nX-Posfix-Api-Key: rk_…\n```\n\nComerciantul se ia **din cheie**. Nu există parametru de comerciant, deci o cheie nu poate citi datele altcuiva. O cheie poate fi limitată la anumite case de marcat: atunci vede doar vânzările lor.\n\nÎncepe cu `GET /api/public/v1/ping`: confirmă cheia și îți arată scope-urile ei.\n\n## Scope-uri\n\nFiecare cheie primește doar permisiunile alese la emitere. Scope-ul cerut de o cerere e scris la începutul descrierii ei.\n\n| Scope | Ce permite |\n|---|---|\n| `products:read` | Catalog, categorii, atribute, blocuri de conținut, liste de prețuri, prețuri. |\n| `inventory:read` | Stocul unui produs. |\n| `customers:read` / `customers:write` | Citește / creează clienți. |\n| `orders:read` / `orders:write` | Citește / creează comenzi, pune datele de livrare. |\n| `sales:read` | Case de marcat, rapoarte Z, bonuri. |\n| `docai:catalog` | Nomenclatorul propriu pentru recunoașterea facturilor. |\n| `docai:recognize` | Trimite facturi (PDF, fotografii) la recunoaștere — contra cost. |\n| `docai:read` | Citește facturile recunoscute și confirmă preluarea. |\n| `webhooks:manage` | Abonamente la evenimente. |\n\nFără scope-ul cerut, sau cu modulul „API Integrare” inactiv, răspunsul e `403`, **fără corp**. „Ping” nu cere nici scope, nici modul: dacă el merge și o cerere dă `403`, cauza e una dintre acestea două.\n\n## Limite\n\n- Pe cheie: o rezervă de **120 de cereri**, care se reface cu **2 pe secundă** (~120 pe minut, în ritm constant).\n- Peste limită: `429` cu `errorCode: RATE_LIMITED`, **fără** antet `Retry-After`. Reîncearcă după câteva secunde, cu pauze crescătoare.\n- O a doua limită, de **300 de cereri pe minut**, se numără pe adresa IP. Serviciul vede azi doar adresa gateway-ului, deci limita aceasta e comună tuturor integratorilor: un `429` poate veni și când cheia ta e sub limită.\n- Limita se verifică înaintea cheii: un `429` poate veni și pentru o cerere cu cheie greșită.\n\n## Erori\n\nRăspunsurile de eroare au mai multe forme. **Decide după statusul HTTP**; `errorCode` și `errors` ajută acolo unde există, iar `detail` e text pentru oameni și se poate schimba.\n\n| HTTP | Corp | Când |\n|---|---|---|\n| `401`, `403` | **fără corp** | Cheie lipsă sau invalidă; scope lipsă sau modul inactiv. Cele două `403` arată la fel: verifică-le cu „Ping”. |\n| `404` | fără corp, sau format simplu | Resursa nu există (sau e a altui comerciant). |\n| `400` | `{\"statusCode\":400,\"message\":\"…\",\"errors\":{\"câmp\":[\"…\"]},\"errorCodes\":{\"câmp\":[{\"code\":\"COD\",\"params\":{…}}]}}` | Parametru sau JSON de tip greșit, GUID invalid, validare pe parametri (formatul de legare). |\n| `400`, `404`, `409` | `{\"status\":…,\"title\":\"…\",\"detail\":\"…\",\"errorCode\":\"COD\",\"params\":{…}}` | Refuz de domeniu cu mesaj în `detail` (format simplu). |\n| `400`, `5xx` | `{\"type\":…,\"title\":\"COD\",\"status\":…,\"detail\":\"…\",\"errorCode\":\"COD\",\"params\":{…},\"errors\":{…},\"errorCodes\":{…}}` | `VALIDATION` (cu `errors` și `errorCodes` pe câmp), refuzul unui serviciu intern cu codul lui, `SERVICE_UNAVAILABLE` (503), `INTERNAL_ERROR` (500). |\n| `429` | `{\"status\":429,\"title\":\"Too Many Requests\",\"detail\":\"…\",\"errorCode\":\"RATE_LIMITED\"}` | Peste limită. |\n\nUn `503` înseamnă că un serviciu intern nu răspunde: reîncearcă. Un `401`/`403` poate apărea și când serviciul care verifică cheia sau modulul e căzut, fără să fie ceva greșit la tine.\n\n## Convenții\n\n- JSON cu nume de câmpuri în **camelCase**. Câmpurile fără valoare vin ca `null`, nu lipsesc.\n- Datele calendaristice au forma `yyyy-MM-dd`; momentele sunt ISO 8601 UTC, cu `Z`.\n- Sumele sunt numere JSON, în lei dacă nu se spune altfel.\n- **TVA:** `price` din catalog e prețul de la casă, **cu TVA**. La comandă, `lines[].unitPrice` se tratează **fără TVA** și TVA-ul se adaugă peste. Nu copia prețul din catalog direct în comandă.\n- Versiunea stă în cale (`/api/public/v1/`). În v1 se adaugă doar câmpuri și evenimente noi, nu se schimbă cele existente: **ignoră câmpurile pe care nu le cunoști**.\n- Un parametru-listă se repetă (`?categoryCode=A&categoryCode=B`); valorile booleene se scriu `true` / `false`.\n\n## Paginare\n\n- **Cu cursor** — produse, clienți: ceri `limit`, primești `nextCursor`; pagina următoare o ceri cu `cursor=<nextCursor>`, până când vine `null`.\n- **Cu pagini** — comenzi, rapoarte Z, bonuri: `page` (de la 1) și `pageSize`; răspunsul are totalul.\n\n## Webhook-uri: cum primești evenimentele\n\nPosFix trimite un `POST` JSON la adresa ta când se întâmplă ceva la comerciant. Te abonezi cu „Creează webhook” și alegi evenimentele.\n\n| Eveniment | Când | `data` |\n|---|---|---|\n| `product.created` | Produs nou în nomenclator. | `posfixId`, `code`, `name`, `description`, `barcode`, `price` (prețul implicit din nomenclator, nu cel de la casă), `currency`, `vatRate`, `isService` |\n| `product.updated` | Produs modificat. | `posfixId`, `code`, `name`, `description`, `currency`, `vatRate`, `isActive` (fără preț și cod de bare) |\n| `inventory.updated` | S-a schimbat stocul unui produs într-un depozit. | `posfixId`, `warehouseId`, `quantity`, `value`, `averageCost`, `changedAt` |\n| `order.created` | Comandă nouă (inclusiv din panou sau de la casă). | `id`, `number`, `status`, `changeType`, `total`, `paidSum`, `currency` |\n| `order.confirmed` | Comerciantul a confirmat comanda. | la fel |\n| `order.fulfilling` | Livrată parțial. | la fel |\n| `order.fulfilled` | Livrată integral. | la fel |\n| `order.closed` | Închisă manual. | la fel |\n| `order.cancelled` | Anulată. | la fel |\n| `order.partially_paid` | A crescut suma plătită, dar nu acoperă totalul. | la fel; `paidSum` în lei |\n| `order.paid` | Comanda e plătită integral. | la fel; `paidSum` în lei |\n| `docai.recognition.completed` | O factură a fost recunoscută (trimisă prin API sau încărcată în panou). | `id`, `status`, `source`, `externalRef`, `fileName`, `supplierName`, `supplierIdno`, `documentSeries`, `documentNumber`, `issueDate`, `total`, `currency`; datele complete — `GET /api/public/v1/docai/recognitions/{id}` |\n| `docai.recognition.failed` | Recunoașterea n-a reușit (fișier ilizibil, factură prea lungă, întrerupere). | la fel, plus `errorCode` |\n| `docai.recognition.reviewed` | Un om a verificat și a corectat factura în panou. | la fel; datele verificate — pe id |\n\nCorpul are același plic la orice eveniment:\n\n```json\n{ \"id\": \"7c1e9a52-3b4d-4f6e-8a90-1b2c3d4e5f60\", \"type\": \"order.paid\", \"createdAt\": \"2026-09-25T08:12:03.4567891Z\", \"version\": \"1\",\n  \"data\": { \"id\": \"3c9a1f52-7d3e-4b8a-a0c4-6e2b91f0d7a8\", \"number\": \"0000000042\", \"status\": \"Confirmed\", \"changeType\": \"paid\", \"total\": 745.8, \"paidSum\": 745.8, \"currency\": \"MDL\" } }\n```\n\n- `id` e id-ul livrării: același la fiecare reîncercare. **Deduplică după el** — o livrare poate sosi de două ori.\n- `createdAt` e momentul punerii în coadă, nu al evenimentului. Ordinea sosirii nu e garantată.\n\n### Antete\n\n| Antet | Valoare |\n|---|---|\n| `X-Posfix-Signature` | Semnătura, base64 |\n| `X-Posfix-Webhook-Id` | Id-ul livrării (= `id` din corp) |\n| `X-Posfix-Timestamp` | Momentul trimiterii, secunde Unix |\n| `X-Posfix-Event` | Tipul evenimentului |\n\n### Verificarea semnăturii\n\n```\nsignedString       = \"{X-Posfix-Webhook-Id}.{X-Posfix-Timestamp}.{corpul brut}\"\nX-Posfix-Signature = base64( HMAC_SHA256(signingSecret, signedString) )\n```\n\n- Cheia HMAC e **tot** secretul, cu prefixul `whsec_` cu tot, ca octeți UTF-8 — nu se decodează.\n- Semnează **corpul brut**, exact cum a sosit. Un JSON parsat și serializat din nou nu mai dă aceeași semnătură.\n- Compară în timp constant și respinge livrările cu `X-Posfix-Timestamp` mai vechi de ~5 minute.\n\n```js\nconst crypto = require(\"crypto\");\n// rawBody: Buffer, citit cu express.raw({ type: \"application/json\" })\nconst signed = Buffer.concat([Buffer.from(`${webhookId}.${timestamp}.`, \"utf8\"), rawBody]);\nconst expected = crypto.createHmac(\"sha256\", signingSecret).update(signed).digest(\"base64\");\nconst valid = expected.length === signature.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));\n```\n\n### Reîncercări\n\n- Orice răspuns **2xx** în cel mult 15 secunde înseamnă primit.\n- `5xx`, `408`, `429`, redirecționările și căderile de rețea se reîncearcă după 2, 4, 8, 16, 32, 60, 60 de minute. După 8 încercări (~3 ore) livrarea se abandonează.\n- Orice alt `4xx` oprește livrarea **imediat**, fără reîncercare. La fel un nume de domeniu care nu se rezolvă sau care duce la o adresă privată.\n- Redirecționările nu se urmează.\n- Cât modulul „API Integrare” e inactiv, evenimentele nu se trimit și nici nu se păstrează.\n\n## Variabilele colecției\n\n- `baseUrl` — `https://api.posfix.md` (producție) sau `https://dev.posfix.md` (teste).\n- `apiKey` — cheia primită de la PosFix.\n- `today`, `monthStart` — se calculează înaintea fiecărei cereri.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "apikey",
    "apikey": [
      {
        "key": "key",
        "value": "X-Posfix-Api-Key",
        "type": "string"
      },
      {
        "key": "value",
        "value": "{{apiKey}}",
        "type": "string"
      },
      {
        "key": "in",
        "value": "header",
        "type": "string"
      }
    ]
  },
  "event": [
    {
      "listen": "prerequest",
      "script": {
        "type": "text/javascript",
        "exec": [
          "const pad = (n) => String(n).padStart(2, '0');",
          "const fmt = (d) => `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;",
          "const now = new Date();",
          "pm.collectionVariables.set('today', fmt(now));",
          "pm.collectionVariables.set('monthStart', fmt(now).slice(0, 8) + '01');"
        ]
      }
    }
  ],
  "variable": [
    {
      "key": "baseUrl",
      "value": "https://api.posfix.md",
      "description": "Producție. Pentru teste: https://dev.posfix.md"
    },
    {
      "key": "apiKey",
      "value": "",
      "description": "Cheia rk_… primită de la PosFix odată cu modulul „API Integrare”"
    },
    {
      "key": "today",
      "value": "",
      "description": "Calculat automat (YYYY-MM-DD)"
    },
    {
      "key": "monthStart",
      "value": "",
      "description": "Calculat automat (prima zi a lunii)"
    }
  ],
  "item": [
    {
      "name": "0. Verificare",
      "description": "Începe de aici: confirmă că cheia merge și ce scope-uri are.",
      "item": [
        {
          "name": "Ping",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/ping",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "ping"
              ]
            },
            "description": "Verifică cheia. Întoarce comerciantul cheii și scope-urile ei. Începe de aici.\n\n### Răspuns 200\n\n| Câmp | Tip | Descriere |\n|---|---|---|\n| `organizationId` | string (uuid) | Comerciantul căruia îi aparține cheia. |\n| `scopes` | string[] | Scope-urile cheii. |\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 401 | — | Cheia lipsește sau nu e validă. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n\n### De reținut\n\n- Ping nu cere scope și nici modulul „API Integrare”. Dacă ping merge și altă cerere dă `403`, cheii îi lipsește scope-ul acelei cereri sau modulul nu e activ."
          },
          "response": [
            {
              "name": "200",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/ping",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "ping"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"organizationId\": \"3f6c2a1e-8b4d-4c7a-9e21-5d0f7b8a9c12\",\n  \"scopes\": [\n    \"products:read\",\n    \"inventory:read\",\n    \"orders:write\",\n    \"sales:read\"\n  ]\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "1. Catalog și stoc",
      "description": "Sortimentul de vânzare, fișele produselor, stocul, categoriile și atributele.",
      "item": [
        {
          "name": "Listă produse",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/products?limit=50",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "products"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "50",
                  "description": "1–200, implicit 50."
                },
                {
                  "key": "search",
                  "value": "apa",
                  "description": "Fragment din cod sau denumire.",
                  "disabled": true
                },
                {
                  "key": "categoryCode",
                  "value": "BAUTURI",
                  "description": "Codul categoriei; repetabil.",
                  "disabled": true
                },
                {
                  "key": "categoryId",
                  "value": "",
                  "description": "Id-ul categoriei; repetabil.",
                  "disabled": true
                },
                {
                  "key": "includeSubcategories",
                  "value": "true",
                  "description": "Implicit true.",
                  "disabled": true
                },
                {
                  "key": "attribute",
                  "value": "tara:Moldova",
                  "description": "cod:valoare; repetabil.",
                  "disabled": true
                },
                {
                  "key": "includeInactive",
                  "value": "false",
                  "description": "Implicit false.",
                  "disabled": true
                },
                {
                  "key": "includeUnlisted",
                  "value": "false",
                  "description": "Implicit false.",
                  "disabled": true
                },
                {
                  "key": "warehouseId",
                  "value": "",
                  "description": "Stocul unui singur depozit; implicit toate.",
                  "disabled": true
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "nextCursor din pagina precedentă.",
                  "disabled": true
                }
              ]
            },
            "description": "**Scope necesar:** `products:read`\n\nSortimentul de vânzare al comerciantului, pagină cu pagină: produsele care au card la casă, cu prețul, categoria și stocul.\n\n### Parametri de interogare\n\n| Parametru | Tip | Implicit | Descriere |\n|---|---|---|---|\n| `limit` | integer | 50 | Peste 200 se reduce la 200. |\n| `search` | string | — | Fragment din **cod sau denumire**, fără diferență de majuscule. Nu caută în codul de bare. |\n| `categoryId`, `categoryCode` | uuid / string | — | Categoriile de la casă; repetabile, legate prin SAU: `?categoryCode=APA&categoryCode=SUCURI`. |\n| `includeSubcategories` | boolean | `true` | Include subcategoriile celor cerute. |\n| `attribute` | string | — | `cod:valoare`, repetabil. Același cod = SAU, coduri diferite = ȘI: `?attribute=brand:Samyang&attribute=tara:Moldova`. Codul e cel din „Atribute”, cu litere mici. |\n| `includeInactive` | boolean | `false` | Include produsele și cardurile inactive. |\n| `includeUnlisted` | boolean | `false` | Include pozițiile **fără** card la casă (materiale, servicii interne). Se ignoră când filtrezi pe categorie. |\n| `warehouseId` | string (uuid) | toate | Stocul (`onHand`, `onHandByWarehouse`, `inStock`) se ia doar din acest depozit. Id-ul vine din „Depozite”. Nu restrânge lista de produse. |\n| `cursor` | string | — | Valoarea `nextCursor` din pagina precedentă. |\n\n### Răspuns 200\n\n| Câmp | Tip | Descriere |\n|---|---|---|\n| `items[]` | array | Produsele; câmpurile sunt în tabelul de mai jos. `composition` și `contentBlocks` vin goale în listă. |\n| `nextCursor` | string? | Cursorul paginii următoare; `null` pe ultima. |\n\n### Produsul\n\n| Câmp | Tip | Descriere |\n|---|---|---|\n| `posfixId` | string (uuid) | Id-ul produsului în PosFix. Cu el ceri prețuri. |\n| `code` | string | Codul produsului (de obicei 10 cifre). Cu el comanzi și citești stocul. |\n| `name` | string | Denumirea din nomenclator. |\n| `names` | object | Denumirea pe limbi; regula hărților de limbi e în notele cererii. |\n| `description` | string? | Descrierea în română. |\n| `barcode` | string | Codul de bare; `\"\"` dacă lipsește. |\n| `price`, `currency` | number, string | Prețul de vânzare de la casă (cu TVA); fără card la casă, prețul implicit din nomenclator. |\n| `vatRate` | number? | Cota TVA, %. `null` = cotă nestabilită, diferit de `0`. |\n| `unit` | string | Unitatea de bază (`buc`, `kg`). |\n| `isActive` | boolean | Produsul e activ în nomenclator. |\n| `category`, `categoryPath[]` | object?, array | Categoria de la casă (`id`, `name`, `code`, `names`) și drumul ei de la rădăcină. |\n| `imageUrl` | string? | Adresa **relativă** a imaginii; o citești cu aceeași cheie (cererea „Imaginea produsului”). |\n| `attributes[]` | array | `code` și `value` — valorile atributelor (marcă, țară…); `valueNames` = valoarea pe limbi. |\n| `onHand[]` | array | Stocul total, un singur element cu `warehouseId: null`. Cu `?warehouseId=` la listă, stocul acelui depozit, cu id-ul lui. |\n| `onHandByWarehouse[]` | array | Stocul pe fiecare depozit care are marfa: `warehouseId`, `code`, `name`, `quantity`. Depozitul care lipsește are 0. |\n| `inStock` | boolean | Stocul total e peste zero. |\n| `descriptions` | object | Descrierea pe limbi; regula hărților de limbi e în notele cererii. |\n| `composition[]` | array | Compoziția: `ingredient`, `percent`, `allergen`. Doar la „Produs după cod”. |\n| `contentBlocks[]` | array | Blocurile de conținut (HTML curățat): `labels` și `contents` pe limbi, plus câmpurile vechi `labelRo`… `contentEn`. Doar la „Produs după cod”. |\n| `sku` | string? | Codul extern al produsului, altfel SKU-ul de la casă. |\n| `shelfLifeDays` | integer? | Termenul de valabilitate, zile. |\n| `packaging[]` | array | `unit` și `factor`: „1 `unit` = `factor` unități de bază”. |\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 400 | — | Filtru `attribute` fără forma `cod:valoare`, valoare de tip greșit (`tree=1`, GUID invalid). |\n| 400 | `VALIDATION` | „cursor must be a valid product id.” |\n| 401 | — | Cheia lipsește, e greșită, revocată sau expirată. Corp gol. |\n| 403 | — | Cheii îi lipsește scope-ul cerut sau modulul „API Integrare” nu e activ. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n| 503 | `SERVICE_UNAVAILABLE` | Un serviciu intern nu răspunde. Reîncearcă peste câteva secunde. |\n\n### De reținut\n\n- Hărțile de limbi (`names`, `descriptions`, `valueNames`, `labels`, `contents`) au cheia = codul limbii: `{ \"ro\": …, \"ru\": … }`. Limbile vin din lista platformei și pot crește oricând — citește cheile primite, nu presupune exact `ro`/`ru`/`en`. O limbă fără traducere lipsește din hartă: folosești textul de bază (`name`, `description`).\n- Ordinea e după id, nu după nume. Parcurge paginile până la `nextCursor: null`.\n- Lista se păstrează în cache **45 de secunde**, deci stocul din ea poate întârzia. Pentru stocul la zi folosește „Stoc produs”.\n- Un produs fără card la casă nu apare decât cu `includeUnlisted=true`.\n- Listele se trimit repetând parametrul (`?a=x&a=y`), nu cu virgulă. Un boolean se scrie `true`/`false`, nu `1`."
          },
          "response": [
            {
              "name": "200",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/products?limit=50",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "products"
                  ],
                  "query": [
                    {
                      "key": "limit",
                      "value": "50",
                      "description": "1–200, implicit 50."
                    },
                    {
                      "key": "search",
                      "value": "apa",
                      "description": "Fragment din cod sau denumire.",
                      "disabled": true
                    },
                    {
                      "key": "categoryCode",
                      "value": "BAUTURI",
                      "description": "Codul categoriei; repetabil.",
                      "disabled": true
                    },
                    {
                      "key": "categoryId",
                      "value": "",
                      "description": "Id-ul categoriei; repetabil.",
                      "disabled": true
                    },
                    {
                      "key": "includeSubcategories",
                      "value": "true",
                      "description": "Implicit true.",
                      "disabled": true
                    },
                    {
                      "key": "attribute",
                      "value": "tara:Moldova",
                      "description": "cod:valoare; repetabil.",
                      "disabled": true
                    },
                    {
                      "key": "includeInactive",
                      "value": "false",
                      "description": "Implicit false.",
                      "disabled": true
                    },
                    {
                      "key": "includeUnlisted",
                      "value": "false",
                      "description": "Implicit false.",
                      "disabled": true
                    },
                    {
                      "key": "warehouseId",
                      "value": "",
                      "description": "Stocul unui singur depozit; implicit toate.",
                      "disabled": true
                    },
                    {
                      "key": "cursor",
                      "value": "",
                      "description": "nextCursor din pagina precedentă.",
                      "disabled": true
                    }
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"items\": [\n    {\n      \"posfixId\": \"0b8f5e2a-6c1d-4e7f-9a3b-2d4c6e8f1a01\",\n      \"code\": \"0000000123\",\n      \"name\": \"Apă minerală Borjomi 0,5 L\",\n      \"description\": \"Apă minerală naturală carbogazoasă.\",\n      \"barcode\": \"4860019001339\",\n      \"price\": 18.5,\n      \"currency\": \"MDL\",\n      \"vatRate\": 20,\n      \"unit\": \"buc\",\n      \"isActive\": true,\n      \"category\": {\n        \"id\": \"a1b2c3d4-0002-4a5b-8c6d-7e8f9a0b1c02\",\n        \"name\": \"Apă minerală\",\n        \"code\": \"APA\",\n        \"names\": {\n          \"ro\": \"Apă minerală\",\n          \"ru\": \"Минеральная вода\"\n        }\n      },\n      \"imageUrl\": \"/api/public/v1/products/0000000123/image\",\n      \"attributes\": [\n        {\n          \"code\": \"tara\",\n          \"value\": \"Georgia\",\n          \"valueNames\": {\n            \"ro\": \"Georgia\",\n            \"ru\": \"Грузия\"\n          }\n        },\n        {\n          \"code\": \"brand\",\n          \"value\": \"Borjomi\",\n          \"valueNames\": {\n            \"ro\": \"Borjomi\"\n          }\n        }\n      ],\n      \"composition\": [],\n      \"onHand\": [\n        {\n          \"warehouseId\": null,\n          \"quantity\": 144\n        }\n      ],\n      \"onHandByWarehouse\": [\n        {\n          \"warehouseId\": \"e4d3c2b1-0001-4f5e-8d7c-6b5a49382701\",\n          \"code\": \"DEP-1\",\n          \"name\": \"Depozit central\",\n          \"quantity\": 120\n        },\n        {\n          \"warehouseId\": \"e4d3c2b1-0002-4f5e-8d7c-6b5a49382702\",\n          \"code\": \"MAG-2\",\n          \"name\": \"Magazin Botanica\",\n          \"quantity\": 24\n        }\n      ],\n      \"names\": {\n        \"ro\": \"Apă minerală Borjomi 0,5 L\",\n        \"ru\": \"Минеральная вода Borjomi 0,5 л\"\n      },\n      \"descriptions\": {\n        \"ro\": \"Apă minerală naturală carbogazoasă.\",\n        \"ru\": \"Натуральная газированная минеральная вода.\"\n      },\n      \"contentBlocks\": [],\n      \"categoryPath\": [\n        {\n          \"id\": \"a1b2c3d4-0001-4a5b-8c6d-7e8f9a0b1c01\",\n          \"name\": \"Băuturi\",\n          \"code\": \"BAUTURI\",\n          \"names\": {\n            \"ro\": \"Băuturi\",\n            \"ru\": \"Напитки\"\n          }\n        },\n        {\n          \"id\": \"a1b2c3d4-0002-4a5b-8c6d-7e8f9a0b1c02\",\n          \"name\": \"Apă minerală\",\n          \"code\": \"APA\",\n          \"names\": {\n            \"ro\": \"Apă minerală\",\n            \"ru\": \"Минеральная вода\"\n          }\n        }\n      ],\n      \"sku\": \"BRJ-05\",\n      \"shelfLifeDays\": 730,\n      \"packaging\": [\n        {\n          \"unit\": \"bax\",\n          \"factor\": 12\n        }\n      ],\n      \"inStock\": true\n    }\n  ],\n  \"nextCursor\": \"0b8f5e2a-6c1d-4e7f-9a3b-2d4c6e8f1a01\"\n}"
            },
            {
              "name": "400 — filtru de atribut greșit",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/products?limit=50",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "products"
                  ],
                  "query": [
                    {
                      "key": "limit",
                      "value": "50",
                      "description": "1–200, implicit 50."
                    },
                    {
                      "key": "search",
                      "value": "apa",
                      "description": "Fragment din cod sau denumire.",
                      "disabled": true
                    },
                    {
                      "key": "categoryCode",
                      "value": "BAUTURI",
                      "description": "Codul categoriei; repetabil.",
                      "disabled": true
                    },
                    {
                      "key": "categoryId",
                      "value": "",
                      "description": "Id-ul categoriei; repetabil.",
                      "disabled": true
                    },
                    {
                      "key": "includeSubcategories",
                      "value": "true",
                      "description": "Implicit true.",
                      "disabled": true
                    },
                    {
                      "key": "attribute",
                      "value": "tara:Moldova",
                      "description": "cod:valoare; repetabil.",
                      "disabled": true
                    },
                    {
                      "key": "includeInactive",
                      "value": "false",
                      "description": "Implicit false.",
                      "disabled": true
                    },
                    {
                      "key": "includeUnlisted",
                      "value": "false",
                      "description": "Implicit false.",
                      "disabled": true
                    },
                    {
                      "key": "warehouseId",
                      "value": "",
                      "description": "Stocul unui singur depozit; implicit toate.",
                      "disabled": true
                    },
                    {
                      "key": "cursor",
                      "value": "",
                      "description": "nextCursor din pagina precedentă.",
                      "disabled": true
                    }
                  ]
                }
              },
              "status": "Bad Request",
              "code": 400,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "body": "{\n  \"statusCode\": 400,\n  \"message\": \"One or more errors occurred!\",\n  \"errors\": {\n    \"attribute\": [\n      \"Filtrul de atribut „brand” trebuie să aibă forma cod:valoare (de ex. brand:Samyang).\"\n    ]\n  },\n  \"errorCodes\": {\n    \"attribute\": [\n      {\n        \"code\": \"INTEGRATIONS_ATTRIBUTE_FILTER_FORMAT\",\n        \"params\": {\n          \"filter\": \"brand\"\n        }\n      }\n    ]\n  }\n}"
            }
          ]
        },
        {
          "name": "Produs după cod",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/products/:code",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "products",
                ":code"
              ],
              "variable": [
                {
                  "key": "code",
                  "value": "0000000123",
                  "description": "Codul produsului"
                }
              ]
            },
            "description": "**Scope necesar:** `products:read`\n\nFișa completă a unui produs: tot ce dă lista, plus compoziția și blocurile de conținut pe limbi. Găsește și produsele inactive sau fără card la casă.\n\n### Parametri de cale\n\n| Parametru | Tip | Descriere |\n|---|---|---|\n| `code` | string | Codul exact al produsului (cu zerourile din față). |\n\n### Răspuns 200\n\n| Câmp | Tip | Descriere |\n|---|---|---|\n| `posfixId` | string (uuid) | Id-ul produsului în PosFix. Cu el ceri prețuri. |\n| `code` | string | Codul produsului (de obicei 10 cifre). Cu el comanzi și citești stocul. |\n| `name` | string | Denumirea din nomenclator. |\n| `names` | object | Denumirea pe limbi; regula hărților de limbi e în notele cererii. |\n| `description` | string? | Descrierea în română. |\n| `barcode` | string | Codul de bare; `\"\"` dacă lipsește. |\n| `price`, `currency` | number, string | Prețul de vânzare de la casă (cu TVA); fără card la casă, prețul implicit din nomenclator. |\n| `vatRate` | number? | Cota TVA, %. `null` = cotă nestabilită, diferit de `0`. |\n| `unit` | string | Unitatea de bază (`buc`, `kg`). |\n| `isActive` | boolean | Produsul e activ în nomenclator. |\n| `category`, `categoryPath[]` | object?, array | Categoria de la casă (`id`, `name`, `code`, `names`) și drumul ei de la rădăcină. |\n| `imageUrl` | string? | Adresa **relativă** a imaginii; o citești cu aceeași cheie (cererea „Imaginea produsului”). |\n| `attributes[]` | array | `code` și `value` — valorile atributelor (marcă, țară…); `valueNames` = valoarea pe limbi. |\n| `onHand[]` | array | Stocul total, un singur element cu `warehouseId: null`. Cu `?warehouseId=` la listă, stocul acelui depozit, cu id-ul lui. |\n| `onHandByWarehouse[]` | array | Stocul pe fiecare depozit care are marfa: `warehouseId`, `code`, `name`, `quantity`. Depozitul care lipsește are 0. |\n| `inStock` | boolean | Stocul total e peste zero. |\n| `descriptions` | object | Descrierea pe limbi; regula hărților de limbi e în notele cererii. |\n| `composition[]` | array | Compoziția: `ingredient`, `percent`, `allergen`. Doar la „Produs după cod”. |\n| `contentBlocks[]` | array | Blocurile de conținut (HTML curățat): `labels` și `contents` pe limbi, plus câmpurile vechi `labelRo`… `contentEn`. Doar la „Produs după cod”. |\n| `sku` | string? | Codul extern al produsului, altfel SKU-ul de la casă. |\n| `shelfLifeDays` | integer? | Termenul de valabilitate, zile. |\n| `packaging[]` | array | `unit` și `factor`: „1 `unit` = `factor` unități de bază”. |\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 404 | — | Codul nu există. Corp gol. |\n| 400 | `INTEGRATIONS_PRODUCT_CODE_REQUIRED` | „Codul produsului (productCode) este obligatoriu.” |\n| 401 | — | Cheia lipsește, e greșită, revocată sau expirată. Corp gol. |\n| 403 | — | Cheii îi lipsește scope-ul cerut sau modulul „API Integrare” nu e activ. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n| 503 | `SERVICE_UNAVAILABLE` | Un serviciu intern nu răspunde. Reîncearcă peste câteva secunde. |\n\n### De reținut\n\n- Hărțile de limbi (`names`, `descriptions`, `valueNames`, `labels`, `contents`) au cheia = codul limbii: `{ \"ro\": …, \"ru\": … }`. Limbile vin din lista platformei și pot crește oricând — citește cheile primite, nu presupune exact `ro`/`ru`/`en`. O limbă fără traducere lipsește din hartă: folosești textul de bază (`name`, `description`).\n- Blocurile de conținut vin doar pentru produsele active și doar pentru definițiile active (vezi „Blocuri de conținut”).\n- Câmpurile vechi ale blocului (`labelRu`, `contentEn`…) rămân pentru integrările existente și nu vor purta niciodată o limbă adăugată ulterior; folosește `labels` și `contents`.\n- Răspunsul stă în cache 45 de secunde."
          },
          "response": [
            {
              "name": "200",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/products/:code",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "products",
                    ":code"
                  ],
                  "variable": [
                    {
                      "key": "code",
                      "value": "0000000123",
                      "description": "Codul produsului"
                    }
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"posfixId\": \"0b8f5e2a-6c1d-4e7f-9a3b-2d4c6e8f1a01\",\n  \"code\": \"0000000123\",\n  \"name\": \"Apă minerală Borjomi 0,5 L\",\n  \"description\": \"Apă minerală naturală carbogazoasă.\",\n  \"barcode\": \"4860019001339\",\n  \"price\": 18.5,\n  \"currency\": \"MDL\",\n  \"vatRate\": 20,\n  \"unit\": \"buc\",\n  \"isActive\": true,\n  \"category\": {\n    \"id\": \"a1b2c3d4-0002-4a5b-8c6d-7e8f9a0b1c02\",\n    \"name\": \"Apă minerală\",\n    \"code\": \"APA\",\n    \"names\": {\n      \"ro\": \"Apă minerală\",\n      \"ru\": \"Минеральная вода\"\n    }\n  },\n  \"imageUrl\": \"/api/public/v1/products/0000000123/image\",\n  \"attributes\": [\n    {\n      \"code\": \"tara\",\n      \"value\": \"Georgia\",\n      \"valueNames\": {\n        \"ro\": \"Georgia\",\n        \"ru\": \"Грузия\"\n      }\n    },\n    {\n      \"code\": \"brand\",\n      \"value\": \"Borjomi\",\n      \"valueNames\": {\n        \"ro\": \"Borjomi\"\n      }\n    }\n  ],\n  \"composition\": [\n    {\n      \"ingredient\": \"Apă minerală naturală\",\n      \"percent\": null,\n      \"allergen\": false\n    },\n    {\n      \"ingredient\": \"Dioxid de carbon\",\n      \"percent\": null,\n      \"allergen\": false\n    }\n  ],\n  \"onHand\": [\n    {\n      \"warehouseId\": null,\n      \"quantity\": 144\n    }\n  ],\n  \"onHandByWarehouse\": [\n    {\n      \"warehouseId\": \"e4d3c2b1-0001-4f5e-8d7c-6b5a49382701\",\n      \"code\": \"DEP-1\",\n      \"name\": \"Depozit central\",\n      \"quantity\": 120\n    },\n    {\n      \"warehouseId\": \"e4d3c2b1-0002-4f5e-8d7c-6b5a49382702\",\n      \"code\": \"MAG-2\",\n      \"name\": \"Magazin Botanica\",\n      \"quantity\": 24\n    }\n  ],\n  \"names\": {\n    \"ro\": \"Apă minerală Borjomi 0,5 L\",\n    \"ru\": \"Минеральная вода Borjomi 0,5 л\"\n  },\n  \"descriptions\": {\n    \"ro\": \"Apă minerală naturală carbogazoasă.\",\n    \"ru\": \"Натуральная газированная минеральная вода.\"\n  },\n  \"contentBlocks\": [\n    {\n      \"key\": \"pastrare\",\n      \"labelRo\": \"Condiții de păstrare\",\n      \"labelRu\": \"Условия хранения\",\n      \"labelEn\": null,\n      \"contentRo\": \"<p>A se păstra la loc uscat, ferit de soare.</p>\",\n      \"contentRu\": null,\n      \"contentEn\": null,\n      \"sortOrder\": 1,\n      \"labels\": {\n        \"ro\": \"Condiții de păstrare\",\n        \"ru\": \"Условия хранения\"\n      },\n      \"contents\": {\n        \"ro\": \"<p>A se păstra la loc uscat, ferit de soare.</p>\"\n      }\n    }\n  ],\n  \"categoryPath\": [\n    {\n      \"id\": \"a1b2c3d4-0001-4a5b-8c6d-7e8f9a0b1c01\",\n      \"name\": \"Băuturi\",\n      \"code\": \"BAUTURI\",\n      \"names\": {\n        \"ro\": \"Băuturi\",\n        \"ru\": \"Напитки\"\n      }\n    },\n    {\n      \"id\": \"a1b2c3d4-0002-4a5b-8c6d-7e8f9a0b1c02\",\n      \"name\": \"Apă minerală\",\n      \"code\": \"APA\",\n      \"names\": {\n        \"ro\": \"Apă minerală\",\n        \"ru\": \"Минеральная вода\"\n      }\n    }\n  ],\n  \"sku\": \"BRJ-05\",\n  \"shelfLifeDays\": 730,\n  \"packaging\": [\n    {\n      \"unit\": \"bax\",\n      \"factor\": 12\n    }\n  ],\n  \"inStock\": true\n}"
            }
          ]
        },
        {
          "name": "Imaginea produsului",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/products/:code/image",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "products",
                ":code",
                "image"
              ],
              "variable": [
                {
                  "key": "code",
                  "value": "0000000123",
                  "description": "Codul produsului"
                }
              ]
            },
            "description": "**Scope necesar:** `products:read`\n\nImaginea produsului din nomenclator, ca fișier. De obicei o miniatură JPEG de cel mult 320×320.\n\n### Parametri de cale\n\n| Parametru | Tip | Descriere |\n|---|---|---|\n| `code` | string | Codul produsului. |\n\n### Răspuns\n\n**200** cu octeții imaginii. `Content-Type` e tipul imaginii (`image/jpeg` pentru miniatură); `Cache-Control: private, max-age=300`.\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 404 | — | Produs inexistent, fără imagine sau imagine indisponibilă. Corp gol. |\n| 401 | — | Cheia lipsește, e greșită, revocată sau expirată. Corp gol. |\n| 403 | — | Cheii îi lipsește scope-ul cerut sau modulul „API Integrare” nu e activ. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n| 503 | `SERVICE_UNAVAILABLE` | Un serviciu intern nu răspunde. Reîncearcă peste câteva secunde. |\n\n### De reținut\n\n- Cererea cere cheia, deci `imageUrl` nu merge direct într-un `<img src>` din browserul clientului tău. Descarc-o pe server și servește-o tu."
          },
          "response": [
            {
              "name": "404 — produs fără imagine",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/products/:code/image",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "products",
                    ":code",
                    "image"
                  ],
                  "variable": [
                    {
                      "key": "code",
                      "value": "0000000123",
                      "description": "Codul produsului"
                    }
                  ]
                }
              },
              "status": "Not Found",
              "code": 404,
              "_postman_previewlanguage": "text",
              "header": [],
              "body": ""
            }
          ]
        },
        {
          "name": "Stoc produs",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/inventory?productCode=0000000123",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "inventory"
              ],
              "query": [
                {
                  "key": "productCode",
                  "value": "0000000123",
                  "description": "Codul produsului, obligatoriu."
                },
                {
                  "key": "warehouseId",
                  "value": "",
                  "description": "Un depozit; fără el, totalul.",
                  "disabled": true
                }
              ]
            },
            "description": "**Scope necesar:** `inventory:read`\n\nStocul la zi al unui produs, pe un depozit sau pe toate. Nu trece prin cache.\n\n### Parametri de interogare\n\n| Parametru | Tip | Implicit | Descriere |\n|---|---|---|---|\n| `productCode` | string | — | Obligatoriu. Codul exact al produsului. |\n| `warehouseId` | string (uuid) | toate | Depozitul. Un depozit necunoscut dă cantitatea 0, nu eroare. |\n\n### Răspuns 200\n\n| Câmp | Tip | Descriere |\n|---|---|---|\n| `posfixId`, `code`, `name` | string | Produsul. |\n| `onHand[]` | array | Un singur element: `warehouseId` (cel cerut sau `null` = total) și `quantity`, în unitatea de bază. |\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 400 | `INTEGRATIONS_PRODUCT_CODE_REQUIRED` | „Codul produsului (productCode) este obligatoriu.” |\n| 404 | — | Codul nu există. Corp gol. |\n| 401 | — | Cheia lipsește, e greșită, revocată sau expirată. Corp gol. |\n| 403 | — | Cheii îi lipsește scope-ul cerut sau modulul „API Integrare” nu e activ. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n| 503 | `SERVICE_UNAVAILABLE` | Un serviciu intern nu răspunde. Reîncearcă peste câteva secunde. |\n\n### De reținut\n\n- Cantitatea poate fi **negativă**: s-a vândut marfă care nu fusese încă recepționată."
          },
          "response": [
            {
              "name": "200",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/inventory?productCode=0000000123",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "inventory"
                  ],
                  "query": [
                    {
                      "key": "productCode",
                      "value": "0000000123",
                      "description": "Codul produsului, obligatoriu."
                    },
                    {
                      "key": "warehouseId",
                      "value": "",
                      "description": "Un depozit; fără el, totalul.",
                      "disabled": true
                    }
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"posfixId\": \"0b8f5e2a-6c1d-4e7f-9a3b-2d4c6e8f1a01\",\n  \"code\": \"0000000123\",\n  \"name\": \"Apă minerală Borjomi 0,5 L\",\n  \"onHand\": [\n    {\n      \"warehouseId\": null,\n      \"quantity\": 144\n    }\n  ]\n}"
            }
          ]
        },
        {
          "name": "Depozite",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/warehouses",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "warehouses"
              ]
            },
            "description": "**Scope necesar:** `inventory:read`\n\nDepozitele active ale comerciantului, cel implicit primul. Id-ul merge în `warehouseId` la „Listă produse” și la „Stoc produs”.\n\n### Răspuns 200\n\n| Câmp | Tip | Descriere |\n|---|---|---|\n| `items[].id` | string (uuid) | Id-ul depozitului. |\n| `items[].code` | string | Codul depozitului. |\n| `items[].name` | string | Denumirea depozitului. |\n| `items[].isDefault` | boolean | Depozitul implicit al comerciantului. |\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 401 | — | Cheia lipsește, e greșită, revocată sau expirată. Corp gol. |\n| 403 | — | Cheii îi lipsește scope-ul cerut sau modulul „API Integrare” nu e activ. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n| 503 | `SERVICE_UNAVAILABLE` | Un serviciu intern nu răspunde. Reîncearcă peste câteva secunde. |\n\n### De reținut\n\n- Lista stă în cache 45 de secunde.\n- Un depozit dezactivat lipsește din listă, dar stocul lui rămâne în `onHand` și `onHandByWarehouse` până se mută marfa."
          },
          "response": [
            {
              "name": "200",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/warehouses",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "warehouses"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"items\": [\n    {\n      \"id\": \"e4d3c2b1-0001-4f5e-8d7c-6b5a49382701\",\n      \"code\": \"DEP-1\",\n      \"name\": \"Depozit central\",\n      \"isDefault\": true\n    },\n    {\n      \"id\": \"e4d3c2b1-0002-4f5e-8d7c-6b5a49382702\",\n      \"code\": \"MAG-2\",\n      \"name\": \"Magazin Botanica\",\n      \"isDefault\": false\n    }\n  ]\n}"
            }
          ]
        },
        {
          "name": "Atribute",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/attributes",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "attributes"
              ]
            },
            "description": "**Scope necesar:** `products:read`\n\nAtributele produselor (marcă, țară, gust…) cu valorile folosite. Construiește filtrele magazinului din ele; codul merge în `attribute=cod:valoare` la „Listă produse”.\n\n### Răspuns 200\n\n| Câmp | Tip | Descriere |\n|---|---|---|\n| `items[].code` | string | Codul atributului, cu litere mici. |\n| `items[].name` | string | Denumirea dată de comerciant. |\n| `items[].names` | object | Denumirea pe limbi (hartă de limbi, ca la produs). |\n| `items[].dataType` | string | `string`, `number`, `boolean`, `date` sau `enum`. |\n| `items[].values[]` | string[] | Valorile predefinite și cele folosite pe produse, fără dubluri. |\n| `items[].valueNames[]` | array | Fiecare valoare din `values`, în aceeași ordine: `value` și `names` (valoarea pe limbi). |\n| `items[].sortOrder` | integer | Ordinea de afișare. |\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 401 | — | Cheia lipsește, e greșită, revocată sau expirată. Corp gol. |\n| 403 | — | Cheii îi lipsește scope-ul cerut sau modulul „API Integrare” nu e activ. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n| 503 | `SERVICE_UNAVAILABLE` | Un serviciu intern nu răspunde. Reîncearcă peste câteva secunde. |\n\n### De reținut\n\n- O valoare folosită doar pe produse inactive apare în listă, dar filtrul pe ea poate întoarce zero produse.\n- Filtrul `attribute=cod:valoare` primește valoarea din `values`, niciodată o traducere din `valueNames`."
          },
          "response": [
            {
              "name": "200",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/attributes",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "attributes"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"items\": [\n    {\n      \"code\": \"brand\",\n      \"name\": \"Marcă\",\n      \"dataType\": \"string\",\n      \"values\": [\n        \"Borjomi\",\n        \"Franzeluța\",\n        \"Nongshim\"\n      ],\n      \"sortOrder\": 1\n    },\n    {\n      \"code\": \"tara\",\n      \"name\": \"Țara de origine\",\n      \"dataType\": \"enum\",\n      \"values\": [\n        \"Georgia\",\n        \"Moldova\"\n      ],\n      \"sortOrder\": 2,\n      \"names\": {\n        \"ro\": \"Țara de origine\",\n        \"ru\": \"Страна происхождения\"\n      },\n      \"valueNames\": [\n        {\n          \"value\": \"Georgia\",\n          \"names\": {\n            \"ro\": \"Georgia\",\n            \"ru\": \"Грузия\"\n          }\n        },\n        {\n          \"value\": \"Moldova\",\n          \"names\": {\n            \"ro\": \"Moldova\",\n            \"ru\": \"Молдова\"\n          }\n        }\n      ]\n    }\n  ]\n}"
            }
          ]
        },
        {
          "name": "Categorii",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/categories?tree=true",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "categories"
              ],
              "query": [
                {
                  "key": "tree",
                  "value": "true",
                  "description": "true = arbore; implicit listă plată."
                }
              ]
            },
            "description": "**Scope necesar:** `products:read`\n\nCategoriile de la casă (cele după care se grupează produsele la vânzare), active. Categoriile contabile din nomenclator nu apar aici.\n\n### Parametri de interogare\n\n| Parametru | Tip | Implicit | Descriere |\n|---|---|---|---|\n| `tree` | boolean | `false` | `true` = rădăcinile, cu `children` completat; altfel toate categoriile într-o listă, cu `children: null`. |\n\n### Răspuns 200\n\n| Câmp | Tip | Descriere |\n|---|---|---|\n| `items[].id`, `name`, `code` | string | Categoria; codul poate fi gol. |\n| `items[].names` | object | Denumirea pe limbi (hartă de limbi, ca la produs). |\n| `items[].parentId` | string (uuid)? | Categoria părinte. |\n| `items[].sortOrder` | integer | Ordinea de afișare. |\n| `items[].children` | array? | Subcategoriile, doar cu `tree=true`. |\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 400 | — | `tree` altceva decât `true`/`false`. |\n| 401 | — | Cheia lipsește, e greșită, revocată sau expirată. Corp gol. |\n| 403 | — | Cheii îi lipsește scope-ul cerut sau modulul „API Integrare” nu e activ. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n| 503 | `SERVICE_UNAVAILABLE` | Un serviciu intern nu răspunde. Reîncearcă peste câteva secunde. |\n\n### De reținut\n\n- În lista plată, `parentId` poate indica o categorie inactivă, care lipsește din listă: tratează acea categorie ca rădăcină."
          },
          "response": [
            {
              "name": "200 — arbore",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/categories?tree=true",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "categories"
                  ],
                  "query": [
                    {
                      "key": "tree",
                      "value": "true",
                      "description": "true = arbore; implicit listă plată."
                    }
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"items\": [\n    {\n      \"id\": \"a1b2c3d4-0001-4a5b-8c6d-7e8f9a0b1c01\",\n      \"name\": \"Băuturi\",\n      \"code\": \"BAUTURI\",\n      \"parentId\": null,\n      \"sortOrder\": 1,\n      \"names\": {\n        \"ro\": \"Băuturi\",\n        \"ru\": \"Напитки\"\n      },\n      \"children\": [\n        {\n          \"id\": \"a1b2c3d4-0002-4a5b-8c6d-7e8f9a0b1c02\",\n          \"name\": \"Apă minerală\",\n          \"code\": \"APA\",\n          \"parentId\": \"a1b2c3d4-0001-4a5b-8c6d-7e8f9a0b1c01\",\n          \"sortOrder\": 1,\n          \"names\": {\n            \"ro\": \"Apă minerală\",\n            \"ru\": \"Минеральная вода\"\n          },\n          \"children\": []\n        }\n      ]\n    },\n    {\n      \"id\": \"a1b2c3d4-0004-4a5b-8c6d-7e8f9a0b1c04\",\n      \"name\": \"Panificație\",\n      \"code\": \"PANIFICATIE\",\n      \"parentId\": null,\n      \"sortOrder\": 2,\n      \"names\": {\n        \"ro\": \"Panificație\"\n      },\n      \"children\": []\n    }\n  ]\n}"
            }
          ]
        },
        {
          "name": "Blocuri de conținut",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/content-blocks",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "content-blocks"
              ]
            },
            "description": "**Scope necesar:** `products:read`\n\nDefinițiile blocurilor de conținut ale comerciantului (ingrediente, păstrare…). Fac locurile din șablonul paginii de produs; conținutul fiecărui produs vine la „Produs după cod”.\n\n### Răspuns 200\n\n| Câmp | Tip | Descriere |\n|---|---|---|\n| `items[].key` | string | Cheia; aceeași ca în `contentBlocks[].key` al produsului. |\n| `items[].labels` | object | Eticheta pe limbi (hartă de limbi, ca la produs). |\n| `items[].labelRo` | string | Eticheta în română. |\n| `items[].labelRu`, `labelEn` | string? | Etichetele în rusă și engleză — câmpuri vechi; o limbă adăugată ulterior apare doar în `labels`. |\n| `items[].sortOrder` | integer | Ordinea de afișare. |\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 401 | — | Cheia lipsește, e greșită, revocată sau expirată. Corp gol. |\n| 403 | — | Cheii îi lipsește scope-ul cerut sau modulul „API Integrare” nu e activ. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n| 503 | `SERVICE_UNAVAILABLE` | Un serviciu intern nu răspunde. Reîncearcă peste câteva secunde. |"
          },
          "response": [
            {
              "name": "200",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/content-blocks",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "content-blocks"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"items\": [\n    {\n      \"key\": \"ingrediente\",\n      \"labelRo\": \"Ingrediente\",\n      \"labelRu\": \"Состав\",\n      \"labelEn\": null,\n      \"sortOrder\": 1,\n      \"labels\": {\n        \"ro\": \"Ingrediente\",\n        \"ru\": \"Состав\"\n      }\n    },\n    {\n      \"key\": \"pastrare\",\n      \"labelRo\": \"Condiții de păstrare\",\n      \"labelRu\": \"Условия хранения\",\n      \"labelEn\": \"Storage\",\n      \"sortOrder\": 2,\n      \"labels\": {\n        \"ro\": \"Condiții de păstrare\",\n        \"ru\": \"Условия хранения\",\n        \"en\": \"Storage\"\n      }\n    }\n  ]\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "2. Prețuri",
      "description": "Listele de prețuri și prețul efectiv al produselor pe o listă.",
      "item": [
        {
          "name": "Liste de prețuri",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/price-lists",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "price-lists"
              ]
            },
            "description": "**Scope necesar:** `products:read`\n\nListele de prețuri ale comerciantului, cu lista implicită prima. Id-ul lor merge în „Prețuri pe produse”.\n\n### Răspuns 200\n\n| Câmp | Tip | Descriere |\n|---|---|---|\n| `items[].id`, `code`, `name` | string | Lista. |\n| `items[].currencyCode` | string | Moneda listei. |\n| `items[].isDefault` | boolean | Lista implicită a comerciantului. |\n| `items[].isActive` | boolean | Lista e activă. Apar și cele inactive. |\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 401 | — | Cheia lipsește, e greșită, revocată sau expirată. Corp gol. |\n| 403 | — | Cheii îi lipsește scope-ul cerut sau modulul „API Integrare” nu e activ. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n| 503 | `SERVICE_UNAVAILABLE` | Un serviciu intern nu răspunde. Reîncearcă peste câteva secunde. |\n\n### De reținut\n\n- O listă cu condiții (public, volum) sau în afara perioadei ei de valabilitate apare aici, dar „Prețuri pe produse” o ignoră și dă prețul implicit."
          },
          "response": [
            {
              "name": "200",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/price-lists",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "price-lists"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"items\": [\n    {\n      \"id\": \"5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a01\",\n      \"code\": \"PL-RETAIL\",\n      \"name\": \"Preț cu amănuntul\",\n      \"currencyCode\": \"MDL\",\n      \"isDefault\": true,\n      \"isActive\": true\n    },\n    {\n      \"id\": \"5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a02\",\n      \"code\": \"ANGRO\",\n      \"name\": \"Angro HoReCa\",\n      \"currencyCode\": \"MDL\",\n      \"isDefault\": false,\n      \"isActive\": true\n    }\n  ]\n}"
            }
          ]
        },
        {
          "name": "Prețuri pe produse",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/products/prices",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "products",
                "prices"
              ]
            },
            "description": "**Scope necesar:** `products:read`\n\nPrețul efectiv al unor produse pe o listă de prețuri — de exemplu, prețurile de angro pentru un client HoReCa.\n\n### Corpul cererii\n\n| Câmp | Tip | Obligatoriu | Reguli |\n|---|---|---|---|\n| `productIds[]` | string (uuid)[] | da | `posfixId`-urile produselor, 1–1000. Goală → `400` „Lista de produse (productIds) este obligatorie.”; peste 1000 → `400` „Cel mult 1.000 produse pe un apel.” |\n| `priceListId` | string (uuid) | nu | Lista. Lipsă → lista implicită a comerciantului. |\n\n### Răspuns 200\n\n| Câmp | Tip | Descriere |\n|---|---|---|\n| `items[].posfixId` | string (uuid) | Produsul. |\n| `items[].price` | number | Prețul efectiv. |\n| `items[].currency` | string | Moneda produsului. |\n\n### Cum se alege prețul\n\n1. Prețul explicit al produsului din listă, dacă există și e valabil azi.\n2. La o listă de tip reducere: prețul implicit minus procentul listei, rotunjit la 2 zecimale.\n3. Altfel, prețul implicit al produsului din nomenclator.\n\nLista se ignoră (toate produsele primesc prețul implicit) dacă e inactivă, a altui comerciant, are condiții de public sau de volum, sau e în afara perioadei de valabilitate. Nu primești eroare în aceste cazuri.\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 400 | `INTEGRATIONS_PRICE_PRODUCTS_REQUIRED`, `INTEGRATIONS_PRICE_PRODUCTS_TOO_MANY` | Lista lipsește sau are peste 1000 de id-uri (format simplu); un id nu e GUID (formatul de legare). |\n| 401 | — | Cheia lipsește, e greșită, revocată sau expirată. Corp gol. |\n| 403 | — | Cheii îi lipsește scope-ul cerut sau modulul „API Integrare” nu e activ. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n| 503 | `SERVICE_UNAVAILABLE` | Un serviciu intern nu răspunde. Reîncearcă peste câteva secunde. |\n\n### De reținut\n\n- Produsele necunoscute se **omit** din răspuns, fără eroare; id-urile repetate dau un singur rând. Leagă rândurile după `posfixId`, nu după ordine.\n- Prețurile nu se convertesc: o listă în altă monedă se aplică doar produselor în moneda ei.\n- Lista clientului (`defaultPriceListId` de la „Clienți”) nu se aplică singură: trimite-o tu în `priceListId`.\n- Corpul se citește doar cu `Content-Type: application/json`.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"productIds\": [\n    \"0b8f5e2a-6c1d-4e7f-9a3b-2d4c6e8f1a01\",\n    \"1c9a6f3b-7d2e-4f80-8b4c-3e5d7f9a2b02\"\n  ],\n  \"priceListId\": \"5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a02\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [
            {
              "name": "200",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/products/prices",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "products",
                    "prices"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"productIds\": [\n    \"0b8f5e2a-6c1d-4e7f-9a3b-2d4c6e8f1a01\",\n    \"1c9a6f3b-7d2e-4f80-8b4c-3e5d7f9a2b02\"\n  ],\n  \"priceListId\": \"5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a02\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"items\": [\n    {\n      \"posfixId\": \"0b8f5e2a-6c1d-4e7f-9a3b-2d4c6e8f1a01\",\n      \"price\": 15.9,\n      \"currency\": \"MDL\"\n    },\n    {\n      \"posfixId\": \"1c9a6f3b-7d2e-4f80-8b4c-3e5d7f9a2b02\",\n      \"price\": 7.11,\n      \"currency\": \"MDL\"\n    }\n  ]\n}"
            },
            {
              "name": "400 — listă goală",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/products/prices",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "products",
                    "prices"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"productIds\": [\n    \"0b8f5e2a-6c1d-4e7f-9a3b-2d4c6e8f1a01\",\n    \"1c9a6f3b-7d2e-4f80-8b4c-3e5d7f9a2b02\"\n  ],\n  \"priceListId\": \"5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a02\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "Bad Request",
              "code": 400,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"type\": \"https://httpstatuses.io/400\",\n  \"title\": \"Validation Failed\",\n  \"status\": 400,\n  \"detail\": \"Lista de produse (productIds) este obligatorie.\",\n  \"errorCode\": \"INTEGRATIONS_PRICE_PRODUCTS_REQUIRED\"\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "3. Clienți",
      "description": "Clienții comerciantului. Un client se identifică după codul fiscal.",
      "item": [
        {
          "name": "Creează client",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/customers",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "customers"
              ]
            },
            "description": "**Scope necesar:** `customers:write`\n\nCreează clientul după codul fiscal. Dacă există deja un partener cu același cod, îl întoarce **neschimbat** — cererea nu actualizează nimic.\n\n### Corpul cererii\n\n| Câmp | Tip | Obligatoriu | Reguli |\n|---|---|---|---|\n| `name` | string | da | Denumirea. Goală → `400` „Numele clientului este obligatoriu.” |\n| `fiscalCode` | string | da | IDNO sau IDNP, exact 13 cifre. Altfel → `400` „Codul fiscal (IDNO/IDNP, 13 cifre) este obligatoriu pentru un client distinct.” |\n| `email` | string | nu | Nu se validează. |\n| `phone` | string | nu | Se salvează așa cum vine. |\n| `address` | string | nu | Se salvează ca adresă juridică și reală. |\n\n### Răspuns 200\n\n| Câmp | Tip | Descriere |\n|---|---|---|\n| `posfixId` | string (uuid) | Id-ul partenerului în PosFix. Pe comenzi apare ca `partnerId`. |\n| `code` | string | Codul partenerului, 10 cifre (`0000000318`). |\n| `name` | string | Denumirea, normalizată: „Societate cu Răspundere Limitată „FLOAREA”” devine `SRL FLOAREA`. |\n| `fiscalCode` | string? | IDNO sau IDNP. |\n| `email`, `phone` | string? | Contactele. |\n| `address` | string? | Adresa reală; dacă lipsește, cea juridică. |\n| `isActive` | boolean | Partenerul e activ. |\n| `defaultPriceListId` | string (uuid)? | Lista de prețuri a clientului, dacă are una. |\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 400 | `INTEGRATIONS_CUSTOMER_NAME_REQUIRED`, `INTEGRATIONS_CUSTOMER_FISCAL_CODE_REQUIRED` | Nume gol sau cod fiscal greșit (format simplu). |\n| 401 | — | Cheia lipsește, e greșită, revocată sau expirată. Corp gol. |\n| 403 | — | Cheii îi lipsește scope-ul cerut sau modulul „API Integrare” nu e activ. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n| 503 | `SERVICE_UNAVAILABLE` | Un serviciu intern nu răspunde. Reîncearcă peste câteva secunde. |\n\n### De reținut\n\n- Răspunsul e `200` și la creare, și când clientul exista. Nu există un câmp care să spună care dintre ele.\n- Un partener care era doar furnizor devine și client.\n- Codul care începe cu `2` (IDNP) creează o persoană fizică; restul, persoane juridice.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"SRL Floarea Soarelui\",\n  \"fiscalCode\": \"1012600034567\",\n  \"email\": \"contabil@floarea.md\",\n  \"phone\": \"+37322123456\",\n  \"address\": \"mun. Chișinău, str. Ismail 45\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [
            {
              "name": "200 — client creat sau găsit",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/customers",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "customers"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"name\": \"SRL Floarea Soarelui\",\n  \"fiscalCode\": \"1012600034567\",\n  \"email\": \"contabil@floarea.md\",\n  \"phone\": \"+37322123456\",\n  \"address\": \"mun. Chișinău, str. Ismail 45\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"posfixId\": \"b4e7c1a2-9d3f-4e61-8a0b-2f5c7d9e1a34\",\n  \"code\": \"0000000318\",\n  \"name\": \"SRL Floarea Soarelui\",\n  \"fiscalCode\": \"1012600034567\",\n  \"email\": \"contabil@floarea.md\",\n  \"phone\": \"+37322123456\",\n  \"address\": \"mun. Chișinău, str. Ismail 45\",\n  \"isActive\": true,\n  \"defaultPriceListId\": null\n}"
            },
            {
              "name": "400 — cod fiscal greșit",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/customers",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "customers"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"name\": \"SRL Floarea Soarelui\",\n  \"fiscalCode\": \"1012600034567\",\n  \"email\": \"contabil@floarea.md\",\n  \"phone\": \"+37322123456\",\n  \"address\": \"mun. Chișinău, str. Ismail 45\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "Bad Request",
              "code": 400,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"type\": \"https://httpstatuses.io/400\",\n  \"title\": \"Validation Failed\",\n  \"status\": 400,\n  \"detail\": \"Codul fiscal (IDNO/IDNP, 13 cifre) este obligatoriu pentru un client distinct.\",\n  \"errorCode\": \"INTEGRATIONS_CUSTOMER_FISCAL_CODE_REQUIRED\"\n}"
            }
          ]
        },
        {
          "name": "Client după id",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/customers/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "customers",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "posfixId-ul clientului"
                }
              ]
            },
            "description": "**Scope necesar:** `customers:read`\n\nUn client al comerciantului, după `posfixId`.\n\n### Parametri de cale\n\n| Parametru | Tip | Descriere |\n|---|---|---|\n| `id` | string (uuid) | Id-ul clientului. Altceva decât un GUID → `400`. |\n\n### Răspuns 200\n\n| Câmp | Tip | Descriere |\n|---|---|---|\n| `posfixId` | string (uuid) | Id-ul partenerului în PosFix. Pe comenzi apare ca `partnerId`. |\n| `code` | string | Codul partenerului, 10 cifre (`0000000318`). |\n| `name` | string | Denumirea, normalizată: „Societate cu Răspundere Limitată „FLOAREA”” devine `SRL FLOAREA`. |\n| `fiscalCode` | string? | IDNO sau IDNP. |\n| `email`, `phone` | string? | Contactele. |\n| `address` | string? | Adresa reală; dacă lipsește, cea juridică. |\n| `isActive` | boolean | Partenerul e activ. |\n| `defaultPriceListId` | string (uuid)? | Lista de prețuri a clientului, dacă are una. |\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 404 | — | Nu există, e șters sau e doar furnizor. Corp gol. |\n| 400 | — | `id` nu e GUID (formatul de legare). |\n| 401 | — | Cheia lipsește, e greșită, revocată sau expirată. Corp gol. |\n| 403 | — | Cheii îi lipsește scope-ul cerut sau modulul „API Integrare” nu e activ. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n| 503 | `SERVICE_UNAVAILABLE` | Un serviciu intern nu răspunde. Reîncearcă peste câteva secunde. |"
          },
          "response": [
            {
              "name": "200",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/customers/:id",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "customers",
                    ":id"
                  ],
                  "variable": [
                    {
                      "key": "id",
                      "value": "",
                      "description": "posfixId-ul clientului"
                    }
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"posfixId\": \"b4e7c1a2-9d3f-4e61-8a0b-2f5c7d9e1a34\",\n  \"code\": \"0000000318\",\n  \"name\": \"SRL Floarea Soarelui\",\n  \"fiscalCode\": \"1012600034567\",\n  \"email\": \"contabil@floarea.md\",\n  \"phone\": \"+37322123456\",\n  \"address\": \"mun. Chișinău, str. Ismail 45\",\n  \"isActive\": true,\n  \"defaultPriceListId\": null\n}"
            }
          ]
        },
        {
          "name": "Listă clienți",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/customers?limit=50",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "customers"
              ],
              "query": [
                {
                  "key": "search",
                  "value": "floarea",
                  "description": "Caută în denumire, cod și cod fiscal.",
                  "disabled": true
                },
                {
                  "key": "limit",
                  "value": "50",
                  "description": "1–200, implicit 50."
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "nextCursor din pagina precedentă.",
                  "disabled": true
                }
              ]
            },
            "description": "**Scope necesar:** `customers:read`\n\nClienții comerciantului, pagină cu pagină. Include clienții inactivi și partenerul colectiv „Comenzi online”.\n\n### Parametri de interogare\n\n| Parametru | Tip | Implicit | Descriere |\n|---|---|---|---|\n| `search` | string | — | Fragment din denumire, cod sau cod fiscal, fără diferență de majuscule. |\n| `limit` | integer | 50 | Peste 200 se reduce la 200; 0 sau negativ înseamnă 50. |\n| `cursor` | string (uuid) | — | Valoarea `nextCursor` din pagina precedentă. Altceva decât un GUID → `400`. |\n\n### Răspuns 200\n\n| Câmp | Tip | Descriere |\n|---|---|---|\n| `items[]` | array | Clienții, cu câmpurile de la „Client după id”. |\n| `nextCursor` | string? | Cursorul paginii următoare; `null` pe ultima pagină. |\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 400 | `VALIDATION` | Cursor invalid. |\n| 401 | — | Cheia lipsește, e greșită, revocată sau expirată. Corp gol. |\n| 403 | — | Cheii îi lipsește scope-ul cerut sau modulul „API Integrare” nu e activ. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n| 503 | `SERVICE_UNAVAILABLE` | Un serviciu intern nu răspunde. Reîncearcă peste câteva secunde. |\n\n### De reținut\n\n- Ordinea e după id, nu după nume. Parcurge toate paginile până la `nextCursor: null`; nu te baza pe ordine."
          },
          "response": [
            {
              "name": "200",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/customers?limit=50",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "customers"
                  ],
                  "query": [
                    {
                      "key": "search",
                      "value": "floarea",
                      "description": "Caută în denumire, cod și cod fiscal.",
                      "disabled": true
                    },
                    {
                      "key": "limit",
                      "value": "50",
                      "description": "1–200, implicit 50."
                    },
                    {
                      "key": "cursor",
                      "value": "",
                      "description": "nextCursor din pagina precedentă.",
                      "disabled": true
                    }
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"items\": [\n    {\n      \"posfixId\": \"0a1f2b3c-4d5e-4f60-8a71-92b3c4d5e6f7\",\n      \"code\": \"0000000011\",\n      \"name\": \"Comenzi online\",\n      \"fiscalCode\": null,\n      \"email\": null,\n      \"phone\": null,\n      \"address\": null,\n      \"isActive\": true,\n      \"defaultPriceListId\": null\n    },\n    {\n      \"posfixId\": \"b4e7c1a2-9d3f-4e61-8a0b-2f5c7d9e1a34\",\n      \"code\": \"0000000318\",\n      \"name\": \"SRL Floarea Soarelui\",\n      \"fiscalCode\": \"1012600034567\",\n      \"email\": \"contabil@floarea.md\",\n      \"phone\": \"+37322123456\",\n      \"address\": \"mun. Chișinău, str. Ismail 45\",\n      \"isActive\": true,\n      \"defaultPriceListId\": null\n    }\n  ],\n  \"nextCursor\": null\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "4. Comenzi",
      "description": "Comenzile din magazinul tău. Comanda nu face contabilitate: nota o poartă livrarea.",
      "item": [
        {
          "name": "Creează comandă",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/orders",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "orders"
              ]
            },
            "description": "**Scope necesar:** `orders:write`\n\nComanda din magazinul tău devine comandă de client în PosFix, în starea `Draft`. Comanda e **informativă**: nu emite bon fiscal, factură sau notă contabilă. Acestea apar abia când comerciantul livrează marfa.\n\n### Corpul cererii\n\n| Câmp | Tip | Obligatoriu | Reguli |\n|---|---|---|---|\n| `externalOrderId` | string | da | Id-ul comenzii la tine, max 200. E **cheia de idempotență**: aceeași valoare nu creează a doua comandă. |\n| `orderDate` | string | nu | `yyyy-MM-dd`. Lipsă **sau în alt format** → azi (UTC), fără eroare. Anul alege seria de numerotare. |\n| `currency` | string | nu | Cod de 3 litere, implicit `MDL`. Prețurile nu se convertesc, iar comanda se înregistrează la cursul 1. |\n| `customer.idno` | string | nu | IDNO/IDNP de 13 cifre. Cu el, comanda merge pe partenerul cu acest cod, creat la nevoie. Fără el, pe partenerul colectiv „Comenzi online”. |\n| `customer.name`, `.email`, `.address` | string | nu | Se folosesc **doar** când se creează un partener nou din `idno`. Fără `idno` se pierd — pune-le în `deliveryContact`. |\n| `warehouseId` | string (uuid) | nu | Depozitul din care se rezervă marfa, încă de la creare. Fără el, nu se rezervă nimic. |\n| `deliveryAddress`, `deliveryContact` | string | nu | Adresa și persoana de contact pentru livrare. |\n| `lines[]` | array | da | Cel puțin o linie. |\n| `lines[].productCode` | string | da | **Codul** produsului din catalog (`code`, 10 cifre), nu SKU-ul. Cod necunoscut → `400`. |\n| `lines[].quantity` | number | da | > 0. |\n| `lines[].unitPrice` | number | nu | Prețul unitar **fără TVA**; TVA-ul se adaugă după cota produsului. Lipsă → prețul implicit al produsului din nomenclator, tratat tot ca preț fără TVA: dacă el include deja TVA, TVA-ul se adaugă a doua oară. **Trimite mereu `unitPrice`.** |\n| `lines[].discountPct` | number | nu | Reducere pe linie, 0–100. |\n\n### Răspuns 200\n\n| Câmp | Tip | Descriere |\n|---|---|---|\n| `orderId` | string (uuid) | Id-ul comenzii în PosFix. Numărul ei îl dă „Comandă după id”. |\n| `idempotentReplay` | boolean | `true` dacă `externalOrderId` exista deja. |\n| `status` | string | `created` sau `replayed`. E rezultatul cererii, nu starea comenzii (care e `Draft`). |\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 400 | `VALIDATION` | Câmp obligatoriu lipsă sau greșit; vezi `errors`. |\n| 400 | `INTEGRATIONS_ORDER_PRODUCT_UNKNOWN`, `INTEGRATIONS_ORDER_VAT_UNRESOLVED` | „Produs necunoscut: codul „X” nu există în catalogul comerciantului.” sau „Cota TVA a produsului „X” e nerezolvată, iar comanda nu se înregistrează cu 0%. Verificați cota produsului în catalog.” (format simplu; codul produsului în `params.code`). |\n| 409 | `INTEGRATIONS_ORDER_IN_FLIGHT` | „O comandă cu același externalOrderId se procesează chiar acum. Reîncercați peste câteva secunde.” — aceeași `externalOrderId` e încă în lucru. |\n| 401 | — | Cheia lipsește, e greșită, revocată sau expirată. Corp gol. |\n| 403 | — | Cheii îi lipsește scope-ul cerut sau modulul „API Integrare” nu e activ. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n| 503 | `SERVICE_UNAVAILABLE` | Un serviciu intern nu răspunde. Reîncearcă peste câteva secunde. |\n\n### De reținut\n\n- Răspunsul e `200`, nu `201`, și la creare, și la repetare.\n- La o repetare corpul **nu se compară**: primești comanda existentă chiar dacă ai schimbat liniile. O comandă nouă cere `externalOrderId` nou.\n- Linia: net = cantitate × preț × (1 − reducere/100); TVA = net × cota produsului; total = net + TVA. Netul și TVA-ul se rotunjesc la 2 zecimale, la par (rotunjire bancară).\n- Comanda nouă declanșează webhook-ul `order.created`.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"externalOrderId\": \"shop-100245\",\n  \"orderDate\": \"{{today}}\",\n  \"currency\": \"MDL\",\n  \"customer\": {\n    \"idno\": \"1012600034567\",\n    \"name\": \"SRL Floarea Soarelui\",\n    \"email\": \"comenzi@floarea.md\",\n    \"address\": \"mun. Chișinău, str. Ismail 45\"\n  },\n  \"deliveryAddress\": \"mun. Chișinău, bd. Dacia 27, ap. 4\",\n  \"deliveryContact\": \"Ana Rusu, +373 69 123 456\",\n  \"lines\": [\n    {\n      \"productCode\": \"0000000123\",\n      \"quantity\": 12,\n      \"unitPrice\": 15.42\n    },\n    {\n      \"productCode\": \"0000000124\",\n      \"quantity\": 3,\n      \"unitPrice\": 7.31,\n      \"discountPct\": 10\n    }\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [
            {
              "name": "200 — comandă creată",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/orders",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "orders"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"externalOrderId\": \"shop-100245\",\n  \"orderDate\": \"{{today}}\",\n  \"currency\": \"MDL\",\n  \"customer\": {\n    \"idno\": \"1012600034567\",\n    \"name\": \"SRL Floarea Soarelui\",\n    \"email\": \"comenzi@floarea.md\",\n    \"address\": \"mun. Chișinău, str. Ismail 45\"\n  },\n  \"deliveryAddress\": \"mun. Chișinău, bd. Dacia 27, ap. 4\",\n  \"deliveryContact\": \"Ana Rusu, +373 69 123 456\",\n  \"lines\": [\n    {\n      \"productCode\": \"0000000123\",\n      \"quantity\": 12,\n      \"unitPrice\": 15.42\n    },\n    {\n      \"productCode\": \"0000000124\",\n      \"quantity\": 3,\n      \"unitPrice\": 7.31,\n      \"discountPct\": 10\n    }\n  ]\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"orderId\": \"3c9a1f52-7d3e-4b8a-a0c4-6e2b91f0d7a8\",\n  \"idempotentReplay\": false,\n  \"status\": \"created\"\n}"
            },
            {
              "name": "200 — repetare cu același externalOrderId",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/orders",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "orders"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"externalOrderId\": \"shop-100245\",\n  \"orderDate\": \"{{today}}\",\n  \"currency\": \"MDL\",\n  \"customer\": {\n    \"idno\": \"1012600034567\",\n    \"name\": \"SRL Floarea Soarelui\",\n    \"email\": \"comenzi@floarea.md\",\n    \"address\": \"mun. Chișinău, str. Ismail 45\"\n  },\n  \"deliveryAddress\": \"mun. Chișinău, bd. Dacia 27, ap. 4\",\n  \"deliveryContact\": \"Ana Rusu, +373 69 123 456\",\n  \"lines\": [\n    {\n      \"productCode\": \"0000000123\",\n      \"quantity\": 12,\n      \"unitPrice\": 15.42\n    },\n    {\n      \"productCode\": \"0000000124\",\n      \"quantity\": 3,\n      \"unitPrice\": 7.31,\n      \"discountPct\": 10\n    }\n  ]\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"orderId\": \"3c9a1f52-7d3e-4b8a-a0c4-6e2b91f0d7a8\",\n  \"idempotentReplay\": true,\n  \"status\": \"replayed\"\n}"
            },
            {
              "name": "400 — produs necunoscut",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/orders",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "orders"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"externalOrderId\": \"shop-100245\",\n  \"orderDate\": \"{{today}}\",\n  \"currency\": \"MDL\",\n  \"customer\": {\n    \"idno\": \"1012600034567\",\n    \"name\": \"SRL Floarea Soarelui\",\n    \"email\": \"comenzi@floarea.md\",\n    \"address\": \"mun. Chișinău, str. Ismail 45\"\n  },\n  \"deliveryAddress\": \"mun. Chișinău, bd. Dacia 27, ap. 4\",\n  \"deliveryContact\": \"Ana Rusu, +373 69 123 456\",\n  \"lines\": [\n    {\n      \"productCode\": \"0000000123\",\n      \"quantity\": 12,\n      \"unitPrice\": 15.42\n    },\n    {\n      \"productCode\": \"0000000124\",\n      \"quantity\": 3,\n      \"unitPrice\": 7.31,\n      \"discountPct\": 10\n    }\n  ]\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "Bad Request",
              "code": 400,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"type\": \"https://httpstatuses.io/400\",\n  \"title\": \"Validation Failed\",\n  \"status\": 400,\n  \"detail\": \"Produs necunoscut: codul „0000000999” nu există în catalogul comerciantului.\",\n  \"errorCode\": \"INTEGRATIONS_ORDER_PRODUCT_UNKNOWN\",\n  \"params\": {\n    \"code\": \"0000000999\"\n  }\n}"
            },
            {
              "name": "400 — validare",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/orders",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "orders"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"externalOrderId\": \"shop-100245\",\n  \"orderDate\": \"{{today}}\",\n  \"currency\": \"MDL\",\n  \"customer\": {\n    \"idno\": \"1012600034567\",\n    \"name\": \"SRL Floarea Soarelui\",\n    \"email\": \"comenzi@floarea.md\",\n    \"address\": \"mun. Chișinău, str. Ismail 45\"\n  },\n  \"deliveryAddress\": \"mun. Chișinău, bd. Dacia 27, ap. 4\",\n  \"deliveryContact\": \"Ana Rusu, +373 69 123 456\",\n  \"lines\": [\n    {\n      \"productCode\": \"0000000123\",\n      \"quantity\": 12,\n      \"unitPrice\": 15.42\n    },\n    {\n      \"productCode\": \"0000000124\",\n      \"quantity\": 3,\n      \"unitPrice\": 7.31,\n      \"discountPct\": 10\n    }\n  ]\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "Bad Request",
              "code": 400,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json; charset=utf-8"
                }
              ],
              "body": "{\n  \"type\": \"https://httpstatuses.io/400\",\n  \"title\": \"VALIDATION\",\n  \"status\": 400,\n  \"detail\": \"Câmpul este obligatoriu.\",\n  \"errorCode\": \"VALIDATION\",\n  \"errors\": {\n    \"externalOrderId\": [\n      \"Câmpul este obligatoriu.\"\n    ]\n  },\n  \"errorCodes\": {\n    \"externalOrderId\": [\n      {\n        \"code\": \"COMMON_REQUIRED\",\n        \"params\": {}\n      }\n    ]\n  }\n}"
            }
          ]
        },
        {
          "name": "Comandă după id",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/orders/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "orders",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "orderId din „Creează comandă”"
                }
              ]
            },
            "description": "**Scope necesar:** `orders:read`\n\nO comandă de client, cu liniile, plățile și documentele de vânzare generate din ea. Întoarce orice comandă a comerciantului, inclusiv cele create în panou sau la casă.\n\n### Parametri de cale\n\n| Parametru | Tip | Descriere |\n|---|---|---|\n| `id` | string (uuid) | Id-ul comenzii. |\n\n### Răspuns 200\n\n| Câmp | Tip | Descriere |\n|---|---|---|\n| `id`, `number` | string | Id-ul și numărul comenzii (10 cifre). |\n| `status` | string | Starea; vezi tabelul de mai jos. |\n| `partnerId` | string (uuid) | Clientul. Poate fi partenerul colectiv „Comenzi online”. |\n| `orderDate` | string | `yyyy-MM-dd`. |\n| `expectedShipDate` | string? | Data de livrare planificată, pusă de comerciant. |\n| `currencyCode` | string | Moneda comenzii. |\n| `subTotal`, `vatTotal`, `total` | number | Fără TVA, TVA, cu TVA — în moneda comenzii. |\n| `reservedQtyTotal`, `shippedQtyTotal` | number | Cantități rezervate și livrate, pe toată comanda. |\n| `invoicedSum` | number | Suma vânzărilor contabilizate din comandă, în lei. |\n| `paidSum` | number | Cât s-a plătit (vânzări, avansuri, factura de plată), în lei. |\n| `deliveryAddress`, `deliveryContact`, `trackingNumber` | string? | Livrarea. |\n| `description`, `notes` | string? | Completate de comerciant în panou. |\n| `lines[]` | array | `lineNumber`, `productId` (`posfixId` din catalog), `productName`, `unitOfMeasure`, `quantity`, `reservedQty`, `shippedQty`, `unitPrice` (fără TVA), `discountPct`, `vatRatePct`, `lineTotal` (cu TVA). |\n| `generatedSales[]` | array | Documentele de vânzare făcute din comandă: `id`, `number`, `status` (`Draft`/`Posted`), `date`, `total`. |\n\n### Stările comenzii\n\n| Stare | Ce înseamnă |\n|---|---|\n| `Draft` | Primită, neconfirmată. Așa pornește orice comandă venită prin API. |\n| `Confirmed` | Confirmată de comerciant; marfa se rezervă. La comanda din API cu `warehouseId`, rezervarea există deja de la creare. |\n| `Fulfilling` | Livrată parțial. |\n| `Fulfilled` | Livrată integral: prin factură, lot de curier sau ridicare de la casă. |\n| `Closed` | Închisă manual; restul rezervării se eliberează. |\n| `Cancelled` | Anulată. Comerciantul o poate redeschide. |\n\nStarea o schimbă comerciantul și fluxurile de livrare; API-ul n-o poate schimba.\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 404 | — | Comanda nu există sau e ștearsă. Corp gol. |\n| 400 | — | `id` nu e GUID. |\n| 401 | — | Cheia lipsește, e greșită, revocată sau expirată. Corp gol. |\n| 403 | — | Cheii îi lipsește scope-ul cerut sau modulul „API Integrare” nu e activ. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n| 503 | `SERVICE_UNAVAILABLE` | Un serviciu intern nu răspunde. Reîncearcă peste câteva secunde. |\n\n### De reținut\n\n- La ridicare de la casă sau prin curier, comanda poate ajunge `Fulfilled` fără nimic în `generatedSales`: nota contabilă o poartă raportul Z sau lotul de livrare."
          },
          "response": [
            {
              "name": "200",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/orders/:id",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "orders",
                    ":id"
                  ],
                  "variable": [
                    {
                      "key": "id",
                      "value": "",
                      "description": "orderId din „Creează comandă”"
                    }
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"id\": \"3c9a1f52-7d3e-4b8a-a0c4-6e2b91f0d7a8\",\n  \"number\": \"0000000042\",\n  \"status\": \"Draft\",\n  \"partnerId\": \"b4e7c1a2-9d3f-4e61-8a0b-2f5c7d9e1a34\",\n  \"orderDate\": \"2026-09-25\",\n  \"expectedShipDate\": null,\n  \"currencyCode\": \"MDL\",\n  \"subTotal\": 204.78,\n  \"vatTotal\": 38.59,\n  \"total\": 243.37,\n  \"reservedQtyTotal\": 0,\n  \"shippedQtyTotal\": 0,\n  \"invoicedSum\": 0,\n  \"paidSum\": 0,\n  \"description\": null,\n  \"notes\": null,\n  \"deliveryAddress\": \"mun. Chișinău, bd. Dacia 27, ap. 4\",\n  \"deliveryContact\": \"Ana Rusu, +373 69 123 456\",\n  \"trackingNumber\": null,\n  \"lines\": [\n    {\n      \"lineNumber\": 1,\n      \"productId\": \"0b8f5e2a-6c1d-4e7f-9a3b-2d4c6e8f1a01\",\n      \"productName\": \"Apă minerală Borjomi 0,5 L\",\n      \"unitOfMeasure\": \"buc\",\n      \"quantity\": 12,\n      \"reservedQty\": 0,\n      \"shippedQty\": 0,\n      \"unitPrice\": 15.42,\n      \"discountPct\": 0,\n      \"vatRatePct\": 20,\n      \"lineTotal\": 222.05\n    },\n    {\n      \"lineNumber\": 2,\n      \"productId\": \"1c9a6f3b-7d2e-4f80-8b4c-3e5d7f9a2b02\",\n      \"productName\": \"Pâine Franzeluța albă feliată 400 g\",\n      \"unitOfMeasure\": \"buc\",\n      \"quantity\": 3,\n      \"reservedQty\": 0,\n      \"shippedQty\": 0,\n      \"unitPrice\": 7.31,\n      \"discountPct\": 10,\n      \"vatRatePct\": 8,\n      \"lineTotal\": 21.32\n    }\n  ],\n  \"generatedSales\": []\n}"
            }
          ]
        },
        {
          "name": "Listă comenzi",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/orders?from={{monthStart}}&to={{today}}&page=1&pageSize=20",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "orders"
              ],
              "query": [
                {
                  "key": "status",
                  "value": "Draft",
                  "description": "Draft · Confirmed · Fulfilling · Fulfilled · Closed · Cancelled",
                  "disabled": true
                },
                {
                  "key": "from",
                  "value": "{{monthStart}}",
                  "description": "yyyy-MM-dd, inclusiv."
                },
                {
                  "key": "to",
                  "value": "{{today}}",
                  "description": "yyyy-MM-dd, inclusiv."
                },
                {
                  "key": "page",
                  "value": "1",
                  "description": "De la 1."
                },
                {
                  "key": "pageSize",
                  "value": "20",
                  "description": "1–200, implicit 20."
                }
              ]
            },
            "description": "**Scope necesar:** `orders:read`\n\nComenzile comerciantului, cele mai noi întâi (după dată, apoi după număr).\n\n### Parametri de interogare\n\n| Parametru | Tip | Implicit | Descriere |\n|---|---|---|---|\n| `status` | string | toate | O stare din tabelul de la „Comandă după id”. Altă valoare → `400`. |\n| `from` / `to` | string | — | Data comenzii, inclusiv. Trimite `yyyy-MM-dd`: serverul acceptă și alte forme și le poate citi greșit (`01.09.2026` devine 9 ianuarie). Un text care nu e dată → `400`. |\n| `page` | integer | 1 | Numărul paginii, de la 1. |\n| `pageSize` | integer | 20 | 1–200. **Peste 200 revine la 20**, nu la 200. |\n\n### Răspuns 200\n\n| Câmp | Tip | Descriere |\n|---|---|---|\n| `items[]` | array | `id`, `number`, `status`, `orderDate`, `expectedShipDate`, `total`, `currencyCode`, `paidSum` (lei), `shippedQtyTotal`, `deliveryAddress`, `deliveryContact`, `trackingNumber`. |\n| `total` | integer | Câte comenzi corespund filtrului, pe toate paginile. |\n| `page`, `pageSize` | integer | Valorile aplicate. |\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 400 | `VALIDATION` | Stare sau dată invalidă. |\n| 401 | — | Cheia lipsește, e greșită, revocată sau expirată. Corp gol. |\n| 403 | — | Cheii îi lipsește scope-ul cerut sau modulul „API Integrare” nu e activ. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n| 503 | `SERVICE_UNAVAILABLE` | Un serviciu intern nu răspunde. Reîncearcă peste câteva secunde. |"
          },
          "response": [
            {
              "name": "200",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/orders?from={{monthStart}}&to={{today}}&page=1&pageSize=20",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "orders"
                  ],
                  "query": [
                    {
                      "key": "status",
                      "value": "Draft",
                      "description": "Draft · Confirmed · Fulfilling · Fulfilled · Closed · Cancelled",
                      "disabled": true
                    },
                    {
                      "key": "from",
                      "value": "{{monthStart}}",
                      "description": "yyyy-MM-dd, inclusiv."
                    },
                    {
                      "key": "to",
                      "value": "{{today}}",
                      "description": "yyyy-MM-dd, inclusiv."
                    },
                    {
                      "key": "page",
                      "value": "1",
                      "description": "De la 1."
                    },
                    {
                      "key": "pageSize",
                      "value": "20",
                      "description": "1–200, implicit 20."
                    }
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"items\": [\n    {\n      \"id\": \"3c9a1f52-7d3e-4b8a-a0c4-6e2b91f0d7a8\",\n      \"number\": \"0000000042\",\n      \"status\": \"Draft\",\n      \"orderDate\": \"2026-09-25\",\n      \"expectedShipDate\": null,\n      \"total\": 243.37,\n      \"currencyCode\": \"MDL\",\n      \"paidSum\": 0,\n      \"shippedQtyTotal\": 0,\n      \"deliveryAddress\": \"mun. Chișinău, bd. Dacia 27, ap. 4\",\n      \"deliveryContact\": \"Ana Rusu, +373 69 123 456\",\n      \"trackingNumber\": null\n    }\n  ],\n  \"total\": 1,\n  \"page\": 1,\n  \"pageSize\": 20\n}"
            }
          ]
        },
        {
          "name": "Actualizează livrarea",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/orders/:id/delivery",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "orders",
                ":id",
                "delivery"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "orderId"
                }
              ]
            },
            "description": "**Scope necesar:** `orders:write`\n\nPune numărul de urmărire și, la nevoie, adresa sau contactul de livrare, după ce ai expediat.\n\n### Parametri de cale\n\n| Parametru | Tip | Descriere |\n|---|---|---|\n| `id` | string (uuid) | Id-ul comenzii. |\n\n### Corpul cererii\n\n| Câmp | Tip | Obligatoriu | Reguli |\n|---|---|---|---|\n| `trackingNumber` | string | nu | Numărul de urmărire al coletului. |\n| `deliveryAddress` | string | nu | Adresa de livrare. |\n| `deliveryContact` | string | nu | Persoana de contact. |\n\n### Răspuns\n\n**204** fără corp.\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 404 | — | Comanda nu există sau e ștearsă. Corp gol. |\n| 400 | — | `id` nu e GUID. |\n| 401 | — | Cheia lipsește, e greșită, revocată sau expirată. Corp gol. |\n| 403 | — | Cheii îi lipsește scope-ul cerut sau modulul „API Integrare” nu e activ. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n| 503 | `SERVICE_UNAVAILABLE` | Un serviciu intern nu răspunde. Reîncearcă peste câteva secunde. |\n\n### De reținut\n\n- Se scriu doar câmpurile trimise și nevide. Un câmp **nu se poate goli** prin API.\n- Merge în orice stare a comenzii și nu trimite webhook.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"trackingNumber\": \"RB123456789MD\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [
            {
              "name": "204 — actualizat",
              "originalRequest": {
                "method": "PATCH",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/orders/:id/delivery",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "orders",
                    ":id",
                    "delivery"
                  ],
                  "variable": [
                    {
                      "key": "id",
                      "value": "",
                      "description": "orderId"
                    }
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"trackingNumber\": \"RB123456789MD\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "No Content",
              "code": 204,
              "_postman_previewlanguage": "text",
              "header": [],
              "body": ""
            }
          ]
        }
      ]
    },
    {
      "name": "5. Vânzări (rapoarte Z)",
      "description": "Vânzările de la casă, zi cu zi. Un raport Z = o zi de vânzări pe o casă, gata de contabilizat.",
      "item": [
        {
          "name": "Case de marcat",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/terminals",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "terminals"
              ]
            },
            "description": "**Scope necesar:** `sales:read`\n\nCasele de marcat ale comerciantului. Leagă-ți casele după `id` — e stabil. Cheia limitată la anumite case le vede doar pe acelea.\n\n### Răspuns 200\n\n| Câmp | Tip | Descriere |\n|---|---|---|\n| `items[].id` | string (uuid) | Id-ul casei. |\n| `items[].name` | string | Denumirea; poate fi goală. |\n| `items[].code` | string | Codul casei (`0000-00124-QWERT`); poate fi gol. |\n| `items[].model` | string? | Modelul. |\n| `items[].factoryNumber` | string? | Numărul de fabrică. |\n| `items[].fiscalRegistrationNumber` | string? | Numărul de înregistrare la SFS. |\n| `items[].address` | string? | Adresa de instalare. |\n| `items[].warehouseId` | string (uuid)? | Depozitul casei. |\n| `items[].isFiscalRegistered` | boolean | Casa e înregistrată la SFS. |\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 401 | — | Cheia lipsește, e greșită, revocată sau expirată. Corp gol. |\n| 403 | — | Cheii îi lipsește scope-ul cerut sau modulul „API Integrare” nu e activ. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n| 503 | `SERVICE_UNAVAILABLE` | Un serviciu intern nu răspunde. Reîncearcă peste câteva secunde. |\n\n### De reținut\n\n- Lista include și casele dezactivate (nu și pe cele șterse). Nu are paginare."
          },
          "response": [
            {
              "name": "200",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/terminals",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "terminals"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"items\": [\n    {\n      \"id\": \"5b2d6f3e-8c1a-4f7e-9a2b-3c4d5e6f7a81\",\n      \"name\": \"Casa 1 — Magazin Centru\",\n      \"code\": \"0000-00124-QWERT\",\n      \"model\": \"POSfix\",\n      \"factoryNumber\": \"000000124QWERT\",\n      \"fiscalRegistrationNumber\": \"J403001234\",\n      \"address\": \"mun. Chișinău, str. București 45\",\n      \"warehouseId\": \"0d9e2a8b-1f3c-4b6e-8a7d-2c5f9e1b3a46\",\n      \"isFiscalRegistered\": true\n    }\n  ]\n}"
            }
          ]
        },
        {
          "name": "Rapoarte Z",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/z-reports?from={{monthStart}}&to={{today}}&page=1&pageSize=50",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "z-reports"
              ],
              "query": [
                {
                  "key": "from",
                  "value": "{{monthStart}}",
                  "description": "yyyy-MM-dd, obligatoriu."
                },
                {
                  "key": "to",
                  "value": "{{today}}",
                  "description": "yyyy-MM-dd, obligatoriu, ≥ from."
                },
                {
                  "key": "terminalId",
                  "value": "",
                  "description": "Id de casă; repetabil.",
                  "disabled": true
                },
                {
                  "key": "page",
                  "value": "1",
                  "description": "De la 1."
                },
                {
                  "key": "pageSize",
                  "value": "50",
                  "description": "1–200, implicit 50."
                }
              ]
            },
            "description": "**Scope necesar:** `sales:read`\n\nRapoartele Z închise într-o perioadă, în ordinea închiderii. De aici pleacă importul vânzărilor în contabilitatea ta: un Z = o zi de vânzări pe o casă.\n\n### Parametri de interogare\n\n| Parametru | Tip | Implicit | Descriere |\n|---|---|---|---|\n| `from` / `to` | string | — | Obligatorii, `yyyy-MM-dd`, inclusiv; `to` ≥ `from`. Filtrează după **momentul închiderii**, în zile UTC. |\n| `terminalId` | string (uuid) | toate | Repetabil: `?terminalId=a&terminalId=b`. |\n| `page` | integer | 1 | ≥ 1. |\n| `pageSize` | integer | 50 | 1–200. |\n\n### Răspuns 200\n\n| Câmp | Tip | Descriere |\n|---|---|---|\n| `items[]` | array | Rapoartele Z; câmpurile sunt în tabelul de mai jos. |\n| `totalCount` | integer | Câte rapoarte corespund filtrului. |\n| `page`, `pageSize` | integer | Valorile aplicate. |\n| `totalPages` | integer | Numărul de pagini; 0 când nu e nimic. |\n| `hasNextPage`, `hasPreviousPage` | boolean | Navigarea. |\n| `summary`, `filterableColumns`, `ignoredFilters` | null | Mereu `null`. |\n\n### Raportul Z\n\n| Câmp | Tip | Descriere |\n|---|---|---|\n| `id` | string (uuid) | Id-ul raportului Z. Folosește-l drept cheie de idempotență în sistemul tău. |\n| `terminalId` | string (uuid) | Casa. |\n| `number` | integer | Numărul Z de pe casă. |\n| `reportDateTime` | string | Momentul închiderii, ISO 8601 UTC. |\n| `tradingDay` | string | Ziua comercială, `yyyy-MM-dd`: ziua primei vânzări din Z. Datează documentele contabile după ea. |\n| `warehouseId` | string (uuid)? | Depozitul. |\n| `cashierName` | string? | Casierul. |\n| `totalSales`, `totalReturns` | number | Vânzări și retururi, lei, cu TVA. |\n| `totalDiscounts` | number | Reducerile, lei. |\n| `netRevenue` | number | `totalSales − totalReturns`. Net de retururi, **nu** de TVA. |\n| `totalVat` | number | TVA-ul zilei, lei. |\n| `cashAmount`, `cardAmount`, `otherAmount` | number | Încasări: numerar, card, restul formelor de plată. |\n| `receiptsCount`, `returnsCount` | integer | Bonuri și retururi. |\n| `sealedAt` | string? | Momentul sigilării Z-ului, ISO 8601 UTC. |\n| `fiscalSeal` | string? | Amprenta SHA-256 (64 de caractere hex) a Z-ului. |\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 400 | `INTEGRATIONS_API_DATE_REQUIRED`, `INTEGRATIONS_API_DATE_INVALID`, `COMMON_DATE_ON_OR_AFTER`, `COMMON_BETWEEN` | „Parametrul este obligatoriu: o dată în formatul yyyy-MM-dd.”, „Data trebuie să fie cel mai devreme 01.09.2026.”, „Valoarea trebuie să fie între 1 și 200.” — în `errors` și `errorCodes`, pe câmp (formatul de legare). |\n| 401 | — | Cheia lipsește, e greșită, revocată sau expirată. Corp gol. |\n| 403 | — | Cheii îi lipsește scope-ul cerut sau modulul „API Integrare” nu e activ. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n| 503 | `SERVICE_UNAVAILABLE` | Un serviciu intern nu răspunde. Reîncearcă peste câteva secunde. |\n\n### De reținut\n\n- Moldova e la UTC+2/+3: un Z închis la 01:30 ora locală cade, la filtrare, pe ziua precedentă.\n- O cheie limitată la anumite case vede doar rapoartele lor. Un filtru pe o casă nepermisă întoarce o pagină goală, nu `403`."
          },
          "response": [
            {
              "name": "200",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/z-reports?from={{monthStart}}&to={{today}}&page=1&pageSize=50",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "z-reports"
                  ],
                  "query": [
                    {
                      "key": "from",
                      "value": "{{monthStart}}",
                      "description": "yyyy-MM-dd, obligatoriu."
                    },
                    {
                      "key": "to",
                      "value": "{{today}}",
                      "description": "yyyy-MM-dd, obligatoriu, ≥ from."
                    },
                    {
                      "key": "terminalId",
                      "value": "",
                      "description": "Id de casă; repetabil.",
                      "disabled": true
                    },
                    {
                      "key": "page",
                      "value": "1",
                      "description": "De la 1."
                    },
                    {
                      "key": "pageSize",
                      "value": "50",
                      "description": "1–200, implicit 50."
                    }
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"items\": [\n    {\n      \"id\": \"8f1c2b7a-3d4e-4f5a-9b6c-7d8e9f0a1b2c\",\n      \"terminalId\": \"5b2d6f3e-8c1a-4f7e-9a2b-3c4d5e6f7a81\",\n      \"number\": 67,\n      \"reportDateTime\": \"2026-09-24T17:47:31Z\",\n      \"tradingDay\": \"2026-09-24\",\n      \"warehouseId\": \"0d9e2a8b-1f3c-4b6e-8a7d-2c5f9e1b3a46\",\n      \"cashierName\": \"Ana Rusu\",\n      \"totalSales\": 282,\n      \"totalReturns\": 89.9,\n      \"totalDiscounts\": 0,\n      \"netRevenue\": 192.1,\n      \"totalVat\": 22.56,\n      \"cashAmount\": 102.2,\n      \"cardAmount\": 89.9,\n      \"otherAmount\": 0,\n      \"receiptsCount\": 4,\n      \"returnsCount\": 1,\n      \"sealedAt\": \"2026-09-24T17:47:33Z\",\n      \"fiscalSeal\": \"3f9a0c1e5b7d2a4f6e8c0b1d3f5a7c9e1b3d5f7a9c0e2b4d6f8a0c2e4b6d8f0a\"\n    }\n  ],\n  \"totalCount\": 1,\n  \"page\": 1,\n  \"pageSize\": 50,\n  \"summary\": null,\n  \"filterableColumns\": null,\n  \"ignoredFilters\": null,\n  \"totalPages\": 1,\n  \"hasNextPage\": false,\n  \"hasPreviousPage\": false\n}"
            },
            {
              "name": "400 — perioadă lipsă",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/z-reports?from={{monthStart}}&to={{today}}&page=1&pageSize=50",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "z-reports"
                  ],
                  "query": [
                    {
                      "key": "from",
                      "value": "{{monthStart}}",
                      "description": "yyyy-MM-dd, obligatoriu."
                    },
                    {
                      "key": "to",
                      "value": "{{today}}",
                      "description": "yyyy-MM-dd, obligatoriu, ≥ from."
                    },
                    {
                      "key": "terminalId",
                      "value": "",
                      "description": "Id de casă; repetabil.",
                      "disabled": true
                    },
                    {
                      "key": "page",
                      "value": "1",
                      "description": "De la 1."
                    },
                    {
                      "key": "pageSize",
                      "value": "50",
                      "description": "1–200, implicit 50."
                    }
                  ]
                }
              },
              "status": "Bad Request",
              "code": 400,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/problem+json"
                }
              ],
              "body": "{\n  \"statusCode\": 400,\n  \"message\": \"One or more errors occurred!\",\n  \"errors\": {\n    \"from\": [\n      \"Parametrul este obligatoriu: o dată în formatul yyyy-MM-dd.\"\n    ]\n  },\n  \"errorCodes\": {\n    \"from\": [\n      {\n        \"code\": \"INTEGRATIONS_API_DATE_REQUIRED\",\n        \"params\": {}\n      }\n    ]\n  }\n}"
            }
          ]
        },
        {
          "name": "Raport Z",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/z-reports/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "z-reports",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "id din „Rapoarte Z”"
                }
              ]
            },
            "description": "**Scope necesar:** `sales:read`\n\nUn raport Z cu vânzările lui adunate pe produs, formele de plată și TVA-ul pe cote. E tot ce-ți trebuie ca să contabilizezi ziua.\n\n### Parametri de cale\n\n| Parametru | Tip | Descriere |\n|---|---|---|\n| `id` | string (uuid) | Id-ul raportului Z. |\n\n### Răspuns 200\n\n| Câmp | Tip | Descriere |\n|---|---|---|\n| `report` | object | Raportul Z, cu câmpurile de la „Rapoarte Z”. |\n| `lines[]` | array | Vânzările zilei, adunate pe produs × cotă TVA × sens (vânzare/retur). Același produs poate apărea pe mai multe linii; returul are cantitate și total negative. |\n| `lines[].productCode`, `productName` | string | Codul și denumirea; pot fi goale. |\n| `lines[].productId` | string (uuid)? | Produsul din nomenclator; `null` dacă articolul de la casă nu e legat de nomenclator. |\n| `lines[].posProductId` | string (uuid)? | Cardul de produs de la casă. |\n| `lines[].unit` | string? | Unitatea de pe bon (`buc`, `kg`). |\n| `lines[].quantity`, `lines[].total` | number | Cantitatea și totalul **cu TVA**, lei. |\n| `lines[].vatCode` | string | 20% → `A`, 8% → `B`, altfel litera de pe bon (`C`, `D`, `_` = fără TVA). |\n| `lines[].vatAmount`, `lines[].vatPercent` | number | TVA-ul încasat efectiv și cota. Nu recalcula TVA-ul din literă. |\n| `lines[].discountAmount` | number | Reducerea acordată pe bonuri pentru linie, deja scăzută din `total`. `total` + `discountAmount` = valoarea înainte de reducere. |\n| `tenders[]` | array | Încasările pe forme de plată: `code`, `amount`, `isCash`. De aici se decontează plățile. |\n| `vat` | object | `totalA`…`totalD` (cu TVA) și `vatA`…`vatD` pe cote; `total` = suma liniilor. Litera `_` și cele necunoscute intră în `D`. |\n| `cardVoidsTotal` | number | Plăți cu cardul anulate înainte de decontarea băncii: banca decontează mai puțin decât arată `tenders`. |\n| `cardRefundsTotal` | number | Rambursări pe card după decontare; aparțin unei perioade ulterioare. |\n\n### Codurile din `tenders[].code`\n\n`cash` · `card` · `voucher` · `check` · `ticket` · `mealTicket` · `subscription` · `transfer` · `credit` · `leasing` · `advance` · `deposit` · `pledge` · `compensation` · `otherCredit`. `other` apare doar la rapoartele vechi fără defalcare. `isCash` e `true` doar pentru `cash`.\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 404 | — | Z-ul nu există, e al altui comerciant sau casa lui nu e permisă cheii. Corp gol. |\n| 400 | — | `id` nu e GUID (formatul de legare). |\n| 401 | — | Cheia lipsește, e greșită, revocată sau expirată. Corp gol. |\n| 403 | — | Cheii îi lipsește scope-ul cerut sau modulul „API Integrare” nu e activ. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n| 503 | `SERVICE_UNAVAILABLE` | Un serviciu intern nu răspunde. Reîncearcă peste câteva secunde. |"
          },
          "response": [
            {
              "name": "200",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/z-reports/:id",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "z-reports",
                    ":id"
                  ],
                  "variable": [
                    {
                      "key": "id",
                      "value": "",
                      "description": "id din „Rapoarte Z”"
                    }
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"report\": {\n    \"id\": \"8f1c2b7a-3d4e-4f5a-9b6c-7d8e9f0a1b2c\",\n    \"terminalId\": \"5b2d6f3e-8c1a-4f7e-9a2b-3c4d5e6f7a81\",\n    \"number\": 67,\n    \"reportDateTime\": \"2026-09-24T17:47:31Z\",\n    \"tradingDay\": \"2026-09-24\",\n    \"warehouseId\": \"0d9e2a8b-1f3c-4b6e-8a7d-2c5f9e1b3a46\",\n    \"cashierName\": \"Ana Rusu\",\n    \"totalSales\": 282,\n    \"totalReturns\": 89.9,\n    \"totalDiscounts\": 0,\n    \"netRevenue\": 192.1,\n    \"totalVat\": 22.56,\n    \"cashAmount\": 102.2,\n    \"cardAmount\": 89.9,\n    \"otherAmount\": 0,\n    \"receiptsCount\": 4,\n    \"returnsCount\": 1,\n    \"sealedAt\": \"2026-09-24T17:47:33Z\",\n    \"fiscalSeal\": \"3f9a0c1e5b7d2a4f6e8c0b1d3f5a7c9e1b3d5f7a9c0e2b4d6f8a0c2e4b6d8f0a\"\n  },\n  \"lines\": [\n    {\n      \"productCode\": \"4840167001234\",\n      \"productId\": \"a1b2c3d4-0000-4000-8000-000000000001\",\n      \"posProductId\": \"b1c2d3e4-0000-4000-8000-000000000001\",\n      \"productName\": \"Lapte Fabrikant 2,5% 1L\",\n      \"unit\": \"buc\",\n      \"quantity\": 3,\n      \"total\": 59.7,\n      \"vatCode\": \"B\",\n      \"vatAmount\": 4.42,\n      \"vatPercent\": 8,\n      \"discountAmount\": 0\n    },\n    {\n      \"productCode\": \"4840001000057\",\n      \"productId\": \"a1b2c3d4-0000-4000-8000-000000000002\",\n      \"posProductId\": \"b1c2d3e4-0000-4000-8000-000000000002\",\n      \"productName\": \"Pâine albă 500 g\",\n      \"unit\": \"buc\",\n      \"quantity\": 5,\n      \"total\": 42.5,\n      \"vatCode\": \"B\",\n      \"vatAmount\": 3.15,\n      \"vatPercent\": 8,\n      \"discountAmount\": 0\n    },\n    {\n      \"productCode\": \"8714599101230\",\n      \"productId\": \"a1b2c3d4-0000-4000-8000-000000000003\",\n      \"posProductId\": \"b1c2d3e4-0000-4000-8000-000000000003\",\n      \"productName\": \"Cafea Jacobs Monarch 250 g\",\n      \"unit\": \"buc\",\n      \"quantity\": 2,\n      \"total\": 179.8,\n      \"vatCode\": \"A\",\n      \"vatAmount\": 29.97,\n      \"vatPercent\": 20,\n      \"discountAmount\": 0\n    },\n    {\n      \"productCode\": \"8714599101230\",\n      \"productId\": \"a1b2c3d4-0000-4000-8000-000000000003\",\n      \"posProductId\": \"b1c2d3e4-0000-4000-8000-000000000003\",\n      \"productName\": \"Cafea Jacobs Monarch 250 g\",\n      \"unit\": \"buc\",\n      \"quantity\": -1,\n      \"total\": -89.9,\n      \"vatCode\": \"A\",\n      \"vatAmount\": -14.98,\n      \"vatPercent\": 20,\n      \"discountAmount\": 0\n    }\n  ],\n  \"tenders\": [\n    {\n      \"code\": \"cash\",\n      \"amount\": 102.2,\n      \"isCash\": true\n    },\n    {\n      \"code\": \"card\",\n      \"amount\": 89.9,\n      \"isCash\": false\n    }\n  ],\n  \"vat\": {\n    \"totalA\": 89.9,\n    \"totalB\": 102.2,\n    \"totalC\": 0,\n    \"totalD\": 0,\n    \"vatA\": 14.99,\n    \"vatB\": 7.57,\n    \"vatC\": 0,\n    \"vatD\": 0,\n    \"total\": 192.1\n  },\n  \"cardVoidsTotal\": 0,\n  \"cardRefundsTotal\": 0\n}"
            }
          ]
        },
        {
          "name": "Bonurile unui Z",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/z-reports/:id/receipts?page=1&pageSize=100",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "z-reports",
                ":id",
                "receipts"
              ],
              "query": [
                {
                  "key": "page",
                  "value": "1",
                  "description": "De la 1."
                },
                {
                  "key": "pageSize",
                  "value": "100",
                  "description": "1–500, implicit 100."
                }
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "id din „Rapoarte Z”"
                }
              ]
            },
            "description": "**Scope necesar:** `sales:read`\n\nBonurile brute ale unui Z, cu liniile și plățile fiecăruia, în ordinea emiterii. Pentru contabilitate ajunge „Raport Z”; bonurile servesc la reconciliere și la analize pe client sau pe oră.\n\n### Parametri de cale\n\n| Parametru | Tip | Descriere |\n|---|---|---|\n| `id` | string (uuid) | Id-ul raportului Z. |\n\n### Parametri de interogare\n\n| Parametru | Tip | Implicit | Descriere |\n|---|---|---|---|\n| `page` | integer | 1 | ≥ 1. |\n| `pageSize` | integer | 100 | 1–500. |\n\n### Răspuns 200\n\n| Câmp | Tip | Descriere |\n|---|---|---|\n| `items[]` | array | Bonurile. |\n| `items[].id`, `transactionNumber` | string, integer | Id-ul și numărul tranzacției. |\n| `items[].receiptNumber` | integer? | Numărul bonului. |\n| `items[].fiscalReceiptNumber`, `sfsFiscalCode`, `sfsReceiptUrl` | string? | Datele fiscale; adresa de verificare la SFS. |\n| `items[].dateTime` | string | ISO 8601 UTC. |\n| `items[].cashierName` | string? | Casierul. |\n| `items[].total`, `vat`, `discount`, `change` | number | Totalul cu TVA, TVA-ul, reducerea, restul dat. **Negative pe retur.** |\n| `items[].currency` | string | Moneda (`MDL`). |\n| `items[].isRefund`, `isFiscal` | boolean | Retur; bon fiscal. |\n| `items[].lines[]` | array | `productCode`, `productId`, `posProductId`, `productName`, `unit`, `quantity` (negativă pe retur), `unitPrice`, `total`, `discountAmount`, `vatCode` (litera de pe bon), `vatAmount`, `vatPercent`. |\n| `items[].payments[]` | array | `code` (codul ACPS de pe bon: `\"1\"` numerar, `\"2\"` card…), `name`, `amount` (la numerar: suma dată de client), `cardMask`, `reference` (RRN), `voidedAmount`, `refundedAmount`, `reversedAt`. |\n| `totalCount`, `page`, `pageSize` | integer | Paginarea. |\n| `terminalId` | string (uuid) | Casa Z-ului. |\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 404 | — | Z-ul nu există, e al altui comerciant sau casa lui nu e permisă cheii. Corp gol. |\n| 400 | — | `id` nu e GUID, sau `page`/`pageSize` în afara limitelor. |\n| 401 | — | Cheia lipsește, e greșită, revocată sau expirată. Corp gol. |\n| 403 | — | Cheii îi lipsește scope-ul cerut sau modulul „API Integrare” nu e activ. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n| 503 | `SERVICE_UNAVAILABLE` | Un serviciu intern nu răspunde. Reîncearcă peste câteva secunde. |\n\n### De reținut\n\n- Codurile de plată de aici sunt cele ACPS de pe bon, **alt vocabular** decât `tenders[].code` din „Raport Z”."
          },
          "response": [
            {
              "name": "200",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/z-reports/:id/receipts?page=1&pageSize=100",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "z-reports",
                    ":id",
                    "receipts"
                  ],
                  "query": [
                    {
                      "key": "page",
                      "value": "1",
                      "description": "De la 1."
                    },
                    {
                      "key": "pageSize",
                      "value": "100",
                      "description": "1–500, implicit 100."
                    }
                  ],
                  "variable": [
                    {
                      "key": "id",
                      "value": "",
                      "description": "id din „Rapoarte Z”"
                    }
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"items\": [\n    {\n      \"id\": \"c7d8e9f0-1a2b-4c3d-8e4f-5a6b7c8d9e0f\",\n      \"transactionNumber\": 1051,\n      \"receiptNumber\": 41,\n      \"fiscalReceiptNumber\": null,\n      \"dateTime\": \"2026-09-24T07:12:05Z\",\n      \"cashierName\": \"Ana Rusu\",\n      \"total\": 76.7,\n      \"vat\": 5.68,\n      \"discount\": 0,\n      \"change\": 23.3,\n      \"currency\": \"MDL\",\n      \"isRefund\": false,\n      \"isFiscal\": true,\n      \"sfsFiscalCode\": \"7F3A9C21\",\n      \"sfsReceiptUrl\": \"https://mev.sfs.md/receipt-verifier/7F3A9C21\",\n      \"lines\": [\n        {\n          \"productCode\": \"4840167001234\",\n          \"productId\": \"a1b2c3d4-0000-4000-8000-000000000001\",\n          \"posProductId\": \"b1c2d3e4-0000-4000-8000-000000000001\",\n          \"productName\": \"Lapte Fabrikant 2,5% 1L\",\n          \"unit\": \"buc\",\n          \"quantity\": 3,\n          \"unitPrice\": 19.9,\n          \"total\": 59.7,\n          \"discountAmount\": 0,\n          \"vatCode\": \"B\",\n          \"vatAmount\": 4.42,\n          \"vatPercent\": 8\n        },\n        {\n          \"productCode\": \"4840001000057\",\n          \"productId\": \"a1b2c3d4-0000-4000-8000-000000000002\",\n          \"posProductId\": \"b1c2d3e4-0000-4000-8000-000000000002\",\n          \"productName\": \"Pâine albă 500 g\",\n          \"unit\": \"buc\",\n          \"quantity\": 2,\n          \"unitPrice\": 8.5,\n          \"total\": 17,\n          \"discountAmount\": 0,\n          \"vatCode\": \"B\",\n          \"vatAmount\": 1.26,\n          \"vatPercent\": 8\n        }\n      ],\n      \"payments\": [\n        {\n          \"code\": \"1\",\n          \"name\": \"NUMERAR\",\n          \"amount\": 100,\n          \"cardMask\": null,\n          \"reference\": null,\n          \"voidedAmount\": 0,\n          \"refundedAmount\": 0,\n          \"reversedAt\": null\n        }\n      ]\n    }\n  ],\n  \"totalCount\": 1,\n  \"page\": 1,\n  \"pageSize\": 100,\n  \"terminalId\": \"5b2d6f3e-8c1a-4f7e-9a2b-3c4d5e6f7a81\"\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "6. Webhook-uri",
      "description": "Evenimentele trimise la adresa ta: produse, stoc, comenzi, facturi recunoscute. Contractul livrării e în descrierea colecției.",
      "item": [
        {
          "name": "Creează webhook",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/webhooks",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "webhooks"
              ]
            },
            "description": "**Scope necesar:** `webhooks:manage`\n\nAbonează o adresă a ta la evenimente. Răspunsul conține secretul de semnare — **singura dată** când îl vezi.\n\n### Corpul cererii\n\n| Câmp | Tip | Obligatoriu | Reguli |\n|---|---|---|---|\n| `url` | string | da | Adresă `https`, max 2048 caractere. O adresă IP privată e refuzată. |\n| `eventTypes[]` | string[] | da | Cel puțin un eveniment din lista din descrierea colecției. |\n\n### Răspuns 200\n\n| Câmp | Tip | Descriere |\n|---|---|---|\n| `id` | string (uuid) | Id-ul abonamentului. |\n| `url` | string | Adresa. |\n| `eventTypes[]` | string[] | Evenimentele, cu litere mici, fără dubluri. |\n| `signingSecret` | string | `whsec_…` — cheia cu care verifici semnătura. Nu se mai poate citi și nu se poate schimba: pentru un secret nou, șterge abonamentul și creează altul. |\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 400 | `VALIDATION` | „Adresa webhook-ului trebuie să folosească https.”, „Alegeți cel puțin un tip de eveniment.”, „Tip de eveniment necunoscut. Tipurile permise: ….” — codul fiecăruia în `errorCodes`, pe câmp. |\n| 401 | — | Cheia lipsește, e greșită, revocată sau expirată. Corp gol. |\n| 403 | — | Cheii îi lipsește scope-ul cerut sau modulul „API Integrare” nu e activ. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n| 503 | `SERVICE_UNAVAILABLE` | Un serviciu intern nu răspunde. Reîncearcă peste câteva secunde. |\n\n### De reținut\n\n- Răspunsul e `200`, nu `201`.\n- Aceeași adresă înregistrată de două ori primește fiecare eveniment de două ori.\n- Numele de domeniu se verifică la fiecare trimitere, nu la înregistrare: un domeniu care duce la o adresă privată se acceptă aici, dar livrările eșuează.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"url\": \"https://erp.magazin.md/hooks/posfix\",\n  \"eventTypes\": [\n    \"order.created\",\n    \"order.paid\",\n    \"inventory.updated\"\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [
            {
              "name": "200",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/webhooks",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "webhooks"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"url\": \"https://erp.magazin.md/hooks/posfix\",\n  \"eventTypes\": [\n    \"order.created\",\n    \"order.paid\",\n    \"inventory.updated\"\n  ]\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"id\": \"e3b5c1d2-7a8f-4c6e-9d0b-1a2b3c4d5e6f\",\n  \"url\": \"https://erp.magazin.md/hooks/posfix\",\n  \"eventTypes\": [\n    \"order.created\",\n    \"order.paid\",\n    \"inventory.updated\"\n  ],\n  \"signingSecret\": \"whsec_Q2x4bE9mY2J3aVZ5dGNWa0Q2ZUx0ZlQ5c2h1Tk1jUgA\"\n}"
            },
            {
              "name": "400 — adresă http",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/webhooks",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "webhooks"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"url\": \"https://erp.magazin.md/hooks/posfix\",\n  \"eventTypes\": [\n    \"order.created\",\n    \"order.paid\",\n    \"inventory.updated\"\n  ]\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "Bad Request",
              "code": 400,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json; charset=utf-8"
                }
              ],
              "body": "{\n  \"type\": \"https://httpstatuses.io/400\",\n  \"title\": \"VALIDATION\",\n  \"status\": 400,\n  \"detail\": \"Adresa webhook-ului trebuie să folosească https.\",\n  \"errorCode\": \"VALIDATION\",\n  \"errors\": {\n    \"url\": [\n      \"Adresa webhook-ului trebuie să folosească https.\"\n    ]\n  },\n  \"errorCodes\": {\n    \"url\": [\n      {\n        \"code\": \"INTEGRATIONS_WEBHOOK_URL_HTTPS\",\n        \"params\": {}\n      }\n    ]\n  }\n}"
            }
          ]
        },
        {
          "name": "Listă webhook-uri",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/webhooks",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "webhooks"
              ]
            },
            "description": "**Scope necesar:** `webhooks:manage`\n\nAbonamentele active, cele mai noi întâi. Secretul nu apare.\n\n### Răspuns 200 — listă\n\n| Câmp | Tip | Descriere |\n|---|---|---|\n| `[].id` | string (uuid) | Id-ul abonamentului. |\n| `[].url` | string | Adresa. |\n| `[].eventTypes[]` | string[] | Evenimentele. |\n| `[].isActive` | boolean | Mereu `true`: un abonament se oprește doar prin ștergere. |\n| `[].createdAt` | string | ISO 8601 UTC. |\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 401 | — | Cheia lipsește, e greșită, revocată sau expirată. Corp gol. |\n| 403 | — | Cheii îi lipsește scope-ul cerut sau modulul „API Integrare” nu e activ. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n| 503 | `SERVICE_UNAVAILABLE` | Un serviciu intern nu răspunde. Reîncearcă peste câteva secunde. |\n\n### De reținut\n\n- Răspunsul e direct o listă JSON, fără paginare."
          },
          "response": [
            {
              "name": "200",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/webhooks",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "webhooks"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "[\n  {\n    \"id\": \"e3b5c1d2-7a8f-4c6e-9d0b-1a2b3c4d5e6f\",\n    \"url\": \"https://erp.magazin.md/hooks/posfix\",\n    \"eventTypes\": [\n      \"order.created\",\n      \"order.paid\",\n      \"inventory.updated\"\n    ],\n    \"isActive\": true,\n    \"createdAt\": \"2026-09-25T08:02:11.482913Z\"\n  }\n]"
            }
          ]
        },
        {
          "name": "Șterge webhook",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/webhooks/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "webhooks",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "",
                  "description": "id din „Creează webhook”"
                }
              ]
            },
            "description": "**Scope necesar:** `webhooks:manage`\n\nOprește un abonament. Livrările din coadă nu mai pleacă.\n\n### Parametri de cale\n\n| Parametru | Tip | Descriere |\n|---|---|---|\n| `id` | string (uuid) | Id-ul abonamentului. |\n\n### Răspuns\n\n**204** fără corp.\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 404 | `INTEGRATIONS_RESOURCE_NOT_FOUND` | Id necunoscut, al altui comerciant sau **deja șters** (format simplu). |\n| 400 | — | `id` nu e GUID. |\n| 401 | — | Cheia lipsește, e greșită, revocată sau expirată. Corp gol. |\n| 403 | — | Cheii îi lipsește scope-ul cerut sau modulul „API Integrare” nu e activ. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n| 503 | `SERVICE_UNAVAILABLE` | Un serviciu intern nu răspunde. Reîncearcă peste câteva secunde. |\n\n### De reținut\n\n- A doua ștergere a aceluiași id dă `404`, nu `204`."
          },
          "response": [
            {
              "name": "204 — șters",
              "originalRequest": {
                "method": "DELETE",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/webhooks/:id",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "webhooks",
                    ":id"
                  ],
                  "variable": [
                    {
                      "key": "id",
                      "value": "",
                      "description": "id din „Creează webhook”"
                    }
                  ]
                }
              },
              "status": "No Content",
              "code": 204,
              "_postman_previewlanguage": "text",
              "header": [],
              "body": ""
            },
            {
              "name": "404 — necunoscut sau deja șters",
              "originalRequest": {
                "method": "DELETE",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/webhooks/:id",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "webhooks",
                    ":id"
                  ],
                  "variable": [
                    {
                      "key": "id",
                      "value": "",
                      "description": "id din „Creează webhook”"
                    }
                  ]
                }
              },
              "status": "Not Found",
              "code": 404,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"type\": \"https://httpstatuses.io/404\",\n  \"title\": \"Not Found\",\n  \"status\": 404,\n  \"detail\": \"Resursa cerută nu există sau nu aparține acestui comerciant.\",\n  \"errorCode\": \"INTEGRATIONS_RESOURCE_NOT_FOUND\"\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "7. Recunoaștere facturi",
      "description": "Facturi PDF sau fotografiate → date structurate, potrivite pe nomenclatorul tău. Pașii: (1) încarci nomenclatorul (o dată, apoi doar schimbările); (2) trimiți fișierul și primești `202` cu id; (3) aștepți webhook-ul `docai.recognition.completed` sau întrebi pe id din 10 în 10 secunde; (4) citești factura și o imporți; (5) confirmi cu `ack`. Serviciul e contra cost: fiecare fișier nou consumă din pachetul comerciantului.",
      "item": [
        {
          "name": "Nomenclator — încărcare / actualizare",
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/docai/catalog/items",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "docai",
                "catalog",
                "items"
              ]
            },
            "description": "**Scope necesar:** `docai:catalog`\n\nNomenclatorul **programului tău** (1C, ERP), pe care se potrivesc liniile facturilor recunoscute. Codul tău (`externalId`) e cheia: o a doua trimitere actualizează poziția, nu o dublează. Trimite-l în bucăți de cel mult 1000 de poziții. Nimic nu se șterge — o poziție scoasă din uz se trimite cu `isActive: false`.\n\n### Corpul cererii\n\n| Câmp | Tip | Obligatoriu | Reguli |\n|---|---|---|---|\n| `items[]` | array | da | 1–1000 de poziții; un cod o singură dată pe cerere. |\n| `items[].externalId` | string | da | Codul poziției la tine, cel mult 100 de caractere. |\n| `items[].name` | string | da, pe cele active | Cel mult 500 de caractere. |\n| `items[].unit` | string | nu | Unitatea ta de măsură, cel mult 50; nu se convertește. |\n| `items[].barcodes` | string[] | nu | Coduri de bare. Se păstrează doar cele de 8–14 cifre; restul se numără în `ignoredBarcodes`. |\n| `items[].isActive` | boolean | nu | Implicit `true`. `false` = retrasă (nu se mai propune). |\n\n### Răspuns 200\n\n| Câmp | Tip | Descriere |\n|---|---|---|\n| `created`, `updated`, `unchanged`, `deactivated` | integer | Ce s-a întâmplat cu pozițiile trimise. |\n| `ignoredBarcodes` | integer | Coduri de bare lăsate deoparte (nu sunt 8–14 cifre). |\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 400 | `DOCAI_CATALOG_API_TOO_MANY_ITEMS`, `DOCAI_CATALOG_API_DUPLICATE_CODE`, validare pe câmp | „O cerere poate avea cel mult 1.000 de poziții, iar aceasta are 1.200. Trimiteți nomenclatorul în mai multe cereri.” |\n| 401 | — | Cheia lipsește, e greșită, revocată sau expirată. Corp gol. |\n| 403 | — | Cheii îi lipsește scope-ul cerut sau modulul „API Integrare” nu e activ. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n| 503 | `SERVICE_UNAVAILABLE` | Un serviciu intern nu răspunde. Reîncearcă peste câteva secunde. |\n\n### De reținut\n\n- Odată ce programul tău ține nomenclatorul prin API, încărcarea lui din Excel în panou se refuză — altfel sincronizarea următoare ar rescrie ce a încărcat omul.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"items\": [\n    {\n      \"externalId\": \"LAC-0012\",\n      \"name\": \"Lapte Lactis 2,5% 1l\",\n      \"unit\": \"buc\",\n      \"barcodes\": [\n        \"4840811000181\"\n      ]\n    },\n    {\n      \"externalId\": \"LAC-0099\",\n      \"isActive\": false\n    }\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [
            {
              "name": "200",
              "originalRequest": {
                "method": "PUT",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/docai/catalog/items",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "docai",
                    "catalog",
                    "items"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"items\": [\n    {\n      \"externalId\": \"LAC-0012\",\n      \"name\": \"Lapte Lactis 2,5% 1l\",\n      \"unit\": \"buc\",\n      \"barcodes\": [\n        \"4840811000181\"\n      ]\n    },\n    {\n      \"externalId\": \"LAC-0099\",\n      \"isActive\": false\n    }\n  ]\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"created\": 1,\n  \"updated\": 0,\n  \"unchanged\": 0,\n  \"deactivated\": 1,\n  \"ignoredBarcodes\": 0\n}"
            }
          ]
        },
        {
          "name": "Nomenclator — listă",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/docai/catalog/items?includeInactive=false&page=1&pageSize=100",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "docai",
                "catalog",
                "items"
              ],
              "query": [
                {
                  "key": "search",
                  "value": "",
                  "description": "Text din cod sau denumire; un cod de bare întreg se caută exact.",
                  "disabled": true
                },
                {
                  "key": "includeInactive",
                  "value": "false",
                  "description": "Și pozițiile retrase."
                },
                {
                  "key": "page",
                  "value": "1",
                  "description": "De la 1."
                },
                {
                  "key": "pageSize",
                  "value": "100",
                  "description": "1–200, implicit 100."
                }
              ]
            },
            "description": "**Scope necesar:** `docai:catalog`\n\nCe a primit PosFix din nomenclatorul tău, în ordine alfabetică — pentru verificarea sincronizării.\n\n### Răspuns 200\n\n| Câmp | Tip | Descriere |\n|---|---|---|\n| `items[].externalId`, `name`, `unit`, `barcodes` |  | Cum le-ai trimis. |\n| `items[].isActive` | boolean | Poziția se propune la potrivire. |\n| `items[].usageCount` | integer | Pe câte linii a fost aleasă de oameni la verificare. |\n| `items[].updatedAt` | string | Ultima schimbare, ISO 8601 UTC. |\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 401 | — | Cheia lipsește, e greșită, revocată sau expirată. Corp gol. |\n| 403 | — | Cheii îi lipsește scope-ul cerut sau modulul „API Integrare” nu e activ. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n| 503 | `SERVICE_UNAVAILABLE` | Un serviciu intern nu răspunde. Reîncearcă peste câteva secunde. |"
          },
          "response": [
            {
              "name": "200",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/docai/catalog/items?includeInactive=false&page=1&pageSize=100",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "docai",
                    "catalog",
                    "items"
                  ],
                  "query": [
                    {
                      "key": "search",
                      "value": "",
                      "description": "Text din cod sau denumire; un cod de bare întreg se caută exact.",
                      "disabled": true
                    },
                    {
                      "key": "includeInactive",
                      "value": "false",
                      "description": "Și pozițiile retrase."
                    },
                    {
                      "key": "page",
                      "value": "1",
                      "description": "De la 1."
                    },
                    {
                      "key": "pageSize",
                      "value": "100",
                      "description": "1–200, implicit 100."
                    }
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"items\": [\n    {\n      \"externalId\": \"LAC-0012\",\n      \"name\": \"Lapte Lactis 2,5% 1l\",\n      \"unit\": \"buc\",\n      \"barcodes\": [\n        \"4840811000181\"\n      ],\n      \"isActive\": true,\n      \"usageCount\": 7,\n      \"updatedAt\": \"2026-09-27T16:02:11Z\"\n    }\n  ],\n  \"totalCount\": 1,\n  \"page\": 1,\n  \"pageSize\": 50,\n  \"totalPages\": 1,\n  \"hasNextPage\": false,\n  \"hasPreviousPage\": false,\n  \"summary\": null,\n  \"filterableColumns\": null,\n  \"ignoredFilters\": null\n}"
            }
          ]
        },
        {
          "name": "Nomenclator — retragere",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/docai/catalog/items/:externalId",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "docai",
                "catalog",
                "items",
                ":externalId"
              ],
              "variable": [
                {
                  "key": "externalId",
                  "value": "LAC-0099",
                  "description": "Codul poziției la tine (URL-encoded)."
                }
              ]
            },
            "description": "**Scope necesar:** `docai:catalog`\n\nRetrage o poziție (echivalent cu `isActive: false`). Nu o șterge: facturile potrivite deja pe ea rămân legate de codul ei. Întoarce poziția.\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 404 | — | Codul nu e în nomenclatorul trimis. Corp gol. |\n| 401 | — | Cheia lipsește, e greșită, revocată sau expirată. Corp gol. |\n| 403 | — | Cheii îi lipsește scope-ul cerut sau modulul „API Integrare” nu e activ. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n| 503 | `SERVICE_UNAVAILABLE` | Un serviciu intern nu răspunde. Reîncearcă peste câteva secunde. |"
          },
          "response": [
            {
              "name": "200",
              "originalRequest": {
                "method": "DELETE",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/docai/catalog/items/:externalId",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "docai",
                    "catalog",
                    "items",
                    ":externalId"
                  ],
                  "variable": [
                    {
                      "key": "externalId",
                      "value": "LAC-0099",
                      "description": "Codul poziției la tine (URL-encoded)."
                    }
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"externalId\": \"LAC-0099\",\n  \"name\": \"Chefir 1l\",\n  \"unit\": \"buc\",\n  \"barcodes\": [],\n  \"isActive\": false,\n  \"usageCount\": 0,\n  \"updatedAt\": \"2026-09-28T08:00:00Z\"\n}"
            }
          ]
        },
        {
          "name": "Trimite o factură la recunoaștere",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/docai/recognitions",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "docai",
                "recognitions"
              ]
            },
            "description": "**Scope necesar:** `docai:recognize`\n\nPune fișierul în coada de recunoaștere și răspunde imediat, **fără** să aștepte citirea (durează de obicei 20–90 de secunde). Rezultatul îl primești ca webhook `docai.recognition.completed` / `docai.recognition.failed`, sau îl citești pe id.\n\n### Corpul cererii\n\n| Câmp | Tip | Obligatoriu | Reguli |\n|---|---|---|---|\n| `file` | fișier (multipart) | da | PDF cel mult 12 MB; fotografie cel mult 20 MB. Tipul se ia din antetul părții; la `application/octet-stream` se citește din conținut. |\n| `documentKind` | string | nu | `supplier_invoice` (factura primită de la furnizor, implicit) sau `sales_invoice`. |\n| `externalRef` | string | nu | Cheia ta (ex. numărul înregistrării la tine). A doua trimitere cu aceeași cheie întoarce recunoașterea existentă, fără alt cost. |\n\n### Răspuns\n\n**202** — fișierul a intrat în coadă. **200** — recunoașterea exista deja: aceeași `externalRef`, același fișier încă în lucru (`existing: true`), sau un fișier deja recunoscut, copiat fără cost (`duplicate: true`, `status: completed`).\n\n| Câmp | Tip | Descriere |\n|---|---|---|\n| `id` | string (uuid) | Id-ul recunoașterii. |\n| `status` | string | `queued` \\| `processing` \\| `completed` \\| `failed`. |\n| `externalRef` | string? | Cheia ta. |\n| `existing` | boolean | Nimic nou nu s-a pus în coadă. |\n| `duplicate` | boolean | Rezultatul e copiat de la același fișier recunoscut deja. |\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 400 | `DOCAI_FILE_REQUIRED`, `DOCAI_FILE_TYPE_NOT_SUPPORTED`, `DOCAI_FILE_TOO_LARGE`, `DOCAI_PDF_TOO_LARGE`, `DOCAI_DOCUMENT_KIND_UNKNOWN` | „Tipul de fișier „application/zip” nu este acceptat. Se acceptă JPEG, PNG, WebP, GIF și PDF.” |\n| 402 | `DOCAI_FREE_TRIAL_USED`, `DOCAI_MONTHLY_LIMIT_REACHED`, `DOCAI_BATCH_EXCEEDS_MONTHLY_LIMIT`, `DOCAI_LIMIT_DENIED` | Pachetul de recunoașteri s-a terminat. Format simplu, cu `title: \"Payment Required\"`. |\n| 422 | `DOCAI_EXTERNAL_REF_REUSED` | „Cheia externă „INV-2026-000812” a fost folosită deja pentru alt document. Folosiți o cheie nouă pentru fiecare document.” |\n| 422 | `DOCAI_EXTERNAL_REF_FILE_IN_FLIGHT` | Același fișier se citește chiar acum sub altă recunoaștere. Retrimite-l cu aceeași cheie după ce aceea se termină: primești o copie gratuită, sub cheia ta. |\n| 422 | `DOCAI_DISABLED`, `DOCAI_API_KEY_MISSING` | Recunoașterea e oprită pe platformă. Reîncearcă mai târziu. |\n| 503 | `SERVICE_UNAVAILABLE` | Prea multe trimiteri simultane sau serviciul nu răspunde. Retrimite cu aceeași `externalRef` — nu se plătește de două ori. |\n| 401 | — | Cheia lipsește, e greșită, revocată sau expirată. Corp gol. |\n| 403 | — | Cheii îi lipsește scope-ul cerut sau modulul „API Integrare” nu e activ. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n| 503 | `SERVICE_UNAVAILABLE` | Un serviciu intern nu răspunde. Reîncearcă peste câteva secunde. |\n\n### De reținut\n\n- Fiecare fișier **nou** consumă o recunoaștere din pachetul comerciantului (primele 3 sunt gratuite). Același fișier trimis din nou nu se mai plătește, dar fără `externalRef` devine o recunoaștere nouă (copie), cu id și webhook proprii — trimite mereu o cheie.\n- Trimiterea **nu se reia automat** la noi: la un timeout sau `503`, retrimite tu, cu aceeași `externalRef`.\n- Cel mult două trimiteri simultane pe cheie; restul așteaptă la rând. Nu trimite zeci de fișiere în paralel — coada le citește oricum câte două.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "file",
                  "type": "file",
                  "src": [],
                  "description": "PDF (≤ 12 MB) sau fotografie JPEG/PNG/WebP/GIF (≤ 20 MB)."
                },
                {
                  "key": "documentKind",
                  "type": "text",
                  "value": "supplier_invoice",
                  "description": "supplier_invoice (implicit) | sales_invoice"
                },
                {
                  "key": "externalRef",
                  "type": "text",
                  "value": "INV-2026-000812",
                  "description": "Cheia ta de idempotență, ≤ 100 de caractere."
                }
              ]
            }
          },
          "response": [
            {
              "name": "202",
              "originalRequest": {
                "method": "POST",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/docai/recognitions",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "docai",
                    "recognitions"
                  ]
                },
                "body": {
                  "mode": "formdata",
                  "formdata": [
                    {
                      "key": "file",
                      "type": "file",
                      "src": [],
                      "description": "PDF (≤ 12 MB) sau fotografie JPEG/PNG/WebP/GIF (≤ 20 MB)."
                    },
                    {
                      "key": "documentKind",
                      "type": "text",
                      "value": "supplier_invoice",
                      "description": "supplier_invoice (implicit) | sales_invoice"
                    },
                    {
                      "key": "externalRef",
                      "type": "text",
                      "value": "INV-2026-000812",
                      "description": "Cheia ta de idempotență, ≤ 100 de caractere."
                    }
                  ]
                }
              },
              "status": "Accepted",
              "code": 202,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"id\": \"6a1f3c9e-2b4d-4e8f-a1c3-5d7e9f0b2a46\",\n  \"status\": \"queued\",\n  \"externalRef\": \"INV-2026-000812\",\n  \"existing\": false,\n  \"duplicate\": false\n}"
            },
            {
              "name": "200 existentă",
              "originalRequest": {
                "method": "POST",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/docai/recognitions",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "docai",
                    "recognitions"
                  ]
                },
                "body": {
                  "mode": "formdata",
                  "formdata": [
                    {
                      "key": "file",
                      "type": "file",
                      "src": [],
                      "description": "PDF (≤ 12 MB) sau fotografie JPEG/PNG/WebP/GIF (≤ 20 MB)."
                    },
                    {
                      "key": "documentKind",
                      "type": "text",
                      "value": "supplier_invoice",
                      "description": "supplier_invoice (implicit) | sales_invoice"
                    },
                    {
                      "key": "externalRef",
                      "type": "text",
                      "value": "INV-2026-000812",
                      "description": "Cheia ta de idempotență, ≤ 100 de caractere."
                    }
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"id\": \"6a1f3c9e-2b4d-4e8f-a1c3-5d7e9f0b2a46\",\n  \"status\": \"completed\",\n  \"externalRef\": \"INV-2026-000812\",\n  \"existing\": true,\n  \"duplicate\": false\n}"
            },
            {
              "name": "402",
              "originalRequest": {
                "method": "POST",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/docai/recognitions",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "docai",
                    "recognitions"
                  ]
                },
                "body": {
                  "mode": "formdata",
                  "formdata": [
                    {
                      "key": "file",
                      "type": "file",
                      "src": [],
                      "description": "PDF (≤ 12 MB) sau fotografie JPEG/PNG/WebP/GIF (≤ 20 MB)."
                    },
                    {
                      "key": "documentKind",
                      "type": "text",
                      "value": "supplier_invoice",
                      "description": "supplier_invoice (implicit) | sales_invoice"
                    },
                    {
                      "key": "externalRef",
                      "type": "text",
                      "value": "INV-2026-000812",
                      "description": "Cheia ta de idempotență, ≤ 100 de caractere."
                    }
                  ]
                }
              },
              "status": "Payment Required",
              "code": 402,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"type\": \"https://httpstatuses.io/402\",\n  \"title\": \"Payment Required\",\n  \"status\": 402,\n  \"detail\": \"Ați atins limita lunară de recunoașteri (100/100). Alegeți un pachet mai mare.\",\n  \"errorCode\": \"DOCAI_MONTHLY_LIMIT_REACHED\",\n  \"params\": {\n    \"used\": 100,\n    \"limit\": 100\n  }\n}"
            },
            {
              "name": "400 tip",
              "originalRequest": {
                "method": "POST",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/docai/recognitions",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "docai",
                    "recognitions"
                  ]
                },
                "body": {
                  "mode": "formdata",
                  "formdata": [
                    {
                      "key": "file",
                      "type": "file",
                      "src": [],
                      "description": "PDF (≤ 12 MB) sau fotografie JPEG/PNG/WebP/GIF (≤ 20 MB)."
                    },
                    {
                      "key": "documentKind",
                      "type": "text",
                      "value": "supplier_invoice",
                      "description": "supplier_invoice (implicit) | sales_invoice"
                    },
                    {
                      "key": "externalRef",
                      "type": "text",
                      "value": "INV-2026-000812",
                      "description": "Cheia ta de idempotență, ≤ 100 de caractere."
                    }
                  ]
                }
              },
              "status": "Bad Request",
              "code": 400,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"type\": \"https://httpstatuses.io/400\",\n  \"title\": \"DOCAI_FILE_TYPE_NOT_SUPPORTED\",\n  \"status\": 400,\n  \"detail\": \"Tipul de fișier „application/zip” nu este acceptat. Se acceptă JPEG, PNG, WebP, GIF și PDF.\",\n  \"errorCode\": \"DOCAI_FILE_TYPE_NOT_SUPPORTED\"\n}"
            }
          ]
        },
        {
          "name": "Recunoașteri — listă",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/docai/recognitions?status=completed&acknowledged=false&page=1&pageSize=50",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "docai",
                "recognitions"
              ],
              "query": [
                {
                  "key": "status",
                  "value": "completed",
                  "description": "queued | processing | completed | failed"
                },
                {
                  "key": "acknowledged",
                  "value": "false",
                  "description": "false = doar cele încă nepreluate"
                },
                {
                  "key": "source",
                  "value": "",
                  "description": "api | control_panel",
                  "disabled": true
                },
                {
                  "key": "reviewed",
                  "value": "",
                  "description": "true = doar cele verificate de un om în panou",
                  "disabled": true
                },
                {
                  "key": "createdFrom",
                  "value": "",
                  "description": "ISO 8601, inclusiv",
                  "disabled": true
                },
                {
                  "key": "createdTo",
                  "value": "",
                  "description": "ISO 8601, exclusiv",
                  "disabled": true
                },
                {
                  "key": "externalRef",
                  "value": "",
                  "description": "Cheia ta",
                  "disabled": true
                },
                {
                  "key": "page",
                  "value": "1",
                  "description": "De la 1."
                },
                {
                  "key": "pageSize",
                  "value": "50",
                  "description": "1–200, implicit 50."
                }
              ]
            },
            "description": "**Scope necesar:** `docai:read`\n\nRecunoașterile comerciantului, **din orice sursă** — și cele trimise de tine, și cele făcute de oameni în panou —, cea mai veche întâi. Preluarea obișnuită: `status=completed&acknowledged=false`, detaliul fiecăreia pe id, apoi `POST …/ack` după ce ai importat-o.\n\n### Răspuns 200\n\n| Câmp | Tip | Descriere |\n|---|---|---|\n| `items[].id`, `status`, `source`, `externalRef`, `fileName` |  | Ca la detaliu. |\n| `items[].reviewed` | boolean | Un om a verificat-o în panou. |\n| `items[].acknowledgedAt` | string? | Când ai confirmat preluarea. |\n| `items[].supplierName`, `supplierIdno`, `documentSeries`, `documentNumber`, `issueDate`, `total`, `currency`, `lineCount` |  | Rezumatul facturii, pentru afișare; datele de import sunt în detaliu. |\n| `totalCount`, `page`, `pageSize`, `totalPages`, `hasNextPage` |  | Paginarea. |\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 400 | `COMMON_INVALID_REQUEST_FIELD`, `COMMON_BETWEEN` | Valoare de filtru necunoscută, pagină în afara intervalului. |\n| 401 | — | Cheia lipsește, e greșită, revocată sau expirată. Corp gol. |\n| 403 | — | Cheii îi lipsește scope-ul cerut sau modulul „API Integrare” nu e activ. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n| 503 | `SERVICE_UNAVAILABLE` | Un serviciu intern nu răspunde. Reîncearcă peste câteva secunde. |\n\n### De reținut\n\n- Ca inbox (`acknowledged=false`), citește **mereu pagina 1**: după fiecare `ack` lista se scurtează, iar o recunoaștere terminată mai târziu poate avea o dată de creare mai veche decât cele deja citite. Cu `page++` ai sări peste facturi."
          },
          "response": [
            {
              "name": "200",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/docai/recognitions?status=completed&acknowledged=false&page=1&pageSize=50",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "docai",
                    "recognitions"
                  ],
                  "query": [
                    {
                      "key": "status",
                      "value": "completed",
                      "description": "queued | processing | completed | failed"
                    },
                    {
                      "key": "acknowledged",
                      "value": "false",
                      "description": "false = doar cele încă nepreluate"
                    },
                    {
                      "key": "source",
                      "value": "",
                      "description": "api | control_panel",
                      "disabled": true
                    },
                    {
                      "key": "reviewed",
                      "value": "",
                      "description": "true = doar cele verificate de un om în panou",
                      "disabled": true
                    },
                    {
                      "key": "createdFrom",
                      "value": "",
                      "description": "ISO 8601, inclusiv",
                      "disabled": true
                    },
                    {
                      "key": "createdTo",
                      "value": "",
                      "description": "ISO 8601, exclusiv",
                      "disabled": true
                    },
                    {
                      "key": "externalRef",
                      "value": "",
                      "description": "Cheia ta",
                      "disabled": true
                    },
                    {
                      "key": "page",
                      "value": "1",
                      "description": "De la 1."
                    },
                    {
                      "key": "pageSize",
                      "value": "50",
                      "description": "1–200, implicit 50."
                    }
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"items\": [\n    {\n      \"id\": \"6a1f3c9e-2b4d-4e8f-a1c3-5d7e9f0b2a46\",\n      \"status\": \"completed\",\n      \"source\": \"api\",\n      \"externalRef\": \"INV-2026-000812\",\n      \"fileName\": \"lactis-1791127.pdf\",\n      \"documentKind\": \"supplier_invoice\",\n      \"createdAt\": \"2026-09-28T09:14:02Z\",\n      \"completedAt\": \"2026-09-28T09:14:41Z\",\n      \"reviewed\": false,\n      \"acknowledgedAt\": null,\n      \"supplierName\": \"Lactis SRL\",\n      \"supplierIdno\": \"1003600012345\",\n      \"documentSeries\": \"AAY\",\n      \"documentNumber\": \"1791127\",\n      \"issueDate\": \"2026-09-21\",\n      \"total\": 410.31,\n      \"currency\": \"MDL\",\n      \"lineCount\": 1\n    }\n  ],\n  \"totalCount\": 1,\n  \"page\": 1,\n  \"pageSize\": 50,\n  \"totalPages\": 1,\n  \"hasNextPage\": false,\n  \"hasPreviousPage\": false,\n  \"summary\": null,\n  \"filterableColumns\": null,\n  \"ignoredFilters\": null\n}"
            }
          ]
        },
        {
          "name": "Recunoaștere — detaliu",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/docai/recognitions/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "docai",
                "recognitions",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "6a1f3c9e-2b4d-4e8f-a1c3-5d7e9f0b2a46",
                  "description": "Id-ul recunoașterii."
                }
              ]
            },
            "description": "**Scope necesar:** `docai:read`\n\nStarea și, când e gata, factura citită. Dacă un om a verificat-o în panou (`reviewed: true`), `invoice` e varianta lui, iar pe linii `match` e poziția aleasă de el. Altfel, pe fiecare linie vin `suggestions` — propunerile pe nomenclatorul tău, cea mai sigură întâi.\n\n### Răspuns 200\n\n| Câmp | Tip | Descriere |\n|---|---|---|\n| `status` | string | `queued` \\| `processing` \\| `completed` \\| `failed`. |\n| `error` | object? | Pe `failed`: `code` (stabil) și `message` (text, în română). |\n| `confidence` | number? | 0..1, din verificări aritmetice (linii, totaluri, IDNO), nu din model. Sub 0,8 merită o privire. |\n| `duplicateOf` | string? | Id-ul recunoașterii din care s-a copiat rezultatul. |\n| `invoice` | object? | Doar pe `completed`: antet, `supplier`/`buyer`/`carrier`, `lines[]`, `totals`. |\n| `invoice.lines[].match` | object? | `{type, id, name, unit}`; `type` = `external` (`id` = codul tău) sau `platform`. |\n| `invoice.lines[].suggestions[]` | array | `{externalId, name, unit, score, method, unitMismatch}`; `method`: `learned` (confirmată deja la același furnizor), `barcode`, `name`. |\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 404 | — | Nu există în datele comerciantului. Corp gol. |\n| 401 | — | Cheia lipsește, e greșită, revocată sau expirată. Corp gol. |\n| 403 | — | Cheii îi lipsește scope-ul cerut sau modulul „API Integrare” nu e activ. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n| 503 | `SERVICE_UNAVAILABLE` | Un serviciu intern nu răspunde. Reîncearcă peste câteva secunde. |\n\n### De reținut\n\n- O recunoaștere `failed` cu `DOCAI_RESPONSE_TRUNCATED` e o factură prea lungă pentru bugetul curent; nu importa nimic din ea.\n- Câmpurile pe care modelul nu le-a putut citi vin `null` — nu le înlocui cu zero."
          },
          "response": [
            {
              "name": "200",
              "originalRequest": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/docai/recognitions/:id",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "docai",
                    "recognitions",
                    ":id"
                  ],
                  "variable": [
                    {
                      "key": "id",
                      "value": "6a1f3c9e-2b4d-4e8f-a1c3-5d7e9f0b2a46",
                      "description": "Id-ul recunoașterii."
                    }
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"id\": \"6a1f3c9e-2b4d-4e8f-a1c3-5d7e9f0b2a46\",\n  \"status\": \"completed\",\n  \"source\": \"api\",\n  \"externalRef\": \"INV-2026-000812\",\n  \"fileName\": \"lactis-1791127.pdf\",\n  \"documentKind\": \"supplier_invoice\",\n  \"createdAt\": \"2026-09-28T09:14:02Z\",\n  \"completedAt\": \"2026-09-28T09:14:41Z\",\n  \"reviewed\": false,\n  \"reviewedAt\": null,\n  \"acknowledgedAt\": null,\n  \"confidence\": 0.96,\n  \"duplicateOf\": null,\n  \"error\": null,\n  \"invoice\": {\n    \"documentType\": \"Factură fiscală\",\n    \"series\": \"AAY\",\n    \"number\": \"1791127\",\n    \"issueDate\": \"2026-09-21\",\n    \"deliveryDate\": \"2026-09-21\",\n    \"currency\": \"MDL\",\n    \"waybillNumber\": null,\n    \"supplier\": {\n      \"name\": \"Lactis SRL\",\n      \"idno\": \"1003600012345\",\n      \"vatCode\": \"0401234\",\n      \"address\": \"mun. Chișinău, str. Uzinelor 1\",\n      \"iban\": \"MD24AG000000022512345678\",\n      \"bank\": \"Moldova Agroindbank\",\n      \"bankCode\": \"AGRNMD2X\"\n    },\n    \"buyer\": {\n      \"name\": \"Aroma Corner SRL\",\n      \"idno\": \"1015600098765\",\n      \"vatCode\": null,\n      \"address\": null,\n      \"iban\": null,\n      \"bank\": null,\n      \"bankCode\": null\n    },\n    \"carrier\": null,\n    \"lines\": [\n      {\n        \"index\": 0,\n        \"barcode\": \"4840811000181\",\n        \"tariffCode\": null,\n        \"name\": \"Lapte 2,5% 1L\",\n        \"unit\": \"buc\",\n        \"quantity\": 24,\n        \"unitPriceExclVat\": 15.83,\n        \"amountExclVat\": 379.92,\n        \"vatRate\": 8,\n        \"vatAmount\": 30.39,\n        \"amountInclVat\": 410.31,\n        \"match\": null,\n        \"suggestions\": [\n          {\n            \"externalId\": \"LAC-0012\",\n            \"name\": \"Lapte Lactis 2,5% 1l\",\n            \"unit\": \"buc\",\n            \"score\": 100,\n            \"method\": \"barcode\",\n            \"unitMismatch\": false\n          },\n          {\n            \"externalId\": \"LAC-0013\",\n            \"name\": \"Lapte Lactis 3,2% 1l\",\n            \"unit\": \"buc\",\n            \"score\": 67,\n            \"method\": \"name\",\n            \"unitMismatch\": false\n          }\n        ]\n      }\n    ],\n    \"totals\": {\n      \"amountExclVat\": 379.92,\n      \"vatAmount\": 30.39,\n      \"total\": 410.31\n    },\n    \"notes\": null\n  }\n}"
            }
          ]
        },
        {
          "name": "Recunoaștere — confirmă preluarea",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/public/v1/docai/recognitions/:id/ack",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "v1",
                "docai",
                "recognitions",
                ":id",
                "ack"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "6a1f3c9e-2b4d-4e8f-a1c3-5d7e9f0b2a46",
                  "description": "Id-ul recunoașterii."
                }
              ]
            },
            "description": "**Scope necesar:** `docai:read`\n\nMarchează factura ca preluată de sistemul tău: lista `acknowledged=false` n-o mai arată. Idempotent — a doua confirmare păstrează data primei. Confirmarea e a comerciantului, nu a cheii: două sisteme care preiau aceleași facturi trebuie să se înțeleagă între ele.\n\n### Erori\n\n| HTTP | `errorCode` | Când |\n|---|---|---|\n| 422 | `DOCAI_ACK_NOT_READY` | „Recunoașterea nu s-a terminat încă. Confirmați preluarea după ce ajunge la „completed” sau „failed”.” |\n| 409 | `CONCURRENCY_CONFLICT` | Factura a fost modificată în același moment (verificare în panou). Repetă cererea. |\n| 404 | — | Nu există în datele comerciantului. Corp gol. |\n| 401 | — | Cheia lipsește, e greșită, revocată sau expirată. Corp gol. |\n| 403 | — | Cheii îi lipsește scope-ul cerut sau modulul „API Integrare” nu e activ. Corp gol. |\n| 429 | `RATE_LIMITED` | Prea multe cereri; vezi „Limite” în descrierea colecției. |\n| 503 | `SERVICE_UNAVAILABLE` | Un serviciu intern nu răspunde. Reîncearcă peste câteva secunde. |"
          },
          "response": [
            {
              "name": "200",
              "originalRequest": {
                "method": "POST",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/api/public/v1/docai/recognitions/:id/ack",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "api",
                    "public",
                    "v1",
                    "docai",
                    "recognitions",
                    ":id",
                    "ack"
                  ],
                  "variable": [
                    {
                      "key": "id",
                      "value": "6a1f3c9e-2b4d-4e8f-a1c3-5d7e9f0b2a46",
                      "description": "Id-ul recunoașterii."
                    }
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                }
              ],
              "body": "{\n  \"id\": \"6a1f3c9e-2b4d-4e8f-a1c3-5d7e9f0b2a46\",\n  \"status\": \"completed\",\n  \"source\": \"api\",\n  \"externalRef\": \"INV-2026-000812\",\n  \"fileName\": \"lactis-1791127.pdf\",\n  \"documentKind\": \"supplier_invoice\",\n  \"createdAt\": \"2026-09-28T09:14:02Z\",\n  \"completedAt\": \"2026-09-28T09:14:41Z\",\n  \"reviewed\": false,\n  \"acknowledgedAt\": \"2026-09-28T09:20:00Z\",\n  \"supplierName\": \"Lactis SRL\",\n  \"supplierIdno\": \"1003600012345\",\n  \"documentSeries\": \"AAY\",\n  \"documentNumber\": \"1791127\",\n  \"issueDate\": \"2026-09-21\",\n  \"total\": 410.31,\n  \"currency\": \"MDL\",\n  \"lineCount\": 1\n}"
            }
          ]
        }
      ]
    }
  ]
}
