# Prezentare generală Platforma Ilustromania este o aplicație SaaS cu mai multe firme pe același cont. Fiecare firmă își alege modulele, oamenii și drepturile, iar datele ei rămân izolate de celelalte. ## Ce este platforma Platforma rulează la `app.ilustromania.com` și adună într-un singur loc lucrurile pe care o firmă le face zilnic: meniul și canalele de livrare pentru un restaurant, creatorii și campaniile pentru o agenție, facturile, documentele, echipa și automatizările pentru oricare dintre ele. Firma ta și modulele ei: | Firmă | Tip | Ce folosește în principal | | --- | --- | --- | | Ilustromania | `agentie` | agenție de creatori, Backstage, social media, oferte, Website Builder | Aceeași platformă poate primi oricâte firme noi. O firmă nouă pornește goală, își activează doar modulele de care are nevoie și își invită oamenii. ## Cum este organizată documentația | Secțiune | Pentru cine | Ce găsești | | --- | --- | --- | | [Ghid](#concepte) | oricine folosește aplicația | conceptele de bază, primii pași și fiecare modul pe rând | | [Administrare](#firme-si-module) | administratorul firmei | firme, module, conturi, roluri, securitate, limite | | [Integrări](#mcp) | echipe tehnice | MCP, OAuth, evenimente, canalele de livrare, DLS, FGO, SPV | | [Referință API](#api-prezentare) | dezvoltatori | toate cele 398 de rute, cu metoda, calea și nivelul de acces | | [Resurse](#standarde) | toată lumea | comparația cu standardele din industrie, foaia de parcurs, glosarul | > [!NOTE] > Referința API este generată direct din codul platformei (harta de acces a rutelor), nu scrisă de mână. Dacă o rută apare aici, ea există și are accesul declarat. ## Principiile platformei - **Firma este granița.** Orice rând de date aparține unei firme, iar baza de date refuză singură citirile din altă firmă (Row Level Security). - **Refuz implicit.** O rută care nu își declară accesul nu răspunde deloc: primește `401 ruta_nedeclarata` înainte să ajungă la cod. - **O singură margine.** Datele din afară (cereri, fișiere, servicii externe) intră printr-un singur loc și sunt validate cu scheme înainte de folosire. - **Fără zerouri false.** Când o sursă lipsește, răspunsul este `503` cu motivul, niciodată o listă goală care ar putea fi citită ca „nu există nimic”. - **Fără ștergeri pe nevăzute.** Produsele, codurile de reducere, joburile și facturile emise se dezactivează sau se anulează, nu dispar. ## Următorii pași 1. Citește [Concepte](#concepte) ca să înțelegi firmele, conturile, rolurile și modulele. 2. Urmează [Primii pași](#primii-pasi) pentru intrarea în cont și alegerea firmei. 3. Deschide modulul cu care lucrezi din meniul din stânga. --- # Concepte Patru noțiuni explică aproape tot ce vezi în platformă - firma, contul, rolul și modulul. Odată înțelese, restul documentației se citește ușor. ## Firma Firma este unitatea de bază. Are datele legale (denumire, CUI, Registrul Comerțului, sediu, conturi bancare), un **tip** (`restaurant`, `agentie` sau `gol`), tema vizuală și logo-ul. Toate datele de lucru (produse, facturi, comenzi, oameni) au o coloană `firma_id`. Baza de date aplică regula „vezi doar firma ta” pe fiecare tabel, cu Row Level Security în mod `FORCE`: chiar și o greșeală în cod nu poate întoarce rândurile altei firme. ```sql title="Regula de izolare (schematic)" ALTER TABLE produse ENABLE ROW LEVEL SECURITY; ALTER TABLE produse FORCE ROW LEVEL SECURITY; CREATE POLICY izolare_firma ON produse USING (firma_id = nullif(current_setting('app.firma_id', true), '')::uuid); ``` ## Contul Un cont este un om. Același cont poate avea acces la mai multe firme, cu un rol diferit în fiecare. În antetul aplicației alegi **firma activă**; tot ce citești și scrii după aceea se întâmplă doar în firma aceea. Firma stă în adresa paginii (`/app//…`), deci fiecare tab al browserului poate lucra pe altă firmă; o adresă fără firmă rămâne pe firma din sesiune. Dacă nu mai ai acces la firma din adresă, platforma răspunde `403`. > [!TIP] > Dacă lucrezi pentru două firme (de exemplu restaurantul și agenția), nu ai nevoie de două conturi. Ai un singur cont, cu acces la amândouă. ## Rolul Rolul spune ce poate face un om într-o firmă. Platforma cunoaște opt roluri de firmă: | Rol | Pentru cine | | --- | --- | | `admin` | administratorul firmei: oameni, drepturi, module, setări | | `patron` | proprietarul: vede tot ce ține de afacere | | `finante` | facturi, plăți, evidență financiară | | `gestiune` | stoc, recepții, furnizori | | `bucatar` | bucătăria: meniu, rețete, ingrediente | | `casier` | vânzarea și casa de marcat | | `marketing` | social media, reclame, recenzii | | `angajat` | acces de bază la paginile primite | Separat, `executor` e rolul tehnic al jetoanelor de serviciu ale agenților de pe server; nu se dă unui om. Peste rol se pot adăuga **drepturi fine** (de exemplu dreptul de a edita meniul) și **pagini** individuale, bifate sau scoase pentru fiecare om. Detaliile sunt în [Roluri și drepturi](#roluri-si-drepturi). ## Modulul Un modul este o funcție a platformei pe care firma o activează sau nu: meniul, agenția, facturile, Backstage și așa mai departe. Tabelul `module_firma` ține, pentru fiecare firmă, ce module are active. Un modul inactiv nu apare în meniu. Drepturile pe rute le decide rolul, nu lista de module: azi doar agenția își verifică și modulul la fiecare cerere. Lista completă este în secțiunea **Module** din meniul din stânga. ## Cum se leagă ```text title="Firmă → conturi → roluri → module" Firma (Ilustromania, agentie) ├── module active: agentie, backstage, social, oferte ... └── acces_firma └── Ana → rol: marketing ``` --- # Primii pași Cum intri în cont, cum îți adaugi o cheie de acces (passkey) și cum alegi firma cu care lucrezi. ## Intrarea în cont Platforma acceptă cinci feluri de intrare. Toate duc la aceeași sesiune. | Metodă | Când o folosești | | --- | --- | | Google | ai un cont Google cu adresa ta de lucru | | Email și parolă | ai primit o parolă temporară de la administrator | | Cod pe email | nu îți amintești parola și vrei să intri o singură dată | | Passkey | ai înregistrat amprenta sau Face ID pe telefon ori laptop | | Invitație | ai primit un link de invitație într-o firmă | > [!NOTE] > Parolele care apar în listele publice de parole sparte sunt refuzate la alegere. După mai multe încercări greșite pe un cont real, proprietarul contului primește o alertă. ## Sesiunea După intrare, browserul primește un cookie de sesiune numit `__Host-sesiune`. Este: - legat strict de domeniul aplicației (prefixul `__Host-`, `Path=/`), - trimis doar pe conexiuni securizate (`Secure`), - inaccesibil scripturilor din pagină (`HttpOnly`), - `SameSite=Lax`, ca un link din email să te ducă direct în aplicație; scrierile venite de pe alte site-uri sunt oprite separat de garda CSRF (vezi [Securitate](#securitate)). Fără bifa **Ține-mă conectat**, sesiunea expiră după 12 ore de nefolosire. ## Passkey Un passkey înlocuiește parola cu amprenta, fața sau codul telefonului. Îl adaugi din pagina contului, după ce ai intrat o dată cu altă metodă. Cheia este legată de domeniul platformei, așa că funcționează pe toate subdomeniile ei. ## Alegerea firmei Dacă ai acces la mai multe firme, alegi firma activă din antet. Alegerea: 1. se păstrează pe cont, nu doar în browser, deci o regăsești pe orice dispozitiv; 2. schimbă meniul din stânga (fiecare firmă are modulele ei); 3. schimbă datele: de acum citești și scrii doar în firma aleasă. Poți avea firme diferite deschise în taburi diferite ale browserului; fiecare tab își trimite firma lui. ## Invitarea colegilor Administratorul firmei invită un om nou din pagina **Acces**. Omul primește un link de invitație, își alege metoda de intrare și ajunge direct în firma care l-a invitat, cu rolul stabilit. ```bash title="Ce face aplicația când accepți o invitație" GET /v1/invitatii/{cod} # arată firma și rolul POST /v1/invitatii/{cod}/accepta # creează accesul în firmă ``` --- # Toate modulele Fiecare firmă își pornește doar modulele de care are nevoie. Alege unul ca să vezi ce face și rutele lui. ## Restaurant Pentru firmele de tip restaurant: meniul, canalele de livrare, stocul și clienții. | Modul | Ce face | | --- | --- | | [Meniul zilei](#modul-meniul-zilei) | Meniul zilei, publicat pe site și pe ecranele din restaurant, cu arhiva zilelor trecute. | | [Reduceri și coduri](#modul-reduceri) | Reducerile pe produse și codurile de reducere pentru campanii. | | [Recenzii](#modul-reviews) | Recenziile clienților de pe canale, cu propuneri de răspuns scrise de AI. | | [Bucătar personal](#modul-bucatar) | Rețete, ingrediente și inventar personal, legate de contul fiecărui om din firmă. | | [Înscriere fidelitate](#modul-fidelitate-inscriere) | Pagina publică, fără cont, prin care un client se înscrie la cardul de fidelitate al restaurantului. | | [Cardul clientului](#modul-fidelitate-card) | Cardul public al clientului: codul QR de arătat la casă și ultimele vizite. | ## Agenție Pentru firmele de tip agenție: creatori, campanii, social media, oferte și căutări. | Modul | Ce face | | --- | --- | | [Agenție](#modul-agentie) | Creatorii, campaniile, brandurile, managerii și plățile agenției. | | [Backstage](#modul-backstage) | Datele din Backstage (rețeaua de creatori live), aduse în platformă doar pentru citire. | | [Social media](#modul-social) | Statisticile postărilor, copertele salvate și ideile de conținut ale echipei. | | [Ofertar](#modul-oferte) | Ofertele de preț pentru clienți, construite din catalogul de servicii și șabloane. | | [Website Builder](#modul-website-builder) | Proiectele de site-uri, traducerea și SEO-ul lor, plus publicarea pe probă și pe oficial. | ## Finanțe și documente Facturi, e-Factura (SPV), avize, evidența financiară, registre și rapoarte. | Modul | Ce face | | --- | --- | | [Evidența financiară](#modul-evidenta) | Cererile de documente, conturile, plățile recurente, obligațiile și tranzacțiile firmei. | | [Registru](#modul-registru) | Registrul firmei: colecțiile de documente, numerotarea și dreptul de emitere. | | [Facturări și avize](#modul-facturari) | Registrul facturilor și proformelor emise de firmă, cu ciornele de factură nouă. | | [Contabilitate](#modul-contabilitate) | Obligațiile, scadențele, sarcinile și întrebările pentru contabil, plus predarea documentelor către contabil. | | [Tranzacții pe cont](#modul-tranzactii-cont) | Mișcările din contul firmei, doar pentru citire, filtrate după perioadă, tip și monedă. | | [Cont card](#modul-cont-card) | Mișcările cu cardul firmei, doar pentru citire, din lista tranzacțiilor. | | [Fluxul banilor](#modul-flux-numerar) | Banii firmei de la începutul perioadei, lună de lună. | | [Profit și pierdere](#modul-profit-pierdere) | Contul de profit și pierdere al firmei, pe luni. | | [Abonamente](#modul-abonamente) | Abonamentele fiecărui client și ce este de facturat luna aceasta. | | [Clienți](#modul-clienti-dls) | Clienții firmei din casa de marcat DLS: nume, CUI și oraș. | | [Furnizori](#modul-furnizori) | Furnizorii firmei din casa de marcat DLS: nume, CUI și oraș. | | [Catalog](#modul-catalog-dls) | Catalogul produselor din casa de marcat DLS, cu prețurile lor. | | [Statistici agenție](#modul-statistici-agentie) | Facturat și încasat pe luni, clienții și orele, pentru firmele de tip agenție. | ## Firma și echipa Oamenii firmei, prezența, dispozitivele, procedurile și sarcinile de zi cu zi. | Modul | Ce face | | --- | --- | | [Angajați și departamente](#modul-angajati) | Echipa firmei, departamentele și oamenii din fiecare departament. | | [Prezență](#modul-prezenta) | Cine este prezent acum, sosirile, plecările și urmele de activitate. | | [De făcut și sarcini](#modul-de-facut) | Lista de lucruri de făcut, adunată din mai multe surse, și sarcinile echipei. | | [Proceduri](#modul-proceduri) | Procedurile de lucru și fișa de informații a firmei. | | [Seif](#modul-seif) | Valorile secrete ale firmei (parole de portaluri, chei), criptate în baza de date. | | [Datele firmei](#modul-date-firma) | Datele legale ale firmei, conturile bancare și conturile externe conectate. | | [Echipă](#modul-echipa) | Membrii firmei, cu rolul și accesul fiecăruia, și departamentele, în aceeași pagină. | ## AI și automatizări Asistentul, centrul Claude, agenții executori și automatizările programate. | Modul | Ce face | | --- | --- | | [Asistent](#modul-asistent) | Asistentul AI din platformă și conversațiile echipei. | | [Centrul Claude](#modul-claude) | Sesiunile Claude, agenții care lucrează acum și jurnalul lor în timp real. | | [Automatizări](#modul-automatizari) | Automatizările programate de pe server, cu stare și pornire. | ## Contul tău Profilul, preferințele, notificările și căutarea, pentru fiecare om în parte. | Modul | Ce face | | --- | --- | | [Profil și preferințe](#modul-profil) | Cine ești pe firma activă, poza, formularul „Bine ai venit” și preferințele tale. | | [Notificări](#modul-notificari) | Notificările contului; fiecare om își vede și își marchează doar notificările lui. | | [Căutare](#modul-cautare) | Căutarea în paginile la care ai acces, după rol și bifele contului. | | [Setări](#modul-setari) | Fereastra de setări: profilul, securitatea, notificările, aspectul, firma și conturile conectate. | | [Update-uri](#modul-noutati) | Noutățile platformei, cu marcajul „văzut” ținut pe cont. | --- # Export și PDF Ce documente poți scoate din platformă ca PDF sau CSV, din ce pagină și prin ce rută. ## Pe scurt Platforma scoate documente în trei feluri: | Fel | Cum funcționează | | --- | --- | | **Fișier de pe server** | serverul trimite un PDF sau CSV gata făcut, prin rută | | **Tipar din browser** | pagina pregătește foaia și deschide dialogul de tipărire; de acolo alegi „Salvează ca PDF” | | **CSV făcut în pagină** | pagina construiește fișierul din datele afișate deja, fără o cerere nouă | ## Documentele | Document | Pagina | Format | Cum | Rută | | --- | --- | --- | --- | --- | | Raportul unei perioade | `/app/rapoarte` | PDF | server | `GET /v1/rapoarte/:id/pdf` | | Raportul unei zile, pe secțiuni | `/app/rapoarte` | PDF sau CSV | server | `POST /v1/rapoarte/descarca` | | Vânzări zi cu zi, vânzări pe produs | `/app/rapoarte` | CSV | în pagină | — | | Factura emisă din bon | `/app/facturi` | PDF | server | `GET /v1/facturi-documente/:id/pdf` | | Factura pe foaie (ciornă sau emisă) | `/app/facturi` | PDF | tipar | — | | Factura primită din SPV (ANAF) | `/app/facturi-primite` | PDF sau XML | server | `GET /v1/spv/facturi/:id/:tip` (`tip` = `pdf` sau `xml`) | | Documentul unei tranzacții din evidență | — (doar API) | fișierul atașat (PDF sau poză) | server | `GET /v1/evidenta/tranzactii/:id/documente/:doc` | | Oferta de preț | `/app/clienti-club` | PDF | tipar | — | | Lista ofertelor | `/app/clienti-club` | CSV | în pagină | — | | Lista clienților | `/app/clienti-club` | CSV | în pagină | — | | Avizele | `/app/avize` | PDF | tipar | — | | Meniul de azi, meniul complet | `/app/meniu` | PDF | tipar | — | | Necesarul comenzilor de catering | `/app/comenzi-catering` | PDF | tipar | — | | Profit și pierdere pe an | `/app/profit-pierdere` | CSV | în pagină | — | | Statisticile firmei pe luni | `/app/statistici-firma` | CSV | în pagină | — | | Serviciile în lucru | `/app/registru` | CSV | în pagină | — | | Oferta din registru | `/app/registru` | PDF | tipar | — | | Actele din registru | `/app/registru` | PDF | vizualizare în pagină | — | > [!NOTE] > La documentele „tipar” nu se descarcă un fișier direct: se deschide dialogul de tipărire al browserului. De acolo alegi „Salvează ca PDF”. ## Raportul unei zile ```json title="POST /v1/rapoarte/descarca" { "zi": "2026-11-10", "sectiuni": ["total", "per_produs", "numerar_card"], "format": "pdf" } ``` | Câmp | Valori | | --- | --- | | `zi` | o zi reală, `AAAA-LL-ZZ` (de exemplu `2026-02-30` e refuzată) | | `sectiuni` | cel puțin una, fără dubluri: `total`, `statistici`, `per_produs`, `livrari`, `numerar_card`, `neconcordante`, `nota`, `lista`, `lista_vanzari` | | `format` | `pdf` sau `csv` | | Răspuns | Când | | --- | --- | | `200` | documentul, ca atașament `raport-.` | | `403 sectiune_interzisa` | ai cerut o secțiune pe care rolul tău nu o are | | `409 zi_neinchisa` | ziua nu e închisă încă la casă | | `502 punte_esuata` | raportul nu s-a putut genera acum | Secțiunile permise se calculează pe server, după rol, nu din pagină. Rolurile fără drept de finanțe nu au nicio secțiune permisă. Un om căruia nu i s-a deschis ecranul primește `404`. ## Rapoartele pe perioadă `GET /v1/rapoarte/:id/pdf` întoarce PDF-ul ca atașament `raport-.pdf`. Fiecare raport aparține unui rol; un rol diferit primește `403 alt_rol`. Ruta răspunde doar firmei pentru care sunt configurate rapoartele; pentru celelalte, `404`, ca să nu afli nici dacă raportul există. ## CSV-urile CSV-urile sunt UTF-8; aproape toate au BOM la început, ca Excel să afișeze corect diacriticele (excepție: statisticile firmei). Separatorul diferă: | Fișier | Separator | | --- | --- | | Raportul zilei (server), vânzările din `/app/rapoarte`, profit și pierdere | `;` | | Clienții, ofertele, serviciile în lucru, statisticile firmei | `,` | În CSV-ul clienților, o celulă care începe cu `=`, `+`, `-` sau `@` primește un apostrof în față, ca Excel să nu o ia drept formulă. > [!WARNING] > Un CSV făcut în pagină conține doar ce vezi: lista filtrată, nu toată baza. Schimbă filtrele înainte de export dacă vrei mai mult. ## Antetele fișierelor de pe server ```http title="Antete la descărcare" Content-Type: application/pdf Content-Disposition: attachment; filename="raport-2026-11-10.pdf" Cache-Control: private, no-store X-Content-Type-Options: nosniff ``` Facturile în PDF (din bon, din SPV) și documentele din evidență se deschid `inline`, direct în browser; XML-ul din SPV și rapoartele se descarcă (`attachment`). Rapoartele și fișierele din SPV au `Cache-Control: private, no-store`, deci nu rămân în memoria intermediară. --- # Aplicația pe telefon și notificări Cum se instalează aplicația pe ecranul telefonului, ce notificări poți primi, cum le oprești și în ce limbă se afișează. ## Pe scurt | Ce | Cum | | --- | --- | | Instalare | aplicație web (PWA): se adaugă pe ecranul principal din browser și se deschide fără bara browserului | | Notificări | push în browser, plus clopoțelul din aplicație, după aceeași regulă de primire | | Preferințe | pe cont și pe firmă: tipuri oprite și ore de liniște | | Limba | română implicit; engleza e încă în lucru | ## Instalarea pe ecran Aplicația are un manifest web (`/manifest.webmanifest`), deci telefonul o poate adăuga pe ecranul principal ca pe o aplicație. | Câmp din manifest | Valoare | | --- | --- | | Nume | numele din manifestul aplicației | | Pornește la | `/` | | Afișare | `standalone` (rezervă: `minimal-ui`) | | Orientare | `portrait` | | Culoare temă și fundal | `#0e4a2b` | | Iconițe | 192 × 192, 512 × 512 și o variantă `maskable` de 512 × 512 | Pe iPhone, paginile au și etichetele Apple pentru ecranul principal, cu titlul din manifest. > [!NOTE] > Manifestul stă în spatele autentificării. De aceea, fiecare pagină îl cere cu `crossorigin="use-credentials"`. Fără asta, browserul l-ar cere fără cookie și ar primi pagina de login în loc de manifest. Un test verifică regula pe toate paginile. Aplicația nu are un buton propriu de instalare. Instalarea se face din meniul browserului. ## Notificările push Fiecare alertă are un **tip**. Pe pagina Setări → Notificări vezi un comutator pentru fiecare tip: | Tip | Ce anunță | | --- | --- | | `mz_dezactivat` | Meniul Zilei 1 sau 2 s-a dezactivat | | `meniul_zilei` | meniul zilei pe toate ecranele, dimineața | | `reduceri` | reducerile de pe Wolt au intrat sau s-au terminat | | `campanie` | campanii Wolt refăcute, cu produse noi la reducere | | `z` | Z-ul nescos la 23:30 | | `rezumat` | rezumatul zilei, la închiderea Z-ului | | `raport_zilei` | raportul zilei gata, după închiderea Z | | `zi_slaba` | zi slabă: vânzări mult sub obișnuit la 15:00 | | `raport` | raportul de dimineață, la 08:00 | | `rapoarte` | rapoartele tale pe departamente (PDF), la 21:00 | | `stoc` | stoc epuizat în bucătărie | | `pret` | preț schimbat în DLS | | `modificare` | modificări făcute de angajați (DLS, portaluri, panou) | | `sincronizare` | sincronizare picată pe un canal | | `tv` | televizor care nu răspunde | | `aparate` | aparatele oprite sau pornite la loc | | `fisier` | fișier nou pe Google Drive | | `actualizare` | update-uri ale aplicației | | `reparatie` | ce n-a putut repara serverul singur | | `cod` | modificare oprită: un script al platformei are o greșeală de scriere | | `imagine` | imagini generate, gata sau eșuate | Orele din tabel sunt cele din catalogul tipurilor. Trimiterea propriu-zisă a notificărilor push și înregistrarea service worker-ului nu sunt în codul platformei. Ele vin din platforma veche. Pagina doar citește abonamentul push existent al browserului. ## Cine ce primește Regula e una singură, pentru push și pentru clopoțel. Se calculează la citire, după drepturile de acum ale omului: - fiecare tip aparține unei funcții (preparate, vânzări, aparate, fișiere, administrare sau toți); îl primești dacă ai una din paginile funcției; - administratorul primește toate tipurile, mai puțin `modificare`; - unele roluri au o listă albă: Gestiune Produse, Bucătar Șef și Marketing primesc doar `rapoarte` și `reduceri`, iar Patron în plus `raport_zilei`; - alertele cu sume în lei (Z, rezumat, zi slabă, raportul zilei) ajung doar la cine vede sumele; - rolul Patron primește alertele de preparate, vânzări și rapoarte, chiar fără paginile lor; - `reduceri` ajunge la orice cont al firmei; - `imagine` și `chat_gata` ajung la un singur cont anume; - tipurile `poza` și `stoc_reumplut` sunt scoase și nu mai ajung la nimeni; - conturile de probă nu primesc nimic. ## Cum le oprești | Acțiune | Rută | | --- | --- | | Citești preferințele | `GET /v1/eu/notificari/preferinte` | | Oprești tipuri sau pui ore de liniște | `POST /v1/eu/notificari/preferinte` | | Marchezi „văzut” | `POST /v1/eu/notificari/vazut` | ```json title="POST /v1/eu/notificari/preferinte" { "oprite": ["zi_slaba", "tv"], "linisteDe": "22:00", "linistePana": "07:00" } ``` - Orele de liniște au forma `HH:MM` și vin **amândouă sau niciuna**. - Corpul schimbă doar ce conține. Restul rămâne cum era. - Implicit nu e nimic oprit și nu există ore de liniște. - Butonul „Oprește pe acest dispozitiv” scoate abonamentul push al browserului curent. Pe celelalte dispozitive notificările continuă. > [!WARNING] > Clopoțelul marchează alertele drept văzute pe server când îl deschizi (`POST /v1/eu/notificari/vazut`). Pe un cont real, deschiderea lui schimbă starea pentru omul acela. Platforma notează, ca urme anonime de utilizare, când o notificare push a fost afișată sau apăsată și dacă pagina rulează instalată (`pwa`). Vezi [Jurnal de audit](#jurnal-de-audit). ## Limba aplicației - Limba implicită este **româna** (`ro-RO`). Cealaltă limbă pregătită este engleza (`en-GB`). - Alegerea se păstrează pe server, pe cont (`GET` și `PUT /v1/eu/preferinte`). În browser rămâne doar o copie, ca prima afișare să iasă direct în limba bună. - Engleza nu se poate alege încă din interfață: e ascunsă până la traducerea completă. - Banii, numerele și datele se formatează după limba aleasă: `1.234,50 lei` în română. --- # Meniul zilei Meniul zilei, publicat pe site și pe ecranele din restaurant, cu arhiva zilelor trecute. ## Pe scurt | | | | --- | --- | | Grup | Restaurant | | Rute API | 5 rute, în [referința API](#api-meniul-zilei) | | Acces | `admin` 3, `public` 1, `sesiune` 1 | ## Ce face - Pagina publică a meniului zilei se citește fără cont; editarea cere sesiune. - Arhiva păstrează fiecare meniu publicat, ca să poată fi refolosit. ## Rutele | Metodă | Cale | Acces | | --- | --- | --- | | `GET` | `/v1/public/meniul-zilei` | `public` | | `GET` | `/v1/arhiva` | `rol:admin` | | `GET` | `/v1/arhiva/{id}/{nume}` | `rol:admin` | | `GET` | `/v1/arhiva/stare/{id}` | `rol:admin` | | `PUT` | `/v1/arhiva/stare/{id}` | `sesiune` | Exemplele de cerere, pentru fiecare rută, sunt în [Referința API](#api-meniul-zilei). --- # Reduceri și coduri Reducerile pe produse și codurile de reducere pentru campanii. ## Pe scurt | | | | --- | --- | | Grup | Restaurant | | Rute API | 6 rute, în [referința API](#api-reduceri) | | Acces | `sesiune` 4, `admin` 2 | ## Ce face - Un cod de reducere se dezactivează, nu se șterge, ca să se poată verifica ulterior pe ce comenzi a fost folosit. ## Rutele | Metodă | Cale | Acces | | --- | --- | --- | | `GET` | `/v1/reduceri/{platforma}` | `rol:admin` | | `POST` | `/v1/reduceri/{platforma}` | `sesiune` | | `GET` | `/v1/reduceri/active` | `sesiune` | | `GET` | `/v1/coduri-reducere` | `rol:admin` | | `POST` | `/v1/coduri-reducere` | `sesiune` | | `PATCH` | `/v1/coduri-reducere/{id}` | `sesiune` | Exemplele de cerere, pentru fiecare rută, sunt în [Referința API](#api-reduceri). --- # Recenzii Recenziile clienților de pe canale, cu propuneri de răspuns scrise de AI. ## Pe scurt | | | | --- | --- | | Grup | Restaurant | | Rute API | 16 rute, în [referința API](#api-reviews) | | Acces | `sesiune` 16 | ## Ce face - Propunerile AI rămân în platformă până le aprobă un om; nimic nu pleacă singur pe canale. ## Rutele | Metodă | Cale | Acces | | --- | --- | --- | | `GET` | `/v1/reviews` | `sesiune` | | `PUT` | `/v1/reviews/{id}/ciorna` | `sesiune` | | `PUT` | `/v1/reviews/{id}/propunere` | `sesiune` | | `POST` | `/v1/reviews/{id}/reformuleaza` | `sesiune` | | `PUT` | `/v1/reviews/{id}/trimitere` | `sesiune` | | `GET` | `/v1/reviews/context` | `sesiune` | | `PUT` | `/v1/reviews/context/{cheie}` | `sesiune` | | `GET` | `/v1/reviews/generator/stare` | `sesiune` | | `GET` | `/v1/reviews/instructiuni` | `sesiune` | | `POST` | `/v1/reviews/instructiuni` | `sesiune` | | `DELETE` | `/v1/reviews/instructiuni/{id}` | `sesiune` | | `GET` | `/v1/reviews/propuneri` | `sesiune` | | `POST` | `/v1/reviews/propuneri/{id}/regenereaza` | `sesiune` | | `POST` | `/v1/reviews/propuneri/genereaza` | `sesiune` | | `POST` | `/v1/reviews/propuneri/genereaza-intern` | `sesiune` | Încă o rută sunt în [referința completă](#api-reviews). Exemplele de cerere, pentru fiecare rută, sunt în [Referința API](#api-reviews). --- # Bucătar personal Rețete, ingrediente și inventar personal, legate de contul fiecărui om din firmă. ## Pe scurt | | | | --- | --- | | Grup | Restaurant | | Rute API | 29 de rute, în [referința API](#api-bucatar) | | Acces | `sesiune` 29 | ## Ce face - Datele personale stau mereu pe perechea (firmă, cont): un coleg nu vede inventarul altuia. ## Rutele | Metodă | Cale | Acces | | --- | --- | --- | | `GET` | `/v1/bucatar/din-inventar` | `sesiune` | | `GET` | `/v1/bucatar/genereaza-imagini-lipsa` | `sesiune` | | `POST` | `/v1/bucatar/genereaza-imagini-lipsa` | `sesiune` | | `GET` | `/v1/bucatar/imagine/{fisier}` | `sesiune` | | `GET` | `/v1/bucatar/ingrediente` | `sesiune` | | `POST` | `/v1/bucatar/ingrediente` | `sesiune` | | `POST` | `/v1/bucatar/ingrediente/{id}/pret` | `sesiune` | | `POST` | `/v1/bucatar/ingrediente/{id}/pret-din-url` | `sesiune` | | `GET` | `/v1/bucatar/inventar` | `sesiune` | | `POST` | `/v1/bucatar/inventar` | `sesiune` | | `GET` | `/v1/bucatar/jurnal` | `sesiune` | | `POST` | `/v1/bucatar/jurnal` | `sesiune` | | `DELETE` | `/v1/bucatar/jurnal/{id}` | `sesiune` | | `POST` | `/v1/bucatar/lista-cumparaturi` | `sesiune` | | `GET` | `/v1/bucatar/meta` | `sesiune` | Încă 14 rute sunt în [referința completă](#api-bucatar). Exemplele de cerere, pentru fiecare rută, sunt în [Referința API](#api-bucatar). --- # Înscriere fidelitate Pagina publică, fără cont, prin care un client se înscrie la cardul de fidelitate al restaurantului. ## Pe scurt | | | | --- | --- | | Grup | Restaurant | | Rute API | 10 rute, în [referința API](#api-fidelitate-inscriere) | | Acces | `sesiune` 7, `public` 3 | ## Ce face - Firma vine din adresă și o confirmă serverul; clientul vede ce se face cu datele lui înainte să accepte. ## Rutele | Metodă | Cale | Acces | | --- | --- | --- | | `GET` | `/v1/fidelitate/card/{cod}` | `public` | | `GET` | `/v1/fidelitate/cifre` | `sesiune` | | `GET` | `/v1/fidelitate/clienti` | `sesiune` | | `GET` | `/v1/fidelitate/clienti/{id}` | `sesiune` | | `POST` | `/v1/fidelitate/clienti/{id}/sterge` | `sesiune` | | `GET` | `/v1/fidelitate/de-verificat` | `sesiune` | | `GET` | `/v1/fidelitate/firma/{slug}` | `public` | | `POST` | `/v1/fidelitate/inscriere` | `public` | | `POST` | `/v1/fidelitate/scanari` | `sesiune` | | `POST` | `/v1/fidelitate/scanari/{id}/rezolva` | `sesiune` | Exemplele de cerere, pentru fiecare rută, sunt în [Referința API](#api-fidelitate-inscriere). --- # Cardul clientului Cardul public al clientului: codul QR de arătat la casă și ultimele vizite. ## Pe scurt | | | | --- | --- | | Grup | Restaurant | ## Ce face - Codul stă în fragmentul adresei, deci nu ajunge în jurnalele serverului; telefonul nu apare nicăieri. --- # Agenție Creatorii, campaniile, brandurile, managerii și plățile agenției. ## Pe scurt | | | | --- | --- | | Grup | Agenție | | Rute API | 12 rute, în [referința API](#api-agentie) | | Acces | `admin` 12 | ## Ce face - Listele sunt paginate pe server, cu căutare și sortare. - Accesul cere sesiune, rol de administrator și modulul „agenție” activ pe firmă. ## Rutele | Metodă | Cale | Acces | | --- | --- | --- | | `GET` | `/v1/agentie/branduri` | `rol:admin` | | `GET` | `/v1/agentie/branduri/coada` | `rol:admin` | | `GET` | `/v1/agentie/branduri/hashtaguri` | `rol:admin` | | `GET` | `/v1/agentie/campanii` | `rol:admin` | | `GET` | `/v1/agentie/campanii/{id}` | `rol:admin` | | `GET` | `/v1/agentie/creatori` | `rol:admin` | | `GET` | `/v1/agentie/creatori/{id}` | `rol:admin` | | `GET` | `/v1/agentie/manageri` | `rol:admin` | | `GET` | `/v1/agentie/notificari` | `rol:admin` | | `GET` | `/v1/agentie/scraper` | `rol:admin` | | `GET` | `/v1/agentie/stare` | `rol:admin` | | `GET` | `/v1/agentie/sumar` | `rol:admin` | Exemplele de cerere, pentru fiecare rută, sunt în [Referința API](#api-agentie). --- # Backstage Datele din Backstage (rețeaua de creatori live), aduse în platformă doar pentru citire. ## Pe scurt | | | | --- | --- | | Grup | Agenție | | Rute API | 2 rute, în [referința API](#api-backstage) | | Acces | `sesiune` 1, `admin` 1 | ## Ce face - Disponibil doar firmei agenției; orice altă firmă primește 403. ## Rutele | Metodă | Cale | Acces | | --- | --- | --- | | `POST` | `/v1/backstage/scrie` | `sesiune` | | `GET` | `/v1/backstage/stare` | `rol:admin` | Exemplele de cerere, pentru fiecare rută, sunt în [Referința API](#api-backstage). --- # Social media Statisticile postărilor, copertele salvate și ideile de conținut ale echipei. ## Pe scurt | | | | --- | --- | | Grup | Agenție | | Rute API | 20 de rute, în [referința API](#api-social) | | Acces | `sesiune` 20 | ## Ce face - Copertele se salvează ca fișiere proprii, pentru că linkurile platformelor sociale expiră. ## Rutele | Metodă | Cale | Acces | | --- | --- | --- | | `GET` | `/v1/social/coperte/{fisier}` | `sesiune` | | `GET` | `/v1/social/fisiere` | `sesiune` | | `GET` | `/v1/social/fisiere/miniatura/{id}` | `sesiune` | | `GET` | `/v1/social/idei` | `sesiune` | | `POST` | `/v1/social/idei` | `sesiune` | | `DELETE` | `/v1/social/idei/{id}` | `sesiune` | | `PUT` | `/v1/social/idei/{id}` | `sesiune` | | `POST` | `/v1/social/idei/{id}/in-calendar` | `sesiune` | | `PATCH` | `/v1/social/idei/{id}/stare` | `sesiune` | | `GET` | `/v1/social/oameni` | `sesiune` | | `GET` | `/v1/social/postari` | `sesiune` | | `POST` | `/v1/social/postari` | `sesiune` | | `DELETE` | `/v1/social/postari/{id}` | `sesiune` | | `PUT` | `/v1/social/postari/{id}` | `sesiune` | | `PATCH` | `/v1/social/postari/{id}/muta` | `sesiune` | Încă 5 rute sunt în [referința completă](#api-social). Exemplele de cerere, pentru fiecare rută, sunt în [Referința API](#api-social). --- # Ofertar Ofertele de preț pentru clienți, construite din catalogul de servicii și șabloane. ## Pe scurt | | | | --- | --- | | Grup | Agenție | | Rute API | 10 rute, în [referința API](#api-oferte) | | Acces | `sesiune` 10 | ## Ce face - Valorile incoerente (de exemplu valabilitate înainte de data ofertei) primesc 400 cu explicația. ## Rutele | Metodă | Cale | Acces | | --- | --- | --- | | `GET` | `/v1/oferte` | `sesiune` | | `POST` | `/v1/oferte` | `sesiune` | | `DELETE` | `/v1/oferte/{id}` | `sesiune` | | `GET` | `/v1/oferte/{id}` | `sesiune` | | `PUT` | `/v1/oferte/{id}` | `sesiune` | | `POST` | `/v1/oferte/{id}/duplica` | `sesiune` | | `POST` | `/v1/oferte/{id}/status` | `sesiune` | | `GET` | `/v1/catalog/sabloane` | `sesiune` | | `GET` | `/v1/catalog/servicii` | `sesiune` | | `POST` | `/v1/catalog/servicii` | `sesiune` | Exemplele de cerere, pentru fiecare rută, sunt în [Referința API](#api-oferte). --- # Website Builder Proiectele de site-uri, traducerea și SEO-ul lor, plus publicarea pe probă și pe oficial. ## Pe scurt | | | | --- | --- | | Grup | Agenție | | Rute API | 11 rute, în [referința API](#api-website-builder) | | Acces | `sesiune` 6, `admin` 5 | ## Ce face - Publicarea are trei trepte: probă, staging și oficial, cu revenire la versiunea anterioară. ## Rutele | Metodă | Cale | Acces | | --- | --- | --- | | `POST` | `/v1/wtso/clona` | `rol:admin` | | `GET` | `/v1/wtso/proiecte` | `rol:admin` | | `POST` | `/v1/wtso/rulare` | `rol:admin` | | `GET` | `/v1/wtso/rulare/{id}` | `rol:admin` | | `GET` | `/v1/wtso/stare` | `rol:admin` | | `POST` | `/v1/publicare/oficial` | `sesiune` | | `POST` | `/v1/publicare/oficial/revino` | `sesiune` | | `POST` | `/v1/publicare/proba` | `sesiune` | | `GET` | `/v1/publicare/staging` | `sesiune` | | `POST` | `/v1/publicare/staging` | `sesiune` | | `GET` | `/v1/publicare/stare` | `sesiune` | Exemplele de cerere, pentru fiecare rută, sunt în [Referința API](#api-website-builder). --- # Evidența financiară Cererile de documente, conturile, plățile recurente, obligațiile și tranzacțiile firmei. ## Pe scurt | | | | --- | --- | | Grup | Finanțe și documente | | Rute API | 30 de rute, în [referința API](#api-evidenta) | | Acces | `sesiune` 21, `admin` 9 | ## Ce face - Totul se citește pe firma activă; starea unei cereri se calculează la citire, nu se stochează. ## Rutele | Metodă | Cale | Acces | | --- | --- | --- | | `GET` | `/v1/evidenta/categorii` | `rol:admin` | | `GET` | `/v1/evidenta/cereri` | `rol:admin` | | `POST` | `/v1/evidenta/cereri` | `sesiune` | | `POST` | `/v1/evidenta/cereri/{id}/primit` | `sesiune` | | `GET` | `/v1/evidenta/conturi` | `rol:admin` | | `POST` | `/v1/evidenta/import` | `sesiune` | | `GET` | `/v1/evidenta/panou` | `rol:admin` | | `GET` | `/v1/evidenta/recurente` | `rol:admin` | | `POST` | `/v1/evidenta/recurente` | `sesiune` | | `PATCH` | `/v1/evidenta/recurente/{id}` | `sesiune` | | `POST` | `/v1/evidenta/recurente/{id}/arhiveaza` | `sesiune` | | `PUT` | `/v1/evidenta/recurente/{id}/plata` | `sesiune` | | `GET` | `/v1/evidenta/setari` | `rol:admin` | | `PUT` | `/v1/evidenta/setari` | `sesiune` | | `GET` | `/v1/evidenta/tranzactii` | `rol:admin` | Încă 15 rute sunt în [referința completă](#api-evidenta). Exemplele de cerere, pentru fiecare rută, sunt în [Referința API](#api-evidenta). --- # Registru Registrul firmei: colecțiile de documente, numerotarea și dreptul de emitere. ## Pe scurt | | | | --- | --- | | Grup | Finanțe și documente | | Rute API | 8 rute, în [referința API](#api-registru) | | Acces | `sesiune` 8 | ## Rutele | Metodă | Cale | Acces | | --- | --- | --- | | `GET` | `/v1/registru` | `sesiune` | | `GET` | `/v1/registru/col` | `sesiune` | | `POST` | `/v1/registru/col/scrie` | `sesiune` | | `GET` | `/v1/registru/drept-emite` | `sesiune` | | `POST` | `/v1/registru/numar` | `sesiune` | | `DELETE` | `/v1/registru/oferte` | `sesiune` | | `POST` | `/v1/registru/oferte` | `sesiune` | | `PUT` | `/v1/registru/oferte` | `sesiune` | Exemplele de cerere, pentru fiecare rută, sunt în [Referința API](#api-registru). --- # Facturări și avize Registrul facturilor și proformelor emise de firmă, cu ciornele de factură nouă. ## Pe scurt | | | | --- | --- | | Grup | Finanțe și documente | | Rute API | 20 de rute, în [referința API](#api-facturari) | | Acces | `sesiune` 20 | ## Ce face - O factură nouă se salvează ca ciornă (prima salvare o creează, următoarele o actualizează); emiterea propriu-zisă nu se face din această pagină. ## Rutele | Metodă | Cale | Acces | | --- | --- | --- | | `GET` | `/v1/facturi` | `sesiune` | | `GET` | `/v1/avize` | `sesiune` | | `POST` | `/v1/avize` | `sesiune` | | `GET` | `/v1/avize/{id}` | `sesiune` | | `PATCH` | `/v1/avize/{id}` | `sesiune` | | `POST` | `/v1/avize/{id}/anuleaza` | `sesiune` | | `GET` | `/v1/avize/clienti` | `sesiune` | | `POST` | `/v1/avize/clienti` | `sesiune` | | `PATCH` | `/v1/avize/clienti/{id}` | `sesiune` | | `GET` | `/v1/avize/emitent` | `sesiune` | | `PUT` | `/v1/avize/emitent` | `sesiune` | | `POST` | `/v1/avize/genereaza-zi` | `sesiune` | | `GET` | `/v1/avize/meniu-camine/{zi}` | `sesiune` | | `PUT` | `/v1/avize/meniu-camine/{zi}` | `sesiune` | | `GET` | `/v1/avize/puncte` | `sesiune` | Încă 5 rute sunt în [referința completă](#api-facturari). Exemplele de cerere, pentru fiecare rută, sunt în [Referința API](#api-facturari). --- # Contabilitate Obligațiile, scadențele, sarcinile și întrebările pentru contabil, plus predarea documentelor către contabil. ## Pe scurt | | | | --- | --- | | Grup | Finanțe și documente | ## Ce face - Pe date reale toate vin din nucleu, pe firma activă. --- # Tranzacții pe cont Mișcările din contul firmei, doar pentru citire, filtrate după perioadă, tip și monedă. ## Pe scurt | | | | --- | --- | | Grup | Finanțe și documente | --- # Cont card Mișcările cu cardul firmei, doar pentru citire, din lista tranzacțiilor. ## Pe scurt | | | | --- | --- | | Grup | Finanțe și documente | ## Ce face - Limita, cashback-ul și plata automată nu au încă sursă: pagina le arată goale, fără cifre de probă. --- # Fluxul banilor Banii firmei de la începutul perioadei, lună de lună. ## Pe scurt | | | | --- | --- | | Grup | Finanțe și documente | ## Ce face - Serverul nu are încă o rută pentru această pagină, așa că pe aplicație apare „Fără date încă”, nu cifre inventate. --- # Profit și pierdere Contul de profit și pierdere al firmei, pe luni. ## Pe scurt | | | | --- | --- | | Grup | Finanțe și documente | ## Ce face - Serverul nu are încă o rută pentru această pagină, așa că pe aplicație apare „Fără date încă”, nu cifre inventate. --- # Abonamente Abonamentele fiecărui client și ce este de facturat luna aceasta. ## Pe scurt | | | | --- | --- | | Grup | Finanțe și documente | ## Ce face - Din pagină se pregătește doar ciorna; nimic nu se emite de aici. Până se leagă sursa, lista rămâne goală. --- # Clienți Clienții firmei din casa de marcat DLS: nume, CUI și oraș. ## Pe scurt | | | | --- | --- | | Grup | Finanțe și documente | | Rute API | o rută, în [referința API](#api-clienti-dls) | | Acces | `sesiune` 1 | ## Ce face - Contactele, soldul și facturile clientului nu vin încă din DLS și nu se afișează. ## Rutele | Metodă | Cale | Acces | | --- | --- | --- | | `GET` | `/v1/clienti` | `sesiune` | Exemplele de cerere, pentru fiecare rută, sunt în [Referința API](#api-clienti-dls). --- # Furnizori Furnizorii firmei din casa de marcat DLS: nume, CUI și oraș. ## Pe scurt | | | | --- | --- | | Grup | Finanțe și documente | | Rute API | 2 rute, în [referința API](#api-furnizori) | | Acces | `sesiune` 2 | ## Ce face - Contactele, soldul și facturile furnizorului nu vin încă din DLS și nu se afișează. ## Rutele | Metodă | Cale | Acces | | --- | --- | --- | | `GET` | `/v1/catalog` | `sesiune` | | `GET` | `/v1/furnizori` | `sesiune` | Exemplele de cerere, pentru fiecare rută, sunt în [Referința API](#api-furnizori). --- # Catalog Catalogul produselor din casa de marcat DLS, cu prețurile lor. ## Pe scurt | | | | --- | --- | | Grup | Finanțe și documente | ## Ce face - Rețetele, costurile și istoricul de preț nu vin încă din DLS și nu se afișează. --- # Statistici agenție Facturat și încasat pe luni, clienții și orele, pentru firmele de tip agenție. ## Pe scurt | | | | --- | --- | | Grup | Finanțe și documente | | Rute API | o rută, în [referința API](#api-statistici-agentie) | | Acces | `admin` 1 | ## Ce face - Pagina cere 24 de luni: graficul arată ultimele 12, iar comparația cu perioada dinainte le folosește pe celelalte. ## Rutele | Metodă | Cale | Acces | | --- | --- | --- | | `GET` | `/v1/statistici-firma` | `rol:admin` | Exemplele de cerere, pentru fiecare rută, sunt în [Referința API](#api-statistici-agentie). --- # Angajați și departamente Echipa firmei, departamentele și oamenii din fiecare departament. ## Pe scurt | | | | --- | --- | | Grup | Firma și echipa | | Rute API | 12 rute, în [referința API](#api-angajati) | | Acces | `sesiune` 12 | ## Rutele | Metodă | Cale | Acces | | --- | --- | --- | | `GET` | `/v1/angajati` | `sesiune` | | `POST` | `/v1/angajati` | `sesiune` | | `PATCH` | `/v1/angajati/{id}` | `sesiune` | | `GET` | `/v1/angajati/{id}/poza` | `sesiune` | | `PUT` | `/v1/angajati/{id}/poza` | `sesiune` | | `GET` | `/v1/departamente` | `sesiune` | | `POST` | `/v1/departamente` | `sesiune` | | `DELETE` | `/v1/departamente/{id}` | `sesiune` | | `PATCH` | `/v1/departamente/{id}` | `sesiune` | | `DELETE` | `/v1/departamente/{id}/oameni/{contId}` | `sesiune` | | `PUT` | `/v1/departamente/{id}/oameni/{contId}` | `sesiune` | | `GET` | `/v1/angajat/meniul-zilei` | `sesiune` | Exemplele de cerere, pentru fiecare rută, sunt în [Referința API](#api-angajati). --- # Prezență Cine este prezent acum, sosirile, plecările și urmele de activitate. ## Pe scurt | | | | --- | --- | | Grup | Firma și echipa | | Rute API | 8 rute, în [referința API](#api-prezenta) | | Acces | `sesiune` 8 | ## Ce face - Un om își scrie doar propria prezență, pe firma activă. ## Rutele | Metodă | Cale | Acces | | --- | --- | --- | | `GET` | `/v1/prezenta` | `sesiune` | | `GET` | `/v1/prezenta/istoric` | `sesiune` | | `GET` | `/v1/prezenta/mini/{fisier}` | `sesiune` | | `POST` | `/v1/prezenta/plecat` | `sesiune` | | `GET` | `/v1/prezenta/social` | `sesiune` | | `POST` | `/v1/prezenta/urme` | `sesiune` | | `GET` | `/v1/alerte` | `sesiune` | | `GET` | `/v1/prezenta/oameni` | `sesiune` | Exemplele de cerere, pentru fiecare rută, sunt în [Referința API](#api-prezenta). --- # De făcut și sarcini Lista de lucruri de făcut, adunată din mai multe surse, și sarcinile echipei. ## Pe scurt | | | | --- | --- | | Grup | Firma și echipa | | Rute API | 6 rute, în [referința API](#api-de-facut) | | Acces | `sesiune` 6 | ## Ce face - O sursă lipsă apare în răspuns la „surse_lipsa”, fără să strice restul listei. ## Rutele | Metodă | Cale | Acces | | --- | --- | --- | | `GET` | `/v1/de-facut` | `sesiune` | | `GET` | `/v1/de-facut/liste-mihai` | `sesiune` | | `GET` | `/v1/sarcini` | `sesiune` | | `POST` | `/v1/sarcini` | `sesiune` | | `DELETE` | `/v1/sarcini/{id}` | `sesiune` | | `PATCH` | `/v1/sarcini/{id}` | `sesiune` | Exemplele de cerere, pentru fiecare rută, sunt în [Referința API](#api-de-facut). --- # Proceduri Procedurile de lucru și fișa de informații a firmei. ## Pe scurt | | | | --- | --- | | Grup | Firma și echipa | | Rute API | 2 rute, în [referința API](#api-proceduri) | | Acces | `admin` 2 | ## Rutele | Metodă | Cale | Acces | | --- | --- | --- | | `GET` | `/v1/proceduri` | `rol:admin` | | `GET` | `/v1/info` | `rol:admin` | Exemplele de cerere, pentru fiecare rută, sunt în [Referința API](#api-proceduri). --- # Seif Valorile secrete ale firmei (parole de portaluri, chei), criptate în baza de date. ## Pe scurt | | | | --- | --- | | Grup | Firma și echipa | | Rute API | 4 rute, în [referința API](#api-seif) | | Acces | `sesiune` 4 | ## Ce face - Stă în Setări, secțiunea „Integrări”, și o vede doar administratorul firmei; ceilalți primesc 403. - Fiecare rând are versiunea cheii de criptare, ca rotirea cheii să se facă fără pierderi. - Dezvăluirea unei valori este o acțiune separată, jurnalizată. ## Rutele | Metodă | Cale | Acces | | --- | --- | --- | | `GET` | `/v1/seif` | `sesiune` | | `POST` | `/v1/seif` | `sesiune` | | `PUT` | `/v1/seif/{id}` | `sesiune` | | `POST` | `/v1/seif/{id}/dezvaluie` | `sesiune` | Exemplele de cerere, pentru fiecare rută, sunt în [Referința API](#api-seif). --- # Datele firmei Datele legale ale firmei, conturile bancare și conturile externe conectate. ## Pe scurt | | | | --- | --- | | Grup | Firma și echipa | | Rute API | 11 rute, în [referința API](#api-date-firma) | | Acces | `sesiune` 10, `admin` 1 | ## Ce face - CUI-ul este unic: dacă altă firmă îl are deja, schimbarea este refuzată. ## Rutele | Metodă | Cale | Acces | | --- | --- | --- | | `GET` | `/v1/firma/{firmaId}/date` | `sesiune` | | `PUT` | `/v1/firma/{firmaId}/date` | `sesiune` | | `DELETE` | `/v1/firma/{firmaId}/logo` | `sesiune` | | `PUT` | `/v1/firma/{firmaId}/logo` | `sesiune` | | `GET` | `/v1/firma/{firmaId}/conturi` | `sesiune` | | `POST` | `/v1/firma/{firmaId}/conturi` | `sesiune` | | `DELETE` | `/v1/firma/{firmaId}/conturi/{contId}` | `sesiune` | | `PUT` | `/v1/firma/{firmaId}/conturi/{contId}` | `sesiune` | | `GET` | `/v1/conturi-conectate` | `sesiune` | | `GET` | `/v1/setari/ordine` | `sesiune` | | `PUT` | `/v1/setari/ordine` | `rol:admin` | Exemplele de cerere, pentru fiecare rută, sunt în [Referința API](#api-date-firma). --- # Echipă Membrii firmei, cu rolul și accesul fiecăruia, și departamentele, în aceeași pagină. ## Pe scurt | | | | --- | --- | | Grup | Firma și echipa | ## Ce face - Tabul „Departamente” îl vede doar cine are dreptul pe departamente; ceilalți ajung pe „Membri”. --- # Asistent Asistentul AI din platformă și conversațiile echipei. ## Pe scurt | | | | --- | --- | | Grup | AI și automatizări | | Rute API | 16 rute, în [referința API](#api-asistent) | | Acces | `sesiune` 9, `admin` 7 | ## Ce face - Asistentul este disponibil doar conturilor alese de firmă; celelalte primesc 403. ## Rutele | Metodă | Cale | Acces | | --- | --- | --- | | `GET` | `/v1/asistent/acces` | `sesiune` | | `GET` | `/v1/asistent/conversatii` | `sesiune` | | `DELETE` | `/v1/asistent/conversatii/{id}` | `sesiune` | | `PATCH` | `/v1/asistent/conversatii/{id}` | `sesiune` | | `GET` | `/v1/asistent/conversatii/{id}/mesaje` | `sesiune` | | `POST` | `/v1/asistent/feedback` | `sesiune` | | `POST` | `/v1/asistent/mesaj` | `sesiune` | | `GET` | `/v1/asistent/stare` | `sesiune` | | `POST` | `/v1/asistent/voce/sesiune` | `sesiune` | | `GET` | `/v1/chat/conversatii` | `rol:admin` | | `POST` | `/v1/chat/conversatii` | `rol:admin` | | `POST` | `/v1/chat/conversatii/{id}/citit` | `rol:admin` | | `GET` | `/v1/chat/conversatii/{id}/mesaje` | `rol:admin` | | `POST` | `/v1/chat/conversatii/{id}/mesaje` | `rol:admin` | | `GET` | `/v1/chat/flux` | `rol:admin` | Încă o rută sunt în [referința completă](#api-asistent). Exemplele de cerere, pentru fiecare rută, sunt în [Referința API](#api-asistent). --- # Centrul Claude Sesiunile Claude, agenții care lucrează acum și jurnalul lor în timp real. ## Pe scurt | | | | --- | --- | | Grup | AI și automatizări | | Rute API | 27 de rute, în [referința API](#api-claude) | | Acces | `sesiune` 14, `admin` 13 | ## Rutele | Metodă | Cale | Acces | | --- | --- | --- | | `GET` | `/v1/claude/alocare` | `rol:admin` | | `PUT` | `/v1/claude/alocare` | `sesiune` | | `POST` | `/v1/claude/alocare/codex` | `sesiune` | | `GET` | `/v1/claude/alocare/garda` | `sesiune` | | `GET` | `/v1/claude/cereri` | `rol:admin` | | `POST` | `/v1/claude/cereri` | `sesiune` | | `GET` | `/v1/claude/costuri` | `rol:admin` | | `POST` | `/v1/claude/costuri` | `sesiune` | | `GET` | `/v1/claude/harta` | `rol:admin` | | `POST` | `/v1/claude/harta` | `sesiune` | | `GET` | `/v1/claude/pasi` | `rol:admin` | | `POST` | `/v1/claude/pasi` | `sesiune` | | `GET` | `/v1/claude/pasi/{rulare}` | `rol:admin` | | `GET` | `/v1/claude/porniri` | `rol:admin` | | `POST` | `/v1/claude/porniri` | `rol:admin` | Încă 12 rute sunt în [referința completă](#api-claude). Exemplele de cerere, pentru fiecare rută, sunt în [Referința API](#api-claude). --- # Automatizări Automatizările programate de pe server, cu stare și pornire. ## Pe scurt | | | | --- | --- | | Grup | AI și automatizări | | Rute API | 8 rute, în [referința API](#api-automatizari) | | Acces | `admin` 4, `sesiune` 4 | ## Ce face - Pagina arată doar câmpurile de afișare; comenzile tehnice ale automatizării nu ies niciodată în browser. ## Rutele | Metodă | Cale | Acces | | --- | --- | --- | | `GET` | `/v1/automatizari` | `rol:admin` | | `DELETE` | `/v1/automatizari/{id}` | `rol:admin` | | `POST` | `/v1/automatizari/{id}` | `rol:admin` | | `POST` | `/v1/automatizari/{id}/activ` | `rol:admin` | | `GET` | `/v1/update-uri` | `sesiune` | | `POST` | `/v1/update-uri` | `sesiune` | | `POST` | `/v1/update-uri/{id}/vazut` | `sesiune` | | `GET` | `/v1/update-uri/poza` | `sesiune` | Exemplele de cerere, pentru fiecare rută, sunt în [Referința API](#api-automatizari). --- # Profil și preferințe Cine ești pe firma activă, poza, formularul „Bine ai venit” și preferințele tale. ## Pe scurt | | | | --- | --- | | Grup | Contul tău | | Rute API | 37 de rute, în [referința API](#api-profil) | | Acces | `sesiune` 37 | ## Rutele | Metodă | Cale | Acces | | --- | --- | --- | | `GET` | `/v1/eu` | `sesiune` | | `POST` | `/v1/eu/firma` | `sesiune` | | `GET` | `/v1/eu/firme` | `sesiune` | | `GET` | `/v1/eu/flux` | `sesiune` | | `GET` | `/v1/eu/pagini-noi` | `sesiune` | | `POST` | `/v1/eu/pagini-noi/vazut` | `sesiune` | | `GET` | `/v1/eu/tot` | `sesiune` | | `POST` | `/v1/eu/vezi-ca` | `sesiune` | | `GET` | `/v1/oameni` | `sesiune` | | `POST` | `/v1/prezenta/bataie` | `sesiune` | | `GET` | `/v1/profil/bun-venit` | `sesiune` | | `PATCH` | `/v1/profil/bun-venit` | `sesiune` | | `PUT` | `/v1/profil/bun-venit` | `sesiune` | | `DELETE` | `/v1/profil/poza` | `sesiune` | | `GET` | `/v1/profil/poza` | `sesiune` | Încă 22 de rute sunt în [referința completă](#api-profil). Exemplele de cerere, pentru fiecare rută, sunt în [Referința API](#api-profil). --- # Notificări Notificările contului; fiecare om își vede și își marchează doar notificările lui. ## Pe scurt | | | | --- | --- | | Grup | Contul tău | | Rute API | 8 rute, în [referința API](#api-notificari) | | Acces | `sesiune` 8 | ## Rutele | Metodă | Cale | Acces | | --- | --- | --- | | `GET` | `/v1/eu/notificari` | `sesiune` | | `POST` | `/v1/eu/notificari/{id}/citita` | `sesiune` | | `POST` | `/v1/eu/notificari/citite` | `sesiune` | | `GET` | `/v1/eu/notificari/istoric` | `sesiune` | | `GET` | `/v1/eu/notificari/necitite` | `sesiune` | | `GET` | `/v1/eu/notificari/preferinte` | `sesiune` | | `POST` | `/v1/eu/notificari/preferinte` | `sesiune` | | `POST` | `/v1/eu/notificari/vazut` | `sesiune` | Exemplele de cerere, pentru fiecare rută, sunt în [Referința API](#api-notificari). --- # Căutare Căutarea în paginile la care ai acces, după rol și bifele contului. ## Pe scurt | | | | --- | --- | | Grup | Contul tău | | Rute API | o rută, în [referința API](#api-cautare) | | Acces | `sesiune` 1 | ## Rutele | Metodă | Cale | Acces | | --- | --- | --- | | `GET` | `/v1/cautare/categorii` | `sesiune` | Exemplele de cerere, pentru fiecare rută, sunt în [Referința API](#api-cautare). --- # Setări Fereastra de setări: profilul, securitatea, notificările, aspectul, firma și conturile conectate. ## Pe scurt | | | | --- | --- | | Grup | Contul tău | ## Ce face - Notificările push se aleg pe tip, cu ore de liniște; „Oprește pe acest dispozitiv” scoate doar browserul curent. --- # Update-uri Noutățile platformei, cu marcajul „văzut” ținut pe cont. ## Pe scurt | | | | --- | --- | | Grup | Contul tău | --- # Firme și module Cum adaugi o firmă pe platformă, ce date legale are și cum îi activezi doar modulele de care are nevoie. ## O platformă, mai multe firme Platforma este construită de la început pentru mai mulți clienți (multi-tenant). Fiecare client este o **firmă** în tabelul `firme`, cu: | Câmp | Ce conține | | --- | --- | | `nume`, `cui`, `reg_com` | datele legale; `cui` este unic pe toată platforma | | `sediu` | adresa sediului social | | `tip` | `restaurant`, `agentie` sau `gol` (firmă nouă, fără profil) | | `tema`, `logo` | aspectul aplicației pentru oamenii firmei | | conturi bancare | gestionate din **Datele firmei** | > [!WARNING] > Dacă încerci să salvezi un CUI pe care îl are deja altă firmă de pe platformă, schimbarea este refuzată. Două firme nu pot avea același CUI. ## Modulele unei firme Tabelul `module_firma` ține, pe fiecare firmă, lista modulelor și starea lor (`activ` sau nu). Un modul activ: - apare în meniul din stânga pentru oamenii cu acces la el; - la agenție, deschide și rutele API ale modulului pentru firma respectivă. Un modul inactiv dispare din meniu. Pe API, drepturile le decide rolul; doar agenția verifică în plus, la fiecare cerere, că modulul e activ. Datele deja scrise rămân în baza de date și reapar când modulul este reactivat. ## Module legate de tipul firmei Tipul firmei și lista ei de module decid meniul din stânga. Câteva reguli sunt scrise direct în cod: | Funcție | Regula | | --- | --- | | Backstage | aparține firmei agenției (Ilustromania); orice altă firmă din sesiune primește `403` | | Factură din bon | butonul apare în aplicație doar administratorului și doar la firmele de tip `restaurant`; serverul verifică separat dreptul de emitere | | Firmă de tip `gol` | pornește fără profil; meniul se construiește din modulele activate | Celelalte module (facturi, echipă, de făcut, seif) nu au o regulă legată de tipul firmei. ## Pași pentru o firmă nouă 1. Administratorul platformei creează firma cu datele legale și tipul ei. 2. Activează modulele de care firma are nevoie. 3. Invită primul administrator al firmei (vezi [Conturi și invitații](#conturi-si-invitatii)). 4. Administratorul firmei își invită apoi echipa și le dă roluri. --- # Conturi și invitații Cum aduci oameni în firmă, cum le resetezi parola și cum vezi aplicația așa cum o văd ei. ## Două feluri de a adăuga un om | Cale | Ce se întâmplă | | --- | --- | | **Invitație** | omul primește un link, își alege singur metoda de intrare și intră în firmă cu rolul stabilit | | **Cont direct** | administratorul creează contul; platforma generează o parolă temporară, arătată o singură dată | Ambele se fac din pagina **Acces** și cer rolul `admin` pe firma activă. ```bash title="Rutele folosite de pagina Acces" POST /v1/acces/invitatii # invitație nouă POST /v1/acces/oameni/cont # cont nou + parolă temporară PATCH /v1/acces/oameni/{id}/rol # schimbă rolul POST /v1/acces/oameni/{id}/parola-temporara # resetare de către administrator ``` ## Resetarea parolei - **Omul singur:** din ecranul de intrare alege „Am uitat parola” și primește pe email un link, valabil 30 de minute. - **Administratorul:** generează o parolă temporară pentru un cont existent al firmei. Parola nouă se arată o singură dată și nu se păstrează nicăieri în clar. În aceeași tranzacție se închid sesiunile deschise ale contului, acordurile OAuth și codurile de email, iar fapta intră în jurnalul de audit. ## Vezi aplicația ca Administratorul poate comuta „Vezi aplicația ca” pe un alt rol, ca să verifice ce vede un bucătar sau un casier. Comutarea ține doar de sesiunea curentă și nu schimbă drepturile nimănui. Doar un administrator real (rolul din baza de date) o poate porni; altfel răspunsul este `403`. ```bash title="Vezi aplicația ca" POST /v1/eu/vezi-ca {"rol": "bucatar"} # null = revii la rolul tău ``` ## Când pleacă un om Din pagina **Acces** oprești accesul omului (`PATCH /v1/acces/oameni/{id}`). Un cont oprit nu mai poate intra: verificarea se face la fiecare intrare. Jetoanele date aplicațiilor externe (MCP) se opresc și ele pe firma respectivă, pentru că rolul este citit live la fiecare apel. --- # Roluri și drepturi Accesul se construiește în trei straturi - rolul în firmă, drepturile fine și paginile bifate pentru fiecare om. ## Cele trei straturi | Strat | Unde stă | Exemplu | | --- | --- | --- | | Rol | `acces_firma(cont_id, firma_id, rol)` | Ana este `marketing` în Ilustromania | | Drepturi fine | `drepturi_cont(cont_id, firma_id, drept)` | Dan are în plus dreptul de a edita meniul | | Pagini | lista de pagini a omului pe firmă | Dan vede și pagina **Stoc**, dar nu **Facturi** | Rolul dă punctul de plecare. Drepturile fine și paginile îl ajustează pentru un om anume, fără să inventezi un rol nou. ## Rolurile | Rol | Folosire obișnuită | | --- | --- | | `admin` | administrează oamenii, drepturile, modulele și setările firmei | | `patron` | proprietarul afacerii | | `finante` | facturi, plăți, evidență | | `gestiune` | stoc, recepții, furnizori | | `bucatar` | meniu, rețete, ingrediente | | `casier` | vânzare, casa de marcat | | `marketing` | social media, reclame, recenzii | | `angajat` | acces de bază | Acestea sunt cele 8 roluri de firmă. Separat există `executor`, rolul tehnic al jetoanelor de serviciu ale agenților de pe server: nu se dă unui om și nu apare în pagina **Acces**. > [!NOTE] > O rută cu acces `rol:admin` cere exact rolul `admin` pe firma activă. Nu există moștenire implicită între roluri: `patron` nu primește automat ce are `admin`. ## Roluri de echipă Pe lângă rolurile fixe, firma își poate defini **roluri de echipă** proprii (de exemplu „Social Media”), cu paginile și notificările lor. Se gestionează din pagina **Acces** și au rutele `/v1/acces/roluri-echipa`. ## Cum decide platforma Fiecare rută își declară accesul. Ordinea verificării: 1. Ruta are acces declarat? Dacă nu: `401 ruta_nedeclarata`. 2. Accesul este `public`? Atunci cererea trece. 3. Există o sesiune validă (sau un jeton `mcp_`)? Dacă nu: `401`. 4. Accesul cere un rol (`rol:`)? Rolul omului pe firma activă trebuie să fie exact acela, altfel `403`. 5. Modulul își face apoi propriile verificări (modul activ, drept fin), tot cu `403` la refuz. **Distribuția rutelor pe acces** Distribuția actuală a celor 398 de rute: | Acces | Rute | | --- | --- | | `sesiune` | 285 | | `rol:admin` | 83 | | `public` | 30 | Cifrele sunt calculate la generarea documentației, din harta de acces a codului. --- # Medii Producția, staging-ul și proba rulează același cod, dar setările de mediu și câteva gărzi le fac să se comporte diferit. ## Pe scurt | Mediu | Adresa | Valoarea `MEDIU` | Rută de probă | | --- | --- | --- | --- | | Producție | `app.ilustromania.com` | `productie` (implicită) | nu există, oricum ar fi configurat serverul | | Staging | `staging.ilustromania.com` | `staging` | există doar dacă e setat `OM_PROBA` | | Probă | serverul de probă (fără adresă publică fixă în cod) | `proba` | există doar dacă e setat `OM_PROBA` | Codul este același. Diferențele vin din variabilele de mediu citite la pornire, în `config.ts`, și din două gărzi legate de numele gazdei de producție, `GAZDA_PRODUCTIE`. ## Mediul declarat: `MEDIU` `MEDIU` acceptă doar `productie`, `staging` și `proba`. Orice altă valoare oprește pornirea. Dacă variabila lipsește, serverul se socotește **producție**: un server uitat neconfigurat primește regulile cele mai stricte, nu cele mai permisive. ## Proba: `OM_PROBA` și `/v1/sesiune/proba` `POST /v1/sesiune/proba` deschide o sesiune pentru „omul de probă” din `OM_PROBA` (cont, firmă, rol), fără parolă. Sesiunea durează cât una obișnuită, 8 ore. E făcută pentru capturi și teste automate, nu pentru oameni. Ruta are trei centuri, puse una peste alta: | Centura | Unde | Ce se întâmplă | | --- | --- | --- | | 1. Fără `OM_PROBA` | la montarea rutelor | ruta nu este înregistrată deloc | | 2. `OM_PROBA` cu `MEDIU=productie` sau cu `GAZDA_PUBLICA=app.ilustromania.com` | la pornire | API-ul refuză să pornească, cu un mesaj clar | | 3. Cerere venită pe gazda de producție | la fiecare cerere | răspunde `404 negasit`, ca și cum ruta n-ar exista | ```bash title="Sesiune de probă pe staging" curl -i -X POST https://staging.ilustromania.com/nucleu/v1/sesiune/proba # 204 + Set-Cookie: __Host-sesiune=… ``` > [!WARNING] > Omul de probă poate avea rolul `admin`. Ruta este publică în harta accesului tocmai pentru că sesiunea o creează ea. De aceea, siguranța stă în cele trei centuri, nu într-o parolă. ## Ce diferă între medii | Setare | Producție | Staging și probă | | --- | --- | --- | | `OM_PROBA` | interzis (pornirea eșuează) | permis | | `CASCADA_PRODUS`, dacă lipsește | `off`: dezactivarea unui produs nu le oprește pe cele care îl conțin | `on` | | Panoul de publicare | publică doar pe `app.ilustromania.com` | nu are rând propriu | | Linkurile din emailurile cu cod | spre `app.ilustromania.com` | spre `staging.ilustromania.com`, dacă de acolo s-a cerut codul | Orice altă gazdă decât cele două primește, în email, adresa de producție. ## Comutatoarele de mediu Următoarele comutatoare au aceeași valoare implicită pe toate mediile. Valoarea lor efectivă pe fiecare server nu e în codul platformei: o pune configurarea serverului. | Variabilă | Implicit | Ce face | | --- | --- | --- | | `SCRIERE_STARI` | `off` | `off` = rutele de scriere ale meniului răspund `409`; `on` = scriu | | `PUNTE_MENIU` | `off` | `off` = nicio cerere spre canale; `simulare` sau `on` = cererea se scrie pentru executor | | `OFICIAL_ACTIV` | `off` | `off` = orice acțiune pe Oficial, în panoul de publicare, dă `409 neactivat` | | `PUNTE_ACCEPTA_V1` | `0` | acceptă sau nu forma veche a punții de sesiune | | `PUBLICARE_DIR` și cele două stări | absente | fără ele, rutele `/v1/publicare/*` nu există; trebuie date toate trei, altfel pornirea eșuează | ## Steagurile de funcții Steagurile (`GET /v1/flaguri`) nu țin de mediu. Ele stau în baza de date a fiecărui mediu, deci staging-ul și producția își au fiecare setul lor. Regula: - rândul cel mai specific câștigă: cont, apoi firmă, apoi global; - `procent` între 1 și 99 pornește funcția treptat, după o „găleată” 0–99 calculată din id-ul contului, aceeași pe orice mașină; - un steag necunoscut înseamnă `false`. Vezi și [Roluri și drepturi](#roluri-si-drepturi) pentru ce vede fiecare om, indiferent de mediu. --- # Securitate Straturile de protecție ale platformei, de la izolarea firmelor în baza de date până la jurnalele care nu scriu niciodată un secret. ## Pe scurt | Strat | Ce face | | --- | --- | | Izolarea firmelor | Row Level Security `FORCE` pe fiecare tabel cu `firma_id` | | Acces declarat | fiecare rută își declară accesul; ruta nedeclarată răspunde `401 ruta_nedeclarata` | | Sesiune | cookie `__Host-sesiune`, `HttpOnly`, `Secure`, `SameSite=Lax` | | Gardă CSRF | scrierile cu cookie venite de pe alt site sunt refuzate cu `403` | | Încadrare | `frame-ancestors 'self'` și `X-Frame-Options: SAMEORIGIN` pe orice răspuns | | Parole | `scrypt` cu sare per cont, liste de parole comune refuzate, ultimele 5 parole nu se pot refolosi | | Limite | 300 de scrieri pe minut pe sesiune, 1200 pe IP (vezi [Limite](#limite)) | | Jurnale | cookie-ul, antetul `Authorization` și orice câmp `secret` sunt înlocuite cu `[ascuns]` | ## Izolarea firmelor Fiecare cerere fixează firma activă în conexiunea la baza de date (`app.firma_id`). Politicile RLS sunt în mod `FORCE`, deci se aplică și proprietarului tabelului. O greșeală într-o interogare nu poate întoarce rândurile altei firme: baza de date pur și simplu nu le arată. > [!NOTE] > Tabelele cele mai sensibile (hash-urile parolelor vechi, sărurile) nu pot fi citite nici de utilizatorul bazei de date al aplicației. Verificarea se face prin funcții SQL care întorc doar „da” sau „nu”. ## Garda CSRF Cookie-ul este `SameSite=Lax`, ca un link dintr-un email să te ducă direct în aplicație. Pentru scrieri există o gardă separată, aplicată înainte de orice rută: ```text title="Regula gărzii CSRF (metode POST, PUT, PATCH, DELETE cu cookie)" Sec-Fetch-Site: cross-site → 403 fără Sec-Fetch-Site, cu Origin ≠ aplicația → 403 fără cookie (aplicații, scripturi, MCP) → trece; se aplică autentificarea rutei ``` ## Parolele - Hash `scrypt`, cu sare per cont; parola în clar nu se scrie nicăieri. - Parolele din listele publice de parole sparte sunt refuzate la alegere. - Parola nouă nu poate fi una dintre ultimele 5 ale contului. - După a treia parolă greșită pe un cont real, proprietarul primește o alertă. - Resetarea de către administrator închide toate sesiunile, acordurile OAuth și codurile de email ale contului. ## Antetele de securitate ```http title="Antete pe orice răspuns" Content-Security-Policy: frame-ancestors 'self' X-Frame-Options: SAMEORIGIN ``` O rută care își pune propriul `Content-Security-Policy` îl păstrează. ## Jurnalele Jurnalele serverului ascund mereu, fără excepție configurabilă: - antetul `Cookie` și antetul `Authorization`; - orice câmp numit `secret`, până la trei niveluri adâncime în obiect (`secret`, `x.secret`, `x.y.secret`) și în corpul cererii; - codul de invitație din cale (`/v1/invitatii/[cod]`). ## Raportarea unei probleme Dacă găsești o vulnerabilitate, scrie administratorului platformei înainte de orice publicare. Nu testa pe datele altor firme. --- # Limite Câte cereri primește platforma, de la cine, cum vezi cât mai ai până la prag și ce răspuns primești când treci de el. ## Limita generală Fiecare cerere trece printr-o limită comună, numărată pe fereastră fixă de un minut: ceasul pornește la prima cerere, iar după 60 de secunde numărătoarea o ia de la zero. Scrierile și citirile au numărători separate: | Cerere | Pe sesiune (cookie) | Pe adresă IP | | --- | --- | --- | | Scrieri: `POST`, `PUT`, `PATCH`, `DELETE` | 300 / minut | 1200 / minut | | Citiri: `GET`, `HEAD` | 1200 / minut | 6000 / minut | Un om lucrează normal sub 30 de scrieri pe minut, deci pragul pe sesiune este de 10 ori mai mare. Pe IP pragul este mai mare fiindcă un birou întreg poate ieși pe aceeași adresă; IP-ul prinde și scripturile fără cookie. O cerere cu o [cheie API](#chei-api) (`Authorization: Bearer ilk_…`) se numără pe cheie, cu aceleași praguri ca o sesiune, și pe IP. O cerere fără cookie de sesiune și fără cheie API se numără doar pe IP. `OPTIONS` nu se numără. Rutele care au limita lor (intrare în cont, OAuth, MCP) o păstrează și pe a lor, peste cea generală. ## Antetele de limită Fiecare răspuns la o scriere spune cât mai ai, după propunerea IETF [RateLimit headers](https://datatracker.ietf.org/doc/draft-ietf-httpapi-ratelimit-headers/) (draft 11): ```http title="Antetele unei scrieri" RateLimit-Policy: "sesiune";q=300;w=60, "ip";q=1200;w=60 RateLimit: "sesiune";r=297;t=42, "ip";r=1188;t=42 ``` - `RateLimit-Policy`: regulile, fixe; `q` = câte cereri, `w` = fereastra în secunde. - `RateLimit`: starea acum, pe fiecare regulă; `r` = câte cereri mai ai, `t` = în câte secunde se golește fereastra. Partea `"sesiune"` apare doar când cererea are cookie de sesiune. Citirile nu poartă aceste antete. ## Răspunsul la depășire ```http title="Răspunsul la depășire" HTTP/1.1 429 Too Many Requests Retry-After: 42 Content-Type: application/json {"eroare": "prea multe cereri"} ``` La o scriere, `Retry-After` este numărul de secunde până se golește fereastra epuizată (cea mai lungă, dacă sunt două). La citiri și la limitele proprii ale rutelor, care nu au valoarea lor, `Retry-After` este `60`. Orice `429` al platformei are `Retry-After`. > [!TIP] > Citește `RateLimit` la fiecare scriere și încetinește când `r` se apropie de zero. Când primești `429`, așteaptă câte secunde spune `Retry-After`. Nu reîncerca imediat în buclă: fiecare încercare intră tot în numărătoare. Pentru o scriere pe care o repeți, folosește [o cheie de idempotență](#idempotenta). ## Limitele rutelor speciale | Rută | Prag | Cheie | | --- | --- | --- | | `POST /v1/oauth/inregistreaza` | 10 / oră | IP | | `GET /v1/oauth/autorizeaza` | 60 / 10 minute | IP | | `POST /v1/oauth/jeton` | 120 / 10 minute | IP | | `POST /v1/mcp` fără jeton valid | 120 / 10 minute | IP | Grupurile de mai jos au și ele limita lor, peste cea generală. În afară de cazurile spuse la fiecare grup, depășirea dă `429` cu un corp `{"eroare": "..."}` și `Retry-After: 60`. Cu `Accept: application/problem+json`, `prea_multe_incercari`, `prea_multe_cereri` și `prea-multe` primesc același cod stabil, `prea_multe_cereri`; `limita_utilizator`, `limita_firma` și `limita_furnizor` își păstrează numele (vezi [Erori](#api-erori)). ## Intrarea în cont Se numără încercările **greșite**, nu toate cererile. Fereastra este fixă, de 15 minute. | Rută | Prag | Cheie | Ce primești peste prag | | --- | --- | --- | --- | | `POST /v1/auth/intrare` (email sau nume + parolă) | 8 eșecuri / 15 minute | IP | `429` `prea_multe_incercari` | | `POST /v1/auth/intrare` | 8 eșecuri / 15 minute | contul + IP | `401`, același refuz ca la o parolă greșită | | `POST /v1/auth/intrare-google` | 8 eșecuri / 15 minute | IP | `429` `prea_multe_incercari` | | `POST /v1/passkey/optiuni-intrare`, `POST /v1/passkey/intra` (Face ID / Touch ID) | 30 de cereri / 15 minute | IP | `429` `prea_multe_cereri` | | `POST /v1/passkey/optiuni-inregistrare` (adaugi o cheie pe cont) | 20 de cereri / 15 minute | contul din sesiune | `429` `prea_multe_cereri` | Blocarea pe cont ține de IP: opt parole greșite de pe altă adresă nu blochează contul pentru cei care intră de la restaurant. Pentru că refuzul pe cont arată la fel ca o parolă greșită, nu afli din răspuns dacă un cont există. ## Codurile pe email și schimbarea parolei Toate rutele de aici numără pe IP, cu fereastră fixă de 15 minute. Pe lângă ele, codul în sine are limitele lui, păstrate în baza de date. | Rută | Prag | Cheie | Ce primești peste prag | | --- | --- | --- | --- | | `POST /v1/auth/cod-email/trimite` | 10 / 15 minute | IP | tot `204`, dar emailul nu mai pleacă | | `POST /v1/auth/cod-email/verifica` | 30 / 15 minute | IP | `429` `prea_multe_incercari` | | `POST /v1/auth/parola-noua`, `POST /v1/auth/parola/schimba`, `POST /v1/auth/parola-uitata/confirma` | 10 / 15 minute, numărate împreună | IP | `429` `prea_multe_incercari` | | `POST /v1/resetare/cere` | 10 / 15 minute pe IP și 3 / 15 minute pe adresa de email | IP, email | `429` `prea_multe_cereri` | | `POST /v1/resetare/confirma` | 10 / 15 minute | IP | `429` `prea_multe_cereri` | | `POST /v1/acces/oameni/:id/parola-temporara` (un admin dă o parolă temporară) | 10 / 10 minute | adminul | `429` `prea_multe_cereri` | Limitele codului primit pe email: - Un cod e valabil 10 minute. După expirare, verificarea dă `410` `cod_expirat`. - Un cod acceptă cel mult 5 încercări greșite. După ele, verificarea dă `429` `prea_multe_incercari`, chiar dacă trimiți codul corect. Ceri un cod nou. - Un cont primește cel mult 5 coduri pe oră. Peste ele, `trimite` răspunde tot `204`, dar codul nu se mai creează. - Un cod nou le anulează pe cele vechi, de același tip, ale contului. `cod-email/trimite` și `resetare/cere` răspund în cel puțin 400 de milisecunde și la fel, fie că emailul are cont, fie că nu. Un `204` sau un `202` nu înseamnă deci că a plecat un email. ## Invitațiile | Rută | Prag | Cheie | | --- | --- | --- | | `GET /v1/invitatii/:cod`, `POST /v1/invitatii/:cod/accepta` | 20 / minut | IP + metodă + rută | Se numără separat pe fiecare rută, cu fereastră fixă de un minut. Crearea unei invitații (`POST /v1/acces/invitatii`, cu sesiune) nu intră aici. Peste prag: `429` `prea_multe_cereri`. ## Paginile publice: catering și fidelitate Rutele fără sesiune, folosite de site, numără pe IP. | Rută | Prag | Fereastră | | --- | --- | --- | | `GET /v1/public/catering/meniu`, `.../activitate`, `.../configurare` | 60 / minut, separat pe fiecare | fixă | | `POST /v1/public/catering/prezenta` | 30 / minut | fixă | | `POST /v1/public/catering/cod` | 20 / 10 minute | fixă | | `POST /v1/public/catering/comanda` | 5 / 10 minute | fixă | | `GET /v1/fidelitate/firma/:slug`, `GET /v1/fidelitate/card/:cod` | 60 / minut, numărate împreună | glisantă | | `POST /v1/fidelitate/inscriere` | 20 / oră pe IP și 3 / oră pe același IP + număr de telefon | glisantă | Peste prag: `429` `prea_multe_cereri`. La meniul de catering, refuzul are și `Cache-Control: no-store`, ca să nu rămână în cache. **Fereastră glisantă** înseamnă că se numără cererile din ultimele 60 de minute (sau ultimul minut), nu cele de la o oră fixă. O cerere iese din numărătoare exact la o oră după ce a intrat. ## Asistentul, AI-ul și chatul Aici se numără pe **cont**, ca un cont să nu consume bugetul altuia. Ferestrele de un minut sunt glisante. | Rută | Prag | Cheie | Ce primești peste prag | | --- | --- | --- | --- | | `POST /v1/meniu/ai/ingrediente`, `POST /v1/meniu/ai/poza` | 20 / minut, numărate împreună | contul | `429` `prea_multe_cereri` | | `POST /v1/meniu/ai/imagine` | 100 / minut | contul | `429` `prea_multe_cereri` | | `POST /v1/facturi/citeste` (factură citită din poză sau PDF) | 20 / minut | contul | `429` `prea_multe_cereri` | | `POST /v1/facturi/scanare` | 20 / minut | contul | `429` `prea_multe_cereri` | | `POST /v1/chat/conversatii/:id/mesaje` | 30 de mesaje / minut | contul | `429` `prea-multe` | | `POST /v1/asistent/mesaj` | 50 de întrebări / zi pe utilizator și 500 / zi pe firmă | contul, firma | `429` `limita_utilizator` sau `limita_firma`, cu un `mesaj` de afișat | Pentru asistent, ziua este ziua UTC. Numărătoarea pornește de la zero la miezul nopții UTC, adică la 02:00 sau 03:00, ora României. Rutele care doar citesc starea unei imagini generate, o anulează sau decid asupra ei nu consumă din limită. Când furnizorul de AI își atinge propria limită, primești `429` `limita_furnizor`. Asta nu ține de numărătoarea ta: reîncearcă după `Retry-After`. ## Mărimea încărcărilor Fiecare rută care primește fișiere are o limită pentru tot corpul cererii. Corpul mai mare de atât e oprit înainte de rută și primești `413` `{"eroare": "cerere invalida"}`. Unele rute verifică după aceea și fișierul în sine. | Rută | Corpul cererii | Fișierul | Peste limita fișierului | | --- | --- | --- | --- | | `POST /v1/meniu/produse/:id/poze/:slot` (poza unui produs) | 21 MiB (poza în base64 + JSON) | 15 MiB, cel mult 80 de milioane de pixeli | `400` `poza_refuzata` | | `PUT /v1/profil/poza` | 5 MiB + 16 KiB | 5 MiB | `413` `poza_prea_mare` | | `PUT /v1/angajati/:id/poza` | 5 MiB + 16 KiB | 5 MiB | `413` `poza_prea_mare` | | `POST /v1/facturi/citeste` | 9 000 000 de octeți | — | — | | `POST /v1/facturi/scanare` | 9 500 000 de octeți | — | — | | `POST /v1/meniu/ai/poza` | 9 000 000 de octeți | — | — | | `POST /v1/meniu/ai/imagine` | 6 000 000 de octeți | — | — | | `POST /v1/evidenta/tranzactii/:id/documente` | 10 MiB + 4096 octeți | 10 MiB | `413` `prea_mare` | | `POST /v1/evidenta/import` (extrasul de cont) | 5 MiB + 4096 octeți | 5 MiB | `413` `prea_mare` | Diferența dintre corp și fișier lasă loc pentru restul cererii, de exemplu antetele formularului `multipart`. 1 MiB = 1 048 576 de octeți. ## Limitele de mărime și timp | Unde | Limită | | --- | --- | | Corpul cererii la înregistrarea unui client OAuth | 2048 octeți | | Corpul cererii la `POST /v1/oauth/jeton` | 4096 octeți | | Răspunsul unei unelte MCP | 100 000 de caractere | | Durata unui apel de unealtă MCP | 20 de secunde | | Paginarea uneltei MCP `meniu_produse_listeaza` | maximum 200 de produse pe pagină | ## Numărul cererii Fiecare răspuns are antetul `x-request-id`. Dacă trimiți tu un `x-request-id` (8–64 de caractere: litere, cifre, `-` sau `_`), platforma îl păstrează; altfel pune unul nou. Când raportezi o problemă cu limitele sau o eroare, trimite acest număr: cererea se găsește după el în jurnalul serverului. --- # Date personale Ce date despre oameni ține platforma, cine le poate vedea, ce nu ajunge niciodată în jurnale și cum se scoate un om din evidențe. ## Pe scurt | Cine | Ce se ține | Cine vede | | --- | --- | --- | | Conturile platformei | nume, email, ultima activitate, parola (doar ca hash) | membrii firmelor contului; hash-ul parolei nu îl citește nici aplicația | | Angajații echipei | nume, specialitate, ora de venire, data angajării, tipul contractului, țara, anul nașterii, salariul net și brut, legături către contract și documente, poza | membrii firmei; salariile doar `admin`, `patron`, `finante` | | Clienții de catering | numele, firma, telefonul, adresa de livrare; la abonați și valoarea lunară | doar `admin` | | Clienții de fidelitate | numele, telefonul, data acordului și versiunea lui, acordul pentru oferte | dreptul `fidelitate.citeste` | | Clienții firmei (B2B) | numele, CUI-ul, orașul | membrii firmei, după drepturi | | Activitatea în platformă | ultima „bătaie” pe pagină, urmele de acțiuni ale contului | firma activă | | Schimbările de parolă | contul, cine a schimbat, pe ce cale, când, IP-ul (fără hash) | nicio rută a API-ului nu le citește | ## Cine vede ce ### Izolarea pe firmă Fiecare tabel cu `firma_id` are Row Level Security în mod `FORCE`: o interogare vede doar rândurile firmei active, indiferent ce cere. Asta se aplică angajaților, comenzilor de catering, clienților de fidelitate și clienților B2B (vezi [Securitate](#securitate)). Conturile nu aparțin unei singure firme: un cont se vede doar dacă are acces la firma activă, sau dacă e chiar contul tău. ### Parola Aplicația poate citi din tabelul conturilor doar `id`, `nume`, `email`, `ultima_activitate` și `creat_la`. Coloana cu hash-ul parolei nu îi este dată deloc. ### Salariile `GET /v1/angajati` întoarce lista oricărui membru al firmei, dar cheile `salariuNet` și `salariuBrut` apar doar pentru rolurile `admin`, `patron` și `finante`. Pentru restul, cheile lipsesc din răspuns (nu sunt goale, lipsesc). Cine încearcă să scrie un salariu fără acest drept primește `403 interzis_salarii`. ### Rolurile pe pagini Restul se decide pe rol și pe drepturi (vezi [Roluri și drepturi](#roluri-si-drepturi)): comenzile de catering doar pentru `admin`, fidelitatea pe drepturile `fidelitate.citeste` și `fidelitate.gestioneaza`. ## Ce nu ajunge în jurnale Jurnalul serverului ascunde mereu, înlocuind cu `[ascuns]`: | Ce | Unde | | --- | --- | | câmpul `secret` | oriunde în obiectul scris, inclusiv în corpul cererii | | cookie-ul | antetul `Cookie` | | autorizarea | antetul `Authorization` | Două căi poartă un cod secret chiar în adresă. În jurnal, codul e înlocuit cu `[cod]`: ```text title="Căile redactate" /v1/invitatii/[/accepta] → /v1/invitatii/[cod] /v1/fidelitate/card/ → /v1/fidelitate/card/[cod] ``` Pentru fiecare cerere, jurnalul scrie doar metoda, adresa (redactată), gazda și adresa IP. La o eroare neprevăzută scrie ruta (tiparul, fără valori), metoda, id-ul cererii și eroarea; niciodată corpul, cookie-ul, antetele sau query-ul. > [!NOTE] > Fără `LOG=1`, jurnalul scrie doar erorile; cererile obișnuite nu se scriu deloc. Redactarea se aplică oricum, oricare ar fi nivelul. ## Scoaterea unui om | Cine | Ce se poate face | Ce rămâne | | --- | --- | --- | | Contul platformei | administratorul oprește accesul la firmă (`PATCH /v1/acces/oameni/:id` cu `{"acces": false}`) | contul; în API nu există o rută care șterge un cont | | Poza de profil | omul o scoate (`DELETE /v1/profil/poza`) | fișierul se mută în arhivă, nu se șterge | | Clientul de fidelitate | anonimizare: `POST /v1/fidelitate/clienti/:id/sterge`, cu dreptul `fidelitate.gestioneaza` | rândul rămâne cu numele `Șters`, telefonul gol, un cod de card nou și data ștergerii; punctele rămân ca registru | | Comanda de catering | — | aplicația poate citi, adăuga și modifica, dar nu șterge | ```json title="Anonimizarea unui client de fidelitate" POST /v1/fidelitate/clienti/7c1e.../sterge → 200 { "sters": true } ``` A doua cerere pe același client întoarce `404`: clientul e deja șters. ## Acordul clientului de fidelitate La înscriere, clientul bifează explicit acordul (`acord: true`, nimic altceva nu e acceptat). Acordul pentru mesaje cu oferte este o bifă separată și lipsa ei înseamnă „nu”. Se păstrează data acordului și versiunea textului acceptat. Cardul de fidelitate este un cod aleator, fără date personale în el. > [!WARNING] > Anonimizarea schimbă doar numele, telefonul și codul cardului. Date personale scrise în alte câmpuri (note, descrieri, etichete, în alte module) nu se șterg odată cu clientul. --- # Jurnal de audit Ce urme lasă platforma la fiecare schimbare importantă, în ce tabel, cu ce câmpuri, cine le poate citi și cât timp rămân. ## Pe scurt Platforma nu are un singur „jurnal de audit”. Are mai multe tabele, fiecare pentru un fel de schimbare. Toate au același principiu: urma se scrie **în aceeași tranzacție** cu schimbarea, iar aplicația poate doar să adauge rânduri, nu să le modifice sau să le șteargă. | Tabel | Ce înregistrează | Cine scrie | Se citește din aplicație | | --- | --- | --- | --- | | `acces_audit` | drepturi, roluri, oameni noi, parole resetate de administrator | modulul `acces` și funcțiile SQL ale contului | nu: nicio rută nu îl citește | | `events` | orice schimbare la produse, categorii, starea zilei, firme | triggere pe tabele și modulul `meniu` | da, `GET /v1/evenimente` | | `jurnal_parole` | orice schimbare a parolei unui cont | trigger pe `conturi` | nu: aplicația nu are drept de citire | | `seif_jurnal` | cine a deschis o intrare din seif și când | modulul seifului | nu: aplicația are doar `select, insert` | | `zi_jurnal` | acțiunile zilei (oprire de produs, export) | modulul `meniu` | prin exportul zilei | | `urme_client` | ce face omul în pagină (clic, filtru, export, erori de formular) | `POST /v1/prezenta/urme` | nu: nicio rută nu îl citește | | `alerte_istoric` | alertele trimise firmei | importul din platforma veche | da, `GET /v1/eu/notificari/istoric` | | `agenti_istoric` | rapoartele agenților automați | `POST /v1/agenti/lucru` | da, ultimele 20 pe agent | | `catering_comenzi_istoric` | fiecare stare prin care trece o comandă de catering | modulul `comenzi-catering` | da, împreună cu comenzile | | `jurnal_live` | ce au făcut verificările, colectorii și executorii | executorii, prin `POST /v1/jurnal-live` | da, doar `admin`, ultimele 400 | ## Drepturi și conturi: `acces_audit` Fiecare schimbare de acces lasă un rând. Nu există schimbare fără urmă: scrierea rândului și schimbarea sunt în aceeași tranzacție. | Câmp | Ce conține | | --- | --- | | `firma_id` | firma în care s-a făcut schimbarea | | `cine` | contul care a făcut schimbarea | | `actiune` | una din lista de mai jos | | `tinta` | contul sau rolul atins (cel mult 100 de caractere) | | `inainte`, `dupa` | valoarea veche și cea nouă, ca text | | `creat_la` | momentul scrierii | Acțiunile permise de constrângerea din baza de date: | Acțiune | Când apare | | --- | --- | | `acces.setat` | accesul unui om pe firmă a fost oprit sau redat | | `rol.setat` | rolul unui om s-a schimbat | | `drepturi_rol.setate` | setul de drepturi al unui rol s-a schimbat | | `om.creat` | un om nou a fost adăugat în firmă (direct sau prin invitație acceptată) | | `rol_personalizat.creat` | un rol nou al firmei | | `rol_personalizat.setat` | paginile unui rol personalizat s-au schimbat | | `drepturi_cont.setate` | drepturile proprii ale unui om s-au schimbat | | `parola.resetata` | administratorul a pus o parolă temporară | > [!WARNING] > Două acțiuni nu lasă rând în `acces_audit`: **ștergerea unui rol personalizat** (lista de acțiuni nu are o acțiune de ștergere) și **resetarea parolei prin email**, făcută de om singur. Resetarea prin email ajunge doar în jurnalul aplicației, ca rând `audit: parola resetata`. Schimbarea parolei rămâne oricum în `jurnal_parole`. Politicile RLS lasă doar citirea și adăugarea pe firma din sesiune. Aplicația nu are drept de ștergere sau de modificare pe acest tabel. ## Schimbările de date: `events` Fiecare `INSERT`, `UPDATE` sau `DELETE` pe produse, categorii și starea zilei scrie automat în `events`, printr-un trigger. La o modificare se scrie **un rând pe câmp schimbat**, cu valoarea veche și cea nouă. Colecția `firme` a fost adăugată ulterior. | Câmp | Ce conține | | --- | --- | | `seq` | numărul crescător al evenimentului | | `colectie` | `produse`, `categorii`, `stari_zi` sau `firme` | | `id`, `camp` | obiectul și câmpul schimbat | | `inainte`, `dupa` | valorile, în JSON | | `cine` | cine a făcut schimbarea (`necunoscut` dacă lipsește din conexiune) | | `sursa` | `platforma`, `dls`, `poller`, `import` sau `executor` | | `la` | momentul schimbării | Fluxul se citește în timp real, cu reluare de unde ai rămas: vezi [Fluxul de evenimente](#evenimente). Aplicația nu poate modifica, șterge sau goli tabelul. ## Parolele: `jurnal_parole` Un trigger pe `conturi` scrie un rând de fiecare dată când se schimbă hash-ul parolei, oricare ar fi calea: - `cont_id`: contul atins; - `actor` și `actor_efectiv`: rolul bazei de date care a făcut schimbarea și proprietarul funcției care a rulat-o; - `cale`: calea declarată de API sau, în lipsa ei, numele aplicației conectate; - `ip`: adresa trimisă de API sau adresa conexiunii; - `la`: momentul. Tabelul nu păstrează niciun hash, nici vechi, nici nou. O eroare la scrierea în jurnal nu blochează niciodată schimbarea parolei. Accesul public la tabel este retras, iar aplicația nu primește drept de citire. ## Celelalte urme - **`seif_jurnal`**: intrarea din seif, contul care a deschis-o (`cine`) și momentul (`la`). Citirile interne făcute de module pe server nu lasă rând. - **`zi_jurnal`**: acțiunile zilei, în forma în care le scrie exportul (`intrare` în JSON, cel mult 4096 de octeți), cu produsul, autorul și data. Numai adăugare. - **`urme_client`**: urmele din pagină sosesc în loturi. Tipurile și cheile sunt pe o listă albă în cod (`clic`, `filtru`, `export`, `formular_eroare`, `perf`, `push_afisat` și altele), deci nu există câmp pentru text liber tastat de om. Numai adăugare și citire pe firmă. - **`alerte_istoric`**: un rând pe alertă (tip, titlu, text, adresă, momentul). Cine ce alertă vede nu se păstrează aici. Se calculează la citire, după drepturile de acum. - **`agenti_istoric`**: un rând pe raportul unui agent (agent, etichetă, oră, text). Din 0243 are RLS pe firmă. Rândurile vechi fără firmă rămân în tabel, dar aplicația nu le mai vede. - **`catering_comenzi_istoric`**: starea comenzii (`noua`, `confirmata`, `in_pregatire`, `livrata`, `anulata`), momentul și contul. Rândurile se șterg odată cu comanda. - **`jurnal_live`**: categoria, nivelul (`info`, `ok`, `avertisment`, `eroare`), mesajul (cel mult 400 de caractere) și procesul. Textul trece printr-un filtru de secrete înainte de scriere. ## Cine le vede | Cine | Ce vede | | --- | --- | | orice om cu sesiune | fluxul `events` al firmei, propriile alerte, istoricul comenzilor de catering și al agenților, după drepturile paginii | | `admin` | în plus, `jurnal_live` | | nimeni din aplicație | `acces_audit`, `jurnal_parole`, `seif_jurnal`, `urme_client` | Toate tabelele de mai sus, cu excepția `jurnal_parole`, sunt izolate pe firmă prin RLS: o firmă nu poate citi urmele alteia (vezi [Securitate](#securitate)). ## Cât se păstrează Codul platformei nu are nicio ștergere programată și niciun termen de păstrare pentru aceste tabele. Pentru aplicație, `events`, `acces_audit`, `alerte_istoric`, `zi_jurnal` și `urme_client` sunt doar de adăugare. Rândurile rămân până la o intervenție făcută direct pe baza de date. > [!NOTE] > Singurele limite sunt cele de citire: 20 de rapoarte pe agent, 400 de rânduri în Jurnal live și un rând pe grup în istoricul alertelor. Rândurile mai vechi rămân în tabel. --- # Leagă un asistent prin MCP Ghid de la cap la coadă: adaugi platforma ca conector în Claude, dai acordul pe firme, verifici legătura și știi cum o oprești. ## Ce obții La final, asistentul tău (de exemplu Claude) poate răspunde la întrebări ca „ce produse am oprite azi?” sau „cât costă ciorba pe Glovo?”, citind direct din platformă. Asistentul **doar citește**: nu poate schimba prețuri, produse sau alte date. Vede numai firmele pe care le bifezi tu și numai ce ai vedea tu în aplicație. Îți trebuie: - un cont în platformă, cu rol pe firma pe care vrei s-o legi; - un asistent care acceptă conectori MCP la distanță, cu autorizare OAuth (Claude pe web, pe desktop sau Claude Code). Adresa serverului, pe care o vei lipi în asistent: ```text title="Adresa serverului MCP" https://app.ilustromania.com/nucleu/v1/mcp ``` ## Pașii ### Adaugă conectorul în asistent În Claude, deschide setările conectorilor și adaugă un **conector personalizat** cu adresa de mai sus. Nu ai nimic de completat la „client” sau „secret”: asistentul se înregistrează singur. În Claude Code, adaugă serverul cu transport HTTP, apoi pornește autentificarea din lista serverelor MCP: ```bash title="Claude Code" claude mcp add --transport http platforma https://app.ilustromania.com/nucleu/v1/mcp ``` ### Lasă asistentul să găsească autorizarea La prima cerere fără jeton, serverul răspunde `401` cu antetul `WWW-Authenticate`, care arată spre metadatele OAuth. De acolo asistentul află singur unde se înregistrează, unde cere acordul și unde primește jetonul. Detaliile sunt în [OAuth 2.1](#oauth). Platforma acceptă două feluri de adrese de întoarcere: adresa conectorului Claude și o adresă locală cu port (`http://localhost:/…` sau `http://127.0.0.1:/…`), cum folosește Claude Code. Orice alt client este refuzat la înregistrare. ### Dă acordul Se deschide pagina platformei **„Permiți accesul?”**. Intră în cont dacă nu ești deja. Pagina arată: - numele aplicației care cere accesul și adresa spre care pleacă accesul; - dreptul cerut: „Citește meniul și starea, nu modifică nimic.”; - firmele tale, **toate bifate la început**. Debifează firmele pe care nu vrei să le vadă asistentul. Numele aplicației îl declară chiar aplicația, platforma nu îl verifică. Apasă **Permite** doar dacă tu ai pornit conectarea, chiar acum. Altfel, apasă **Refuză**. Pagina de acord e valabilă 10 minute. Dacă a expirat, pornește din nou conectarea din asistent. ### Verifică legătura Cere-i asistentului ceva simplu, de exemplu „ce firme vezi în platformă?”. Asistentul cheamă unealta `firme_acord_listeaza` și îți arată firmele bifate la acord. Dacă ai bifat **mai multe firme**, asistentul trebuie să spună la fiecare întrebare despre ce firmă e vorba. Dacă uită, primește mesajul: „Acordul are mai multe firme: cheamă întâi unealta firme_acord_listeaza și dă id-ul firmei în parametrul firma.” Cu o singură firmă bifată, nu e nevoie de nimic în plus. ### Folosește-l Uneltele disponibile sunt în [Server MCP](#mcp): produsele și categoriile meniului, un produs anume, prețurile pe Wolt, Bolt și Glovo și produsele oprite într-o zi. Asistentul alege singur unealta potrivită întrebării tale. ## Cât ține legătura | Ce | Cât | | --- | --- | | Pagina de acord | 10 minute | | Jetonul de acces | 1 oră | | Jetonul de reîmprospătare | 30 de zile, înlocuit cu unul nou la fiecare folosire | Asistentul își reînnoiește singur jetonul de acces. Dacă nu folosește legătura 30 de zile, îți va cere din nou acordul. ## Cum oprești accesul - **Scoate conectorul din asistent.** Asistentul nu mai cere date. - **Revocă jetonul.** Aplicația care are jetonul îl poate anula cu `POST /v1/oauth/revoca`, cu câmpurile `token` și `client_id`. - **Retrage rolul pe firmă.** Rolul se verifică la fiecare apel. Dacă un om nu mai are rol pe o firmă, asistentul lui nu mai primește nimic de la acea firmă, chiar dacă jetonul e încă valabil. ## Dacă ceva nu merge | Ce vezi | Ce înseamnă | Ce faci | | --- | --- | --- | | Asistentul spune că serverul cere autentificare | nu are încă jeton sau jetonul a expirat | pornește din nou conectarea din asistent | | „Cererea de conectare nu este valida.” | linkul de autorizare e greșit sau a expirat | pornește din nou de la asistent, nu din istoricul browserului | | „Contul tău nu are nicio firmă la care să dai acces.” | contul tău nu are rol pe nicio firmă | cere unui administrator al firmei un rol | | „Firma nu e în acest acord sau nu mai ai acces la ea.” | firma nu a fost bifată sau ți s-a retras rolul | refă conectarea și bifează firma | | Eroarea `-32001` („ocupat”) | asistentul a pornit mai mult de 4 apeluri deodată | așteaptă câteva secunde și întreabă din nou | | Răspuns care se termină cu `[TRUNCHIAT: …]` | rezultatul avea peste 100 000 de caractere | cere o listă mai scurtă, cu un filtru | | `429` | prea multe cereri fără jeton de pe aceeași adresă (peste 120 în 10 minute) | așteaptă cât spune antetul `Retry-After` | > [!NOTE] > Legătura merge doar cu jetonul `mcp_…` primit prin acord. Sesiunea din browser nu este acceptată pe `/v1/mcp`, deci nu poți lega un asistent copiind cookie-ul din aplicație. --- # Server MCP Leagă Claude sau alt asistent compatibil MCP de platformă, ca să citească meniul și prețurile firmei tale. ## Ce este Platforma are un server [Model Context Protocol](https://modelcontextprotocol.io) la adresa: ```text title="Adresa serverului" https://app.ilustromania.com/nucleu/v1/mcp ``` Un asistent conectat aici primește un set de **unelte doar de citire**. Uneltele citesc aceleași date ca rutele platformei, cu aceleași drepturi (`firme_acord_listeaza` o servește direct serverul MCP): asistentul nu poate vedea nimic din ce nu ai vedea tu în aplicație. > [!NOTE] > Toate uneltele sunt de citire. Serverul MCP nu poate schimba prețuri, produse sau date ale firmei. ## Conectarea 1. În asistent (de exemplu Claude), adaugă un conector personalizat cu adresa de mai sus. 2. Asistentul descoperă singur serverul de autorizare prin metadatele OAuth și se înregistrează ca client (vezi [OAuth](#oauth)). 3. Te trimite pe pagina de acord a platformei, unde intri în cont și alegi **firmele** la care îi dai acces. 4. După acord, asistentul primește un jeton `mcp_…` și îl trimite la fiecare apel. ## Protocolul | Caracteristică | Valoare | | --- | --- | | Transport | HTTP, `POST /v1/mcp`, mesaje JSON-RPC 2.0, răspuns JSON (fără flux SSE) | | Autentificare | `Authorization: Bearer mcp_…`; cookie-ul de sesiune nu este acceptat | | Versiuni | `2025-06-18` (implicită), `2025-03-26`, `2024-11-05` | | Capabilități | `tools` | | Metode | `initialize`, `ping`, `tools/list`, `tools/call` | | Notificări | răspuns `202` fără corp | | `GET /v1/mcp` | `405`, antet `Allow: POST` | ```bash title="Lista uneltelor" curl https://app.ilustromania.com/nucleu/v1/mcp \ -H "Authorization: Bearer $JETON_MCP" \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' ``` ```json title="Apelul unei unelte" { "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "meniu_produse_listeaza", "arguments": { "firma": "", "q": "ciorbă" } } } ``` ## Uneltele | Unealtă | Ce întoarce | | --- | --- | | `firme_acord_listeaza` | firmele la care ai dat acces acestui client și pe care mai ai rol (doar id și nume); id-ul se dă ca parametrul `firma` celorlalte unelte | | `meniu_produse_listeaza` | produsele meniului, cu filtre (stare, categorie, text) și paginare de cel mult 200 | | `meniu_produs_citeste` | un produs al meniului după identificator | | `meniu_categorii_listeaza` | categoriile meniului firmei | | `meniu_preturi_canal_citeste` | prețurile pe Wolt, Bolt și Glovo, cu filtre și paginare de cel mult 5000 | | `meniu_zi_opriri_listeaza` | produsele oprite într-o zi; fără dată, ziua de azi | | `agenti_executori_listeaza` | agenții din registrul platformei, cu starea lor | | `claude_sesiuni_stare_citeste` | starea sesiunilor Claude din ultimele 24 de ore | | `acces_roluri_listeaza` | rolurile platformei și drepturile fiecăruia (doar nume, fără persoane) | ## Siguranță și limite - Rolul tău se citește **live** la fiecare apel. Dacă rolul îți este retras într-o firmă, uneltele refuză imediat acea firmă, chiar dacă jetonul este încă valid. - Răspunsul unei unelte are cel mult 100 000 de caractere; un apel durează cel mult 20 de secunde. - Pe același jeton rulează cel mult 4 apeluri `tools/call` deodată; al cincilea primește eroarea JSON-RPC `-32001` (ocupat). - Fără jeton valid, serverul primește cel mult 120 de cereri la 10 minute de pe aceeași adresă IP, apoi răspunde `429`. - Jetonul se poate revoca oricând (`POST /v1/oauth/revoca`). --- # OAuth 2.1 Fluxul prin care o aplicație externă primește acces la platformă în numele unui om, cu PKCE, acord pe firme și jetoane rotite. ## Pe scurt Platforma este propriul ei server de autorizare. Respectă profilul OAuth 2.1 cerut de MCP: - descoperire prin metadate (RFC 8414 și RFC 9728); - înregistrare dinamică a clienților (RFC 7591); - cod de autorizare cu **PKCE `S256`** obligatoriu; - jeton de reîmprospătare **rotit** la fiecare folosire. ## Metadatele ```bash title="Descoperirea serverului" GET /.well-known/oauth-authorization-server GET /.well-known/oauth-protected-resource ``` Există și variantele cu sufix (`/.well-known/oauth-authorization-server/v1/oauth`, `/.well-known/oauth-protected-resource/v1/mcp`), cerute de unii clienți MCP. ## Fluxul complet ### Înregistrarea clientului Clientul trimite `POST /v1/oauth/inregistreaza` cu `redirect_uris` și numele lui și primește un `client_id`. Sunt acceptate cel mult 5 adrese de întoarcere, fiecare de cel mult 512 caractere, și doar două feluri: adresa conectorului Claude (`https://claude.ai/api/mcp/auth_callback`) sau o adresă locală cu port explicit (`http://localhost:/…`, `http://127.0.0.1:/…`). Orice altă adresă primește `invalid_redirect_uri`. ### Autorizarea Clientul deschide `GET /v1/oauth/autorizeaza` cu `response_type=code`, `client_id`, `redirect_uri`, `state`, `code_challenge` și `code_challenge_method=S256`. Parametrul `resource` e opțional; dacă îl trimiți, trebuie să fie exact valoarea `resource` din `/.well-known/oauth-protected-resource`. Orice nepotrivire oprește fluxul cu o pagină de eroare, fără redirecționare. ### Acordul Omul ajunge pe pagina de acord a platformei, intră în cont, vede cine cere accesul și bifează firmele. Alege **Permite** sau **Refuză**. ### Codul La permite, browserul se întoarce la `redirect_uri` cu un cod de unică folosință. La refuz, cu `error=access_denied`. ### Jetonul Clientul schimbă codul pe jetoane la `POST /v1/oauth/jeton`. ```bash title="Schimbarea codului pe jetoane" curl https://app.ilustromania.com/nucleu/v1/oauth/jeton \ -d grant_type=authorization_code \ -d code=$COD \ -d client_id=$CLIENT_ID \ -d redirect_uri=http://localhost:8765/callback \ -d code_verifier=$VERIFICATOR ``` ```json title="Răspuns" { "access_token": "mcp_…", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "…", "scope": "citire" } ``` ## Durate | Element | Durată | | --- | --- | | Cererea de autorizare (pagina de acord) | 10 minute | | Codul de autorizare | 60 de secunde, o singură folosire | | Jetonul de acces | 1 oră | | Jetonul de reîmprospătare | 30 de zile, rotit la fiecare folosire | ## Reguli PKCE | Câmp | Regula | | --- | --- | | `code_challenge_method` | doar `S256` | | `code_challenge` | exact 43 de caractere (SHA-256 în base64url) | | `code_verifier` | între 43 și 128 de caractere | ## Reîmprospătare și revocare ```bash title="Jeton nou din jetonul de reîmprospătare" curl https://app.ilustromania.com/nucleu/v1/oauth/jeton \ -d grant_type=refresh_token \ -d refresh_token=$REFRESH \ -d client_id=$CLIENT_ID ``` Jetonul de reîmprospătare folosit nu mai este valid; răspunsul aduce unul nou. Pentru a opri accesul: `POST /v1/oauth/revoca`. ## Cum sunt păstrate jetoanele Jetoanele nu se păstrează în clar: baza de date ține doar amprenta lor. Tabelele OAuth sunt scrise numai prin funcții SQL dedicate, niciodată direct de aplicație. ## Limite | Rută | Prag | Corp maxim | | --- | --- | --- | | `POST /v1/oauth/inregistreaza` | 10 / oră / IP | 2048 octeți | | `GET /v1/oauth/autorizeaza` | 60 / 10 minute / IP | — | | `POST /v1/oauth/jeton` | 120 / 10 minute / IP | 4096 octeți | | `POST /v1/oauth/revoca` | 120 / 10 minute / IP | 4096 octeți | --- # Fluxul de evenimente Fiecare schimbare din firmă devine un eveniment numerotat. Îl poți asculta în timp real, cu reluare de unde ai rămas. ## Jurnalul de evenimente Fiecare scriere importantă (un preț schimbat, un produs oprit, o firmă creată) lasă un rând în tabelul `events`. Rândurile au un număr crescător, `seq`, care dă ordinea exactă a schimbărilor din firmă. ```json title="Un eveniment" { "seq": 18342, "colectie": "produse", "id": "0b6c…", "camp": "pret", "inainte": 32, "dupa": 34, "cine": "ana@firma.ro", "sursa": "platforma", "la": "2026-11-11T10:24:05Z" } ``` | Câmp | Ce înseamnă | | --- | --- | | `seq` | numărul evenimentului, crescător | | `colectie` | ce fel de obiect s-a schimbat | | `id` | identificatorul obiectului | | `camp` | câmpul schimbat, sau `null` pentru o schimbare întreagă | | `inainte`, `dupa` | valoarea veche și cea nouă | | `cine` | cine a făcut schimbarea | | `sursa` | `platforma`, `dls`, `poller`, `import` sau `executor` | | `la` | momentul schimbării | ## Ascultarea în timp real `GET /v1/evenimente` deschide un flux [Server-Sent Events](https://html.spec.whatwg.org/multipage/server-sent-events.html) pentru firma activă. ```javascript title="În browser" const sursa = new EventSource("/nucleu/v1/evenimente", { withCredentials: true }); sursa.addEventListener("produse", (e) => { const ev = JSON.parse(e.data); console.log(ev.seq, ev.camp, ev.inainte, "→", ev.dupa); }); ``` ```text title="Cum arată fluxul" : ok id: 18342 event: produse data: {"seq":18342,"colectie":"produse",...} : ping ``` - Fiecare cadru are `id` egal cu `seq`, iar `event` egal cu `colectie`. - La 15 secunde fără evenimente vine un comentariu `: ping`, care ține conexiunea deschisă. - Fluxul citește singur jurnalul cel puțin o dată la 5 secunde, deci un semnal pierdut întârzie un eveniment, dar nu îl pierde. ## Reluarea de unde ai rămas La reconectare, browserul trimite singur antetul `Last-Event-ID`, iar fluxul continuă cu evenimentele de după el. Poți da cursorul și explicit: ```bash title="Reluare de la un seq" GET /v1/evenimente?dupa=18342 ``` Fără cursor, fluxul pornește de la capăt: istoricul îl citești o dată cu rutele obișnuite, apoi asculți doar ce vine. ## Alte fluxuri | Rută | Ce transmite | | --- | --- | | `GET /v1/eu/flux` | notificările contului tău | | `GET /v1/chat/flux` | mesajele din chat | | `GET /v1/claude/taskuri/flux` | lista de taskuri, la fiecare schimbare | ## Webhook-uri Fluxul de mai sus cere o conexiune deschisă și o sesiune. Pentru un server care trebuie doar să afle când se emite o factură, un aviz, o ofertă sau vine o comandă de catering, platforma trimite webhook-uri semnate după [Standard Webhooks](https://www.standardwebhooks.com), cu reîncercări și jurnal al livrărilor: vezi [Webhook-uri](#webhook-uri). --- # Webhook-uri Platforma anunță serverul tău când se emite o factură, un aviz, o ofertă sau vine o comandă de catering: cereri POST semnate după Standard Webhooks, cu reîncercări și jurnal al livrărilor. ## Cum funcționează Adaugi o adresă `https` a serverului tău și alegi evenimentele care te interesează. Când se întâmplă unul dintre ele, platforma trimite la adresa ta o cerere `POST` cu un corp JSON scurt, semnată cu un secret pe care îl primești la adăugare. Serverul tău verifică semnătura, răspunde `2xx` și, dacă are nevoie de detalii, citește documentul prin API. Formatul urmează specificația [Standard Webhooks](https://www.standardwebhooks.com): antetele `webhook-id`, `webhook-timestamp`, `webhook-signature` și semnătură HMAC-SHA256. Bibliotecile oficiale Standard Webhooks pot verifica semnătura direct. Evenimentul se scrie în aceeași tranzacție cu documentul: dacă documentul nu se salvează, nu pleacă nici evenimentul. Trimiterea o face o sarcină din fundal, la câteva secunde după emitere, niciodată în cererea omului care a emis documentul. ## Evenimentele | Eveniment | Când se trimite | `data` | | --- | --- | --- | | `factura.emisa` | s-a emis o factură (din documente sau dintr-un bon) | `id`, `serie`, `numar` | | `aviz.emis` | s-a emis un aviz de însoțire | `id`, `serie`, `numar` | | `oferta.creata` | s-a creat o ofertă | `id`, `an`, `numar` | | `comanda_catering.primita` | a venit o comandă de catering | `id` | | `test.ping` | doar la „Trimite test” | `mesaj` | `data` conține doar identificatori, nu date personale sau sume. Ca să afli restul, citești documentul prin API. ```json title="Corpul unei livrări (JSON compact, pe un singur rând)" {"type":"factura.emisa","timestamp":"2026-11-12T09:30:00.000Z","data":{"id":"3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f","serie":"A","numar":1042}} ``` ```http title="Antetele unei livrări" POST /webhook HTTP/1.1 Content-Type: application/json webhook-id: msg_2f1c0d3e4a5b4c2b9b1a8f14e45fea8e webhook-timestamp: 1794475800 webhook-signature: v1,xvrLd4URs9sqyiPEOJPIQbrLYrXbWMUwTlIJXh4lqBU= ``` ## Adaugă o destinație Destinațiile se gestionează doar de un **Administrator al firmei**, din sesiune. Pe o firmă unde livrarea webhook-urilor nu e pornită, rutele răspund `409` cu `{"eroare": "webhookuri_oprite_pe_firma"}`. ### Trimite adresa și evenimentele ```bash title="Adăugare" curl -X POST https://app.ilustromania.com/nucleu/v1/webhookuri \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{"url": "https://exemplu.ro/webhook", "evenimente": ["factura.emisa", "aviz.emis"], "descriere": "Contabilitate"}' ``` | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `url` | `string` | da | cel mult 2048 de caractere; doar `https`, pe portul 443, fără utilizator și parolă în adresă, fără adrese locale sau private | | `evenimente` | `string[]` | da | unul sau mai multe din `factura.emisa`, `aviz.emis`, `oferta.creata`, `comanda_catering.primita` | | `descriere` | `string` | nu | cel mult 200 de caractere | O adresă refuzată dă `400` cu `{"eroare": "url_interzis"}`; un corp greșit, `400` cu `{"eroare": "corp_invalid"}`. ### Salvează secretul Răspunsul `201` are forma `{"destinatie": {…}, "secret": "whsec_…"}`. Secretul **se vede doar acum**; platforma îl ține criptat în Seif și nu îl mai arată. Pune-l într-o variabilă de mediu a serverului care primește. ### Trimite un test ```bash title="Test" curl -X POST https://app.ilustromania.com/nucleu/v1/webhookuri/$ID_DESTINATIE/test \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspunsul este `202`; în câteva secunde serverul tău primește un `test.ping`. Rezultatul apare în jurnalul livrărilor. ## Verifică semnătura Semnătura este HMAC-SHA256 peste textul `..`, cu cheia dată de partea base64 de după `whsec_`, codată în base64 și prefixată cu `v1,`. Ca să o verifici: 1. Citește corpul **exact cum a venit**, ca text sau octeți, înainte de orice parsare JSON. Un corp parsat și scris din nou nu mai are aceeași semnătură. 2. Refuză cererea dacă `webhook-timestamp` e la mai mult de 5 minute de ora ta: așa nu poate fi retrimisă mai târziu de altcineva. 3. Calculează semnătura și compar-o, în timp constant, cu fiecare valoare `v1,…` din `webhook-signature`. Valorile sunt despărțite prin spațiu; ajunge să se potrivească una. ```javascript title="Node.js (Express)" import { createHmac, timingSafeEqual } from "node:crypto"; import express from "express"; const SECRET = process.env.WEBHOOK_SECRET; // whsec_… const TOLERANTA_S = 5 * 60; function verifica(secret, antete, corp) { const id = antete["webhook-id"]; const ts = Number(antete["webhook-timestamp"]); const semnaturi = String(antete["webhook-signature"] ?? ""); if (!id || !Number.isInteger(ts) || Math.abs(Date.now() / 1000 - ts) > TOLERANTA_S) return false; const cheie = Buffer.from(secret.replace(/^whsec_/, ""), "base64"); const asteptata = createHmac("sha256", cheie).update(`${id}.${ts}.${corp}`).digest(); return semnaturi.split(" ").some((parte) => { const [versiune, valoare] = parte.split(",", 2); if (versiune !== "v1" || !valoare) return false; const primita = Buffer.from(valoare, "base64"); return primita.length === asteptata.length && timingSafeEqual(primita, asteptata); }); } const app = express(); // corpul brut: semnătura se calculează pe textul exact primit app.post("/webhook", express.raw({ type: "application/json" }), (cerere, raspuns) => { const corp = cerere.body.toString("utf8"); if (!verifica(SECRET, cerere.headers, corp)) return raspuns.sendStatus(400); const eveniment = JSON.parse(corp); // ... pune evenimentul într-o coadă și răspunde repede raspuns.sendStatus(204); }); app.listen(3000); ``` ```python title="Python (Flask)" import base64 import hashlib import hmac import os import time from flask import Flask, abort, request SECRET = os.environ["WEBHOOK_SECRET"] # whsec_… TOLERANTA_S = 5 * 60 def verifica(secret: str, antete, corp: bytes) -> bool: id_ = antete.get("webhook-id", "") ts = antete.get("webhook-timestamp", "") semnaturi = antete.get("webhook-signature", "") if not id_ or not ts.isdigit() or abs(time.time() - int(ts)) > TOLERANTA_S: return False cheie = base64.b64decode(secret.removeprefix("whsec_")) asteptata = hmac.new(cheie, f"{id_}.{ts}.".encode() + corp, hashlib.sha256).digest() for parte in semnaturi.split(" "): versiune, _, valoare = parte.partition(",") if versiune == "v1" and valoare: try: primita = base64.b64decode(valoare) except ValueError: continue if hmac.compare_digest(primita, asteptata): return True return False app = Flask(__name__) @app.post("/webhook") def webhook(): corp = request.get_data() # octeții exacți, înainte de JSON if not verifica(SECRET, request.headers, corp): abort(400) eveniment = request.get_json() # ... pune evenimentul într-o coadă și răspunde repede return "", 204 ``` **Un exemplu calculat, ca să-ți probezi codul** Cu secretul de probă format din `whsec_` urmat de `Y2hlaWUtZGUtcHJvYmEtcGVudHJ1LWRvY3VtZW50YXRpZSEh` (nu e al nimănui; partea de după prefix este base64 pentru textul `cheie-de-proba-pentru-documentatie!!`), `webhook-id` = `msg_2f1c0d3e4a5b4c2b9b1a8f14e45fea8e`, `webhook-timestamp` = `1794475800` și corpul de mai sus, exact pe un rând, semnătura corectă este: ```text v1,xvrLd4URs9sqyiPEOJPIQbrLYrXbWMUwTlIJXh4lqBU= ``` Momentul `1794475800` este fix (12.11.2026, 09:30 UTC), deci funcțiile de mai sus îl refuză din cauza ferestrei de 5 minute; pentru probă, scoate temporar verificarea timpului. Un singur spațiu în plus în corp schimbă semnătura. > [!TIP] > Răspunde repede, cu `2xx`, și fă munca grea după aceea. Platforma așteaptă cel mult 15 secunde; peste acest timp livrarea se consideră eșuată și se reîncearcă. ## Livrări, reîncercări și dubluri | Răspunsul serverului tău | Ce face platforma | | --- | --- | | `2xx` | livrat | | `410 Gone` | oprește destinația; nu mai trimite nimic | | `3xx` (redirecționările nu se urmează), alt cod, eroare de rețea, peste 15 s | eșec: reîncearcă după pauza următoare | Pauzele dintre încercări sunt **5 s, 5 min, 30 min, 2 h, 5 h, 10 h, 14 h, 20 h, 24 h**, fiecare cu o abatere mică (până la +10%), deci cel mult 10 încercări în aproximativ trei zile și jumătate. Un antet `Retry-After` (secunde sau dată HTTP) poate lungi pauza, până la 24 de ore, dar nu o scurtează. După a zecea încercare eșuată, destinația se oprește, iar Administratorii firmei primesc o notificare. - **Aceeași livrare are mereu același `webhook-id`** și același corp, la fiecare încercare și la „Retrimite”. Doar `webhook-timestamp` și semnătura se refac. - **Pot apărea dubluri:** dacă procesul se oprește în timpul unei trimiteri, livrarea se reia. Păstrează `webhook-id`-urile primite și ignoră-le pe cele deja tratate. - **Ordinea nu e garantată.** Folosește `timestamp` din corp sau citește documentul prin API, nu ordinea sosirii. - Corpul răspunsului tău nu se citește: contează doar codul și `Retry-After`. ## Rotirea secretului `POST /v1/webhookuri/{id}/roteste-secret` întoarce `{"destinatie": {…}, "secret": "whsec_…"}` cu un secret nou. Timp de **24 de ore** după rotire, fiecare livrare poartă două semnături în `webhook-signature`, despărțite prin spațiu: cu secretul nou și cu cel vechi. Ai deci o zi să pui secretul nou pe server; codul de verificare de mai sus le acceptă pe amândouă fără schimbări. ## Toate rutele | Rută | Ce face | | --- | --- | | `GET /v1/webhookuri` | destinațiile firmei, fără secrete | | `POST /v1/webhookuri` | adaugă o destinație; `201` cu secretul, o singură dată | | `POST /v1/webhookuri/{id}/opreste` | oprește destinația; livrările în așteptare se anulează | | `POST /v1/webhookuri/{id}/roteste-secret` | secret nou; cel vechi mai semnează 24 de ore | | `POST /v1/webhookuri/{id}/test` | `202`: un `test.ping` doar spre această destinație | | `GET /v1/webhookuri/{id}/livrari` | jurnalul livrărilor destinației (cele mai noi 100) | | `POST /v1/webhookuri/livrari/{id}/retrimite` | `202`: aceeași livrare, de la încercarea 1 | Coduri comune: `401` fără sesiune, `403` fără rolul `admin`, `404` pentru o destinație a altei firme sau inexistentă, `409` cu `{"eroare": "destinatie_oprita"}` pe o destinație oprită, `503` cu `{"eroare": "seif_neconfigurat"}` când Seiful nu poate păstra secretul. O destinație oprită nu se mai pornește: adaugi una nouă. **Câmpurile unei destinații și ale unei livrări** **Destinația:** `id`, `url`, `descriere`, `evenimente`, `activ`, `dezactivat_la` și `motiv` (de exemplu `oprita_de_admin`, `destinatia_a_raspuns_410`, `livrari_esuate`), `rotire_pana` (până când mai semnează secretul vechi), `creata_la`. **Livrarea:** `id`, `destinatie_id`, `eveniment_id` (valoarea din `webhook-id`), `tip`, `incercare` (1–10), `stare` (`de_trimis`, `livrat`, `esuat`, `anulat`), `manual` (test sau retrimitere), `planificata_la`, `trimisa_la`, `cod` (codul HTTP primit), `durata_ms`, `eroare` (de exemplu `termen_depasit`, `dns_esuat`, `adresa_interna`, `conexiune_esuata`), `urmatoarea_la`. Momentele sunt ISO 8601, în UTC. ## Securitatea trimiterii Platforma trimite doar la adrese `https` pe portul 443. Numele serverului se rezolvă înainte de trimitere, iar cererea pleacă doar dacă **toate** adresele găsite sunt publice; conexiunea merge apoi exact la adresa verificată, fără o a doua căutare DNS. Nu se urmează redirecționări. Secretul de semnare stă criptat în Seif și se folosește doar la semnare. --- # Sisteme externe și executori Cum vorbește platforma cu casa de marcat DLS, cu Wolt, Bolt și Glovo, cu FGO și cu SPV-ul ANAF, prin agenți care rulează pe serverul firmei. ## Modelul: coadă și executor Platforma nu deschide singură conexiuni lungi spre sistemele externe. Lucrul se pune într-o **coadă**, iar un **executor** (un agent care rulează pe server, cu rolul `executor`) îl ia, îl face și raportează rezultatul. ```bash title="Ciclul unui executor" GET /v1/facturi-din-bon/coada # ce e de făcut POST /v1/facturi-din-bon/coada/{id}/ia # rezerv sarcina POST /v1/facturi-din-bon/coada/{id}/rezultat # raportez rezultatul ``` Avantajele modelului: - o sarcină nu se pierde dacă sistemul extern cade: rămâne în coadă și se reia; - executorul folosește un **jeton de serviciu**, niciodată sesiunea unui om; - fiecare rezultat rămâne scris pe cerere, deci o eroare ulterioară nu șterge ce s-a făcut deja. ## Sistemele legate | Sistem | Ce face platforma | Module | | --- | --- | --- | | DLS (casa de marcat) | harta produselor DLS pe canale, coada de schimbări spre casă, facturi din bon | `dls-iesire`, `dls-liste`, `facturi-din-bon` | | Wolt, Bolt, Glovo, Google | prețuri pe canal, opriri de produse, starea conexiunii fiecărui canal, reduceri Glovo | `meniu`, `tabel-produse`, `glovo` | | FGO | facturare: produse, clienți, cote TVA, curs valutar, calculul liniei | `fgo` | | SPV (ANAF e-Factura) | autorizarea firmei, sincronizarea facturilor primite, fișierele PDF și XML | `spv` | | Google Drive | pozele produselor, printr-un canal verificat cu jeton | `hook-drive` | ### Alte sisteme găsite în cod | Sistem | Ce face | Prin | | --- | --- | --- | | Pinterest | citește pinii contului la 5 minute și scrie bifa Pinterest în tabelul Produse; repunerea unui pin din copie are un mod separat, pe care timerul nu îl pornește | unitatea `pinterest-canal`, conexiunea în `tabel-produse` | | Netopia | plata cu cardul la comenzile de catering (API v2) și confirmarea plății pe `/v1/public/catering/netopia/callback`; rămâne inactivă până există cheile, iar comanda cu card rămâne la confirmare | `catering-public` | | Lark | doar pagina „Automatizări Lark”, încă fără date; nu există o legătură cu Lark în codul platformei | pagina `automatizari-lark` | | Oglinda pe NAS | copiază la 2 minute ramura de cod pe NAS, doar fast-forward, fără force; o ramură deviată nu e atinsă | unitatea `oglinda-nas` | | Finder, anunțuri | Storia, OLX, Publi24, Imobiliare, Anunțul, la 30 de minute, în `finder_anunturi` | `servicii/finder-colector`, modulul `finder` | | Finder, joburi | BestJobs, eJobs, Hipo, OLX, Undelucram, MyJob, Romjob, la 6 ore | `servicii/finder-colector`, `finder-joburi` | | Meta (Facebook, Instagram), TikTok | statisticile conturilor de social media, doar citire | executorul `social_statistici`, modulul `social` | | Resend | trimiterea emailurilor (cod de intrare, resetare); cheia vine din mediu, pe firmă | `email`, `resetare` | | Gemini | propunerile „Generează cu AI” din fișa produsului și rezerva asistentului | `meniu-ai`, `asistent` | | Codex / ChatGPT | asistentul cu unelte și generarea imaginilor, prin două punți locale | `asistent`, `meniu-ai` | | BNR | cursul valutar din XML-ul anual BNR, pentru facturi | `fgo` | | Framer | site-ul Meniului Zilei: executorul săptămânii scrie cererea spre Framer | `ops/mz-vechi` | | FGO, sincronizarea registrului | la 30 de minute, citește FGO și scrie doar facturile Registrului | `servicii/registru-sync-fgo` | Orele exacte ale fiecăruia sunt în [Ce rulează în fundal](#ce-ruleaza-in-fundal). > [!NOTE] > Rutele FGO sunt doar de citire și cer rolul `admin`. Calculul unei linii de factură nu atinge baza de date. ## Regula 503: niciodată o listă goală Când o sursă externă nu este configurată, lipsește sau trimite date în altă formă decât contractul, ruta răspunde cu `503` și un motiv. Nu răspunde niciodată cu o listă goală sau cu zero, ca să nu pară că firma „nu are nimic”. ```http title="Exemplu: stocul, când fișierul sursă lipsește" HTTP/1.1 503 Service Unavailable {"eroare": "stoc_indisponibil", "motiv": "fisier_lipsa", "fisier": "stoc.json"} ``` Fiecare modul își are codul lui (`stoc_indisponibil`, `receptii_indisponibile`, `sursa_neconfigurata`); câmpul `motiv` apare acolo unde modulul știe cauza. ## Rutele cu verificare proprie Două rute au acces `intern`: dovada nu este o sesiune, ci o verificare făcută chiar de modul (de exemplu jetonul canalului Google Drive la `POST /hook/drive`). Ele nu se folosesc din aplicații obișnuite. --- # Ce rulează în fundal Sarcinile care rulează fără un om în pagină: cele pornite din procesul API, worker-ul cozii de ieșire, executorii și unitățile programate pe serverul firmei, cu orele lor. ## Pe scurt | Unde rulează | Ce e acolo | | --- | --- | | Procesul API | trei sarcini periodice: completarea meniului, pozele lipsă, avizele automate | | Worker-ul (`aplicatii/worker`) | livrarea cozii de ieșire `evenimente_iesire` | | Executorii | programe de pe serverul firmei care iau lucru din API, cu rolul `executor` | | Unități programate | timere pe serverul firmei: verificarea canalelor, SPV, evidența, statisticile și altele | ## Tabelul orelor Orele fixe sunt date cu fusul lor. Intervalele „la fiecare” nu depind de fus: se socotesc de la ultima rulare. Coloana „În cod” spune ce scrie în unitățile din codul platformei. Coloana „Pe server” spune ce rulează de fapt pe serverul platformei, citit cu `systemctl` pe 12.11.2026. Unde cele două diferă, contează coloana „Pe server”: acolo programul a fost schimbat după nevoile restaurantului. | Sarcină | În cod | Pe server | Ce face | | --- | --- | --- | --- | | Statistici social, rularea completă | zilnic, 03:20, plus până la 2 min, ora României | instalată, dar oprită | citește Facebook, Instagram, TikTok, cu 28 de zile înapoi | | Statistici social, rularea ușoară | la 30 min | zilnic, 03:30, ora României | contul, seriile și postările din ultimele 7 zile | | Facturi primite din SPV | luni–sâmbătă, 08:30, plus până la 2 min, ora României | unitatea din cod nu e instalată; rulează în locul ei o sincronizare SPV zilnică, la 03:00, ora României, cu un program din afara codului platformei | aduce facturile primite din ANAF e-Factura | | Evidența, jobul zilnic | zilnic, 06:10, ora României | nu e instalată | import FGO și legarea automată a recurențelor | | Registru ← FGO | la minutul 07 și 37 al fiecărei ore, plus până la 1 min | nu e instalată | se amână singur când serverul e încărcat peste 6 | | Specificațiile calculatoarelor | zilnic, 03:00, ora PC 1, ca sarcină programată pe PC 1, nu pe server | nu rulează pe server: serverul doar primește fișierul scris de PC 1 | model, procesor, RAM, disc și sistem pentru fișa tehnică, doar citire, fără stare live | | Conexiunea canalelor | la 1 min | la 1 min | starea reală Wolt, Bolt, Glovo, Google, Pinterest, pentru Tabel Produse | | Verificarea canalelor | la 3 min | la 5 min | citește canalele și scrie bifele din tabelul Produse | | Pinterest | la 5 min | o dată pe oră, la minutul 20, ora României | citește pinii și scrie bifa Pinterest în tabel | | Colectorul Jurnal live | la 30 s | nu e instalată | ce au făcut timerele și executorii, spre `POST /v1/jurnal-live` | | Oglinda pe NAS | la 2 min | nu e instalată | copiază codul pe NAS, doar fast-forward | | Finder, anunțuri | la 30 min | zilnic, 03:30, ora României | colectorul de anunțuri: Storia, OLX, Publi24, Imobiliare, Anunțul | | Finder, joburi | la 6 h | instalată, dar oprită | colectorul de anunțuri de joburi | | Avize automate | după 20:00, verificat la 10 min, ora României | firmele și contul sunt setate pe API-ul de producție; dacă trecerile rulează: neverificat | copiază avizele zilei pe ziua următoare; duminica e exclusă | | Plasa pozelor lipsă | la 30 min | neverificat | pornește generarea pozei pentru produsele active fără poză | | Completarea meniului | la 60 min | neverificat | completează ce lipsește din fișele produselor | „Nu e instalată” înseamnă că unitatea nu există pe serverul platformei; dacă rulează pe altă mașină nu se poate vedea din cloud, deci acolo e **neverificat**. Ultimele trei rânduri rulează în procesul API și pornesc doar cu variabile din fișierul de setări al serverului, care nu se poate citi fără drept de administrator: de aceea sunt marcate „neverificat”. Serverul rulează pe UTC (citit pe server: `Etc/UTC`). De aceea timerele cu oră fixă își scriu fusul `Europe/Bucharest`, iar trecerea la ora de vară sau de iarnă se face singură. ## Sarcinile din procesul API Cele trei pornesc după ce API-ul începe să asculte. Fiecare e **oprită** până primește lista de firme din mediu. Trecerile nu se suprapun: dacă una încă rulează, următoarea e sărită. | Sarcină | Pornită de | Interval implicit | Condiții | | --- | --- | --- | --- | | Completarea meniului | `pornesteCompletarePeriodica` | `COMPLETARE_MENIU_MINUTE` = 60 | `COMPLETARE_MENIU_FIRME`; fără `GEMINI_API_KEY` se copiază doar de la produsul similar | | Plasa pozelor lipsă | `pornestePozeLipsaPeriodic` | `POZE_LIPSA_MINUTE` = 30 | `COMPLETARE_MENIU_FIRME` și `POZA_AUTOMATA_CONT`; cel mult `POZE_LIPSA_LIMITA` = 5 porniri pe trecere | | Avizele automate | `pornesteAvizeAutomate` | `AVIZE_AUTO_MINUTE` = 10 | `AVIZE_AUTO_FIRME` și `AVIZE_AUTO_CONT` | - **Completarea** scrie doar ce lipsește. Alergenii se scriu numai unde sunt „necompletați”, deci ce a declarat omul nu se suprascrie. Schimbările apar în `events` pe un cont tehnic, nu pe un om. - **Pozele lipsă** nu dublează o generare: un produs cu job activ sau cu o generare în ultimele 6 ore e sărit. - **Avizele** se copiază după ora 20 din București, din ultima zi cu avize, pe ziua de mâine. Duminica nu e nici zi țintă, nici sursă: luni se copiază sâmbăta. Copierea e idempotentă, deci verificarea la 10 minute nu creează dubluri. > [!NOTE] > O firmă care eșuează nu le oprește pe celelalte: eroarea se scrie în jurnalul API-ului, iar trecerea continuă. ## Worker-ul: coada de ieșire Worker-ul livrează evenimentele din tabelul `evenimente_iesire` (outbox) către o adresă dată în `TINTA_URL`. | Pas | Ce face | | --- | --- | | La pornire | numără firmele vizibile; iese cu codul `2` dacă vede 0 firme (RLS), `3` dacă nu are drept de citire, `1` la altă eroare | | O trecere | pentru fiecare firmă ia cel mult 20 de evenimente și le livrează pe rând | | Livrarea | `POST` cu `Idempotency-Key` = id-ul evenimentului; 2xx = `livrata`, altfel reîncercare | | Eșecul | după 5 încercări evenimentul devine `esuata` și nu se mai reia | | Blocajul | un eveniment rămas `in_lucru` peste 10 minute redevine eligibil | | Oprirea | la `SIGTERM` termină evenimentul în curs și iese | Pauza dintre treceri e `INTERVAL_MS`, de cel puțin 100 ms. Timpul de livrare, `TIMEOUT_MS`, e implicit 10 secunde și trebuie să fie sub cele 10 minute de expirare, altfel worker-ul nu pornește. Codul worker-ului mai are o funcție de **reconciliere**: compară prețul și disponibilitatea așteptate cu cele citite pe Wolt, Bolt și Glovo și scrie diferențele ca evenimente `reconciliere.corectie`. Funcția nu e chemată încă din bucla worker-ului, iar unitatea care pornește worker-ul nu e în codul platformei. Pe serverul platformei nu rulează nicio unitate de worker pentru coada de ieșire (citit 12.11.2026). ## Executorii Un executor este un program de pe serverul firmei care vorbește cu platforma **doar prin API**, cu un jeton de serviciu cu rolul `executor`. Jetoanele se citesc din `JETOANE_SERVICIU` și dintr-un dosar de jetoane. Un fișier stricat e sărit, ca să nu oprească API-ul. | Rută | Cine o folosește | | --- | --- | | `GET /v1/facturi-din-bon/coada`, `…/coada/{id}/ia`, `…/coada/{id}/rezultat`, `…/cereri/{id}/emis-in-dls` | executorul facturilor din bon (DLS) | | `POST /v1/spv/sincronizeaza` | timerul SPV | | `POST /v1/jurnal-live` | colectorul Jurnal live | | `POST /v1/executori/social-statistici` | colectorul de statistici social | | `POST /v1/wtso/proiecte`, `…/rulare/preia`, `…/rulare/{id}/log` | worker-ul de traducere și SEO al site-urilor | | `POST /v1/flux-lant/stare` | starea lanțului de produse | | `POST /v1/fidelitate/potrivire` | potrivirea bonurilor de fidelitate | | `POST /v1/agentie/scraper/stare` | starea scraperului agenției | O cerere trece dacă **oricare** identitate a ei are rolul cerut: un jeton de executor valid nu e umbrit de un cookie de om. Modelul cozii este descris în [Sisteme externe și executori](#sisteme-externe). ## Pornite la cerere, nu la oră | Unitate | Pornește când | | --- | --- | | Executorul săptămânii Meniului Zilei | apare o cerere nouă de săptămână sau confirmarea Framer (unitate `.path`) | | Puntea asistentului | rulează mereu și repornește la 3 s după o cădere; cel mult 2 cereri în paralel, 55 s pe cerere | | Puntea imaginilor | rulează mereu și repornește la 3 s; o cerere odată, 180 s pe cerere | Pe serverul platformei, niciuna dintre aceste trei unități nu e instalată cu numele din cod (citit 12.11.2026). Unde rulează: **neverificat**. > [!WARNING] > Patru timere sunt oprite deliberat și pornesc acum doar la cerere: executorii reducerilor Wolt și Glovo, tabul portalului și relogarea Bolt. Lipsa lor de pe server nu este o defecțiune. --- # Prezentarea API-ului Adresa de bază, autentificarea, formatul cererilor și al răspunsurilor, limitele și regulile comune tuturor rutelor. ## Adresa de bază ```text title="Adresa de bază" https://app.ilustromania.com/nucleu ``` Rutele produsului au prefixul de versiune `/v1`. Excepțiile (5) sunt descoperirea OAuth (`/.well-known/…`) și verificarea de sănătate (`/sanatate`). Exemplu: `GET https://app.ilustromania.com/nucleu/v1/agentie/creatori`. ## Autentificarea | Cine apelează | Cum se autentifică | | --- | --- | | Aplicația web (browserul) | cookie-ul de sesiune `__Host-sesiune`, pus la intrarea în cont | | Un script sau un server al firmei | o [cheie API](#chei-api): `Authorization: Bearer ilk_…`, cu rol și expirare; deocamdată doar pe rutele de citire deschise cheilor | | Un script care are nevoie de alte rute | cookie-ul de sesiune, trimis explicit în antetul `Cookie` | | Un asistent AI prin MCP | `Authorization: Bearer mcp_…`, obținut prin [OAuth](#oauth) | | Executorii de pe server | jeton de serviciu, cu rolul `executor` | ```bash title="O cerere cu sesiunea" curl https://app.ilustromania.com/nucleu/v1/agentie/creatori \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` > [!WARNING] > Jetonul de sesiune dă aceleași drepturi ca și contul tău. Nu îl pune în cod, în commituri sau în mesaje; ține-l într-o variabilă de mediu. ## Firma activă Cererea lucrează pe firma activă a sesiunii. Când ai mai multe firme deschise în taburi diferite, aplicația trimite firma tabului în antetul `x-firma-tab`. Firma se verifică pe server: nu poți alege o firmă la care nu ai acces. ## Formatul - Cererile și răspunsurile sunt JSON (`Content-Type: application/json`), în UTF-8. - Corpul cererilor de scriere este validat cu o schemă. Un câmp lipsă sau de alt tip înseamnă `400`. - Datele calendaristice sunt `AAAA-LL-ZZ`; momentele sunt ISO 8601 în UTC (`2026-11-11T10:24:05Z`). ## Răspunsurile de eroare Orice eroare are un corp cu câmpul `eroare`, iar uneori și `motiv` sau alte detalii: ```json title="O eroare" { "eroare": "interzis" } ``` Cu antetul `Accept: application/problem+json`, aceeași eroare vine în formatul standard RFC 9457, cu un `cod` stabil. Lista completă a codurilor și formatul nou sunt în [Erori](#api-erori). ## Numărul cererii Fiecare răspuns are antetul `x-request-id`. Poți trimite tu unul (8–64 de caractere: litere, cifre, `-` sau `_`) și platforma îl păstrează; altfel pune unul nou. Când raportezi o eroare, trimite acest număr. ## Scrierile repetate Câteva rute care creează documente acceptă antetul `Idempotency-Key`: o scriere trimisă din nou cu aceeași cheie nu se aplică de două ori. Detaliile sunt în [Cereri repetate fără dubluri](#idempotenta). ## Accesul fiecărei rute Fiecare rută din referință are o etichetă de acces: | Etichetă | Înseamnă | | --- | --- | | `public` | fără autentificare | | `sesiune` | orice cont intrat; modulul verifică apoi drepturile fine | | `rol:admin` | doar administratorul firmei active | | `rol:executor` | doar jetoanele de serviciu ale agenților | | `intern` | dovadă verificată de modul (canal semnat), nu de sesiune | ## Limitele Scrierile au o limită comună de 300 pe minut pe sesiune și 1200 pe minut pe IP; citirile, 1200 pe sesiune și 6000 pe IP. Fiecare scriere primește antetele `RateLimit` și `RateLimit-Policy`, cu câte cereri mai ai; peste prag, răspunsul este `429` cu `Retry-After`. Detaliile sunt în [Limite](#limite). ## Specificația OpenAPI `GET /v1/openapi.json` întoarce specificația OpenAPI a rutelor descrise cu scheme. Ruta cere rolul `admin`. Din aceeași specificație vin, în referința de aici, tabelele de parametri, de corp și de răspuns și exemplele de corp și de răspuns, pe cele 107 de rute care au schemă; celelalte au descrierea și un exemplu de cerere. ## Versiunile Versiunea curentă este `v1` și este singura. Adăugirile intră în `v1`; o schimbare care rupe ceva apare doar în `v2`, cu cel puțin 6 luni în care merg amândouă. Regula întreagă este în [Versiuni API](#versiuni); schimbările se anunță în [Noutăți](#note-versiune). --- # Erori Codurile HTTP pe care le întoarce platforma, ce înseamnă fiecare și ce faci când îl primești. ## Coduri HTTP | Cod | Înseamnă | Ce faci | | --- | --- | --- | | `200`, `201` | reușit; `201` când s-a creat ceva | — | | `202` | primit, se procesează mai târziu (de exemplu cererea de resetare a parolei) | — | | `400` | cererea nu trece de schemă sau nu se poate citi (JSON stricat, tip de conținut greșit) | corectezi câmpurile | | `401` | nu ești autentificat sau ruta nu este declarată | intri în cont din nou | | `403` | ești autentificat, dar nu ai rolul sau dreptul | ceri administratorului acces | | `404` | obiectul nu există în firma activă | verifici identificatorul și firma | | `409` | starea obiectului nu permite schimbarea (de exemplu un document emis) sau o cerere cu aceeași cheie de idempotență încă rulează | reîncarci obiectul și decizi din nou; la `idempotency_key_in_lucru`, aștepți și repeți | | `413` | corpul cererii este prea mare | trimiți mai puțin | | `422` | datele sunt corecte ca formă, dar regula de business le refuză | citești `eroare`; unele rute adaugă un câmp cu explicația (`motiv`, `detalii` sau lista elementelor refuzate) | | `429` | ai trecut de limită | aștepți `Retry-After` secunde | | `500` | eroare neprevăzută în platformă; corpul e mereu `{"eroare": "eroare interna"}`, fără detalii | reîncerci; dacă se repetă, trimiți administratorului antetul `x-request-id` al răspunsului | | `502` | serviciul extern (FGO, ANAF, un canal) a răspuns cu eroare | reîncerci mai târziu | | `503` | sursa externă nu este configurată sau lipsește | administratorul configurează sursa | > [!NOTE] > Un `404` poate însemna și că obiectul există, dar în altă firmă. Platforma nu spune niciodată dacă un obiect există într-o firmă la care nu ai acces. ## Codurile din câmpul `eroare` Cele mai frecvente valori, numărate în cod: | `eroare` | Cod HTTP obișnuit | Înseamnă | | --- | --- | --- | | `neautentificat` | `401` | lipsește sesiunea sau a expirat | | `ruta_nedeclarata` | `401` | ruta nu are acces declarat | | `interzis` | `403` | rolul sau dreptul lipsește | | `negasit` | `404` | obiectul nu există în firma activă | | `cerere_invalida` | `400` | parametri sau corp care nu trec de schemă | | `corp_invalid` | `400` | corpul cererii nu trece de schemă | | `intrare invalida` | `400` | schema rutei a respins cererea; câmpul `detalii` spune ce câmp și de ce | | `cerere invalida` | `400` (sau alt `4xx`) | cererea nu s-a putut citi: JSON stricat, corp prea mare, tip de conținut greșit | | `prea_multe_cereri` | `429` | limita unei rute speciale | | `prea multe cereri` | `429` | limita generală a sesiunii sau a adresei IP, pentru scrieri și pentru citiri | | `sursa_neconfigurata` | `503` | sursa externă nu este configurată | | `parola_comuna` | `400` | parola apare în listele de parole sparte | | `parola_anterioara` | `400` | parola este una dintre ultimele 5 ale contului | | `jeton_invalid` | `400` | jeton expirat, revocat sau greșit | | `idempotency_key_invalid` | `400` | antetul `Idempotency-Key` are caractere nepermise sau e prea lung ([detalii](#idempotenta)) | | `idempotency_key_alt_corp` | `422` | aceeași cheie de idempotență, trimisă cu alt corp | | `idempotency_key_in_lucru` | `409` | prima cerere cu aceeași cheie încă rulează | | `eroare interna` | `500` | eroare neprevăzută; detaliile rămân doar în jurnalul serverului | ## Formatul RFC 9457, la cerere Dacă trimiți antetul `Accept: application/problem+json`, orice eroare cu câmpul `eroare` vine în formatul [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457), cu `content-type: application/problem+json; charset=utf-8`. Fără antet (sau cu `q=0` pe acest tip), corpul rămâne cel de mai sus, neschimbat. ```bash title="Cererea formatului nou" curl https://app.ilustromania.com/nucleu/v1/oferte/nu-exista \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Accept: application/problem+json" ``` În formatul nou, toate câmpurile vechi rămân (inclusiv `eroare`, `detalii`, `motiv`), iar în plus apar: | Câmp | Ce conține | | --- | --- | | `type` | `urn:ilustromania:eroare:` | | `title` | titlul scurt al codului, în română | | `status` | codul HTTP, ca număr | | `cod` | codul canonic, stabil: după el compari în integrare | | `id_cerere` | același număr ca în antetul `x-request-id` | | `cod_detaliu` | doar când ruta trimitea deja un câmp `cod` (avize, emitere, oferte, tabelul de produse): valoarea lui veche | ```json title="Eroare în format RFC 9457" { "eroare": "corp_invalid", "type": "urn:…:eroare:cerere_invalida", "title": "Cerere invalidă", "status": 400, "cod": "cerere_invalida", "id_cerere": "…" } ``` ## Codurile canonice Câmpul `cod` adună mesajele vechi care spun același lucru. Un mesaj care nu e în tabel și arată deja ca un cod (litere mici, cifre și `_`, de exemplu `ruta_nedeclarata` sau `idempotency_key_alt_corp`) rămâne neschimbat; altfel codul vine din statusul HTTP. | `cod` | `title` | Status implicit | Mesajele vechi adunate aici | | --- | --- | --- | --- | | `cerere_invalida` | Cerere invalidă | `400`, `422` | `cerere_invalida`, `corp_invalid`, `date_gresite`, `date_nevalide`, `intrare invalida`, `cerere invalida`, `parametri_invalizi`, `interogare_invalida`, `id_invalid` | | `neautentificat` | Neautentificat | `401` | `neautentificat` | | `interzis` | Interzis | `403` | `interzis` | | `negasit` | Negăsit | `404` | `negasit` | | `conflict` | Conflict | `409` | — | | `prea_mare` | Prea mare | `413` | — | | `prea_multe_cereri` | Prea multe cereri | `429` | `prea multe cereri`, `prea_multe_cereri`, `prea_multe_incercari`, `prea multe incercari`, `limita_depasita` | | `eroare_interna` | Eroare internă | `500` | `eroare interna` | Un status care nu e în tabel dă `eroare_interna` de la `500` în sus și `cerere_invalida` sub `500`. Pentru codurile care nu sunt în tabel, `title` este chiar codul. > [!NOTE] > Erorile OAuth, MCP și fluxurile în timp real (SSE) nu se transformă: au formatul lor, descris mai jos. ## Erorile OAuth Rutele OAuth folosesc formatul din standard, nu câmpul `eroare`: ```json title="Eroare OAuth" { "error": "unsupported_grant_type", "error_description": "grant_type nesuportat" } ``` ## Erorile MCP Serverul MCP răspunde cu obiecte de eroare JSON-RPC 2.0 (`error.code`, `error.message`). O unealtă care eșuează întoarce rezultatul cu `isError: true`, ca asistentul să poată citi motivul. --- # Paginare și filtrare Cele trei feluri în care platforma împarte o listă lungă în pagini, ce rute folosesc fiecare fel și cât poți cere cel mult. ## Pe scurt Nu există o singură regulă de paginare pentru toată platforma: fiecare modul o alege pe a lui. În cod apar trei feluri. | Fel | Parametri | Ce primești înapoi | Exemple | | --- | --- | --- | --- | | Decalaj | `limita` + `decalaj` | lista și totalul din corp | `GET /v1/evidenta/tranzactii` | | Cursor | `limita` + `dupa` | lista și `urmator` (cursorul paginii următoare sau `null`) | `GET /v1/eu/notificari` | | Număr de pagină | `pagina` (de la 1) | lista și `urmatoarea` (`true` / `false`) | `GET /v1/finder/joburi`, `GET /v1/finder/case/anunturi` | Parametrii se trimit în query string. O cheie necunoscută sau o valoare în afara limitelor dă `400` (vezi [Erori](#api-erori)). ## Decalaj: `limita` și `decalaj` Ceri `limita` rânduri, sărind peste primele `decalaj`. | Rută | `limita` implicit | `limita` maxim | Totalul | | --- | --- | --- | --- | | `GET /v1/evidenta/tranzactii` | 50 | 500 | câmpul `total` din corp, alături de `limita` și `decalaj` | ```bash title="Pagina a doua de tranzacții, câte 50" curl -s "https://app.ilustromania.com/nucleu/v1/evidenta/tranzactii?limita=50&decalaj=50" \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### Filtrele listei de produse | Parametru | Valori | | --- | --- | | `stare` | starea produsului | | `categorie_id` | id-ul categoriei | | `q` | text căutat, 1–100 de caractere | | `arhivate` | `1` = include și produsele arhivate (implicit nu apar) | | `catering` | `1` = doar produsele de catering | | `sortare` | `nume` (implicit), `pret`, `creat_la` | | `ordine` | `asc` (implicit) sau `desc` | ### Filtrele tranzacțiilor din evidență `luna` (`AAAA-LL`), `entitate`, `tip`, `categorie`, `cauta` (cel mult 200 de caractere) și `arhivate` (`true` / `false`). Fără `luna` primești toate lunile. ## Cursor: `limita` și `dupa` Notificările se citesc de la cele mai noi spre cele vechi. Prima cerere nu are `dupa`; următoarele trimit valoarea primită în `urmator`. Când `urmator` e `null`, ai ajuns la capăt. | Parametru | Regula | | --- | --- | | `limita` | implicit 30, cel mult 100 | | `dupa` | cursorul primit, 1–200 de caractere | | `tip` | `mentiune` sau `activitate` (opțional) | ```bash title="Notificările, pagină cu pagină" curl -s "https://app.ilustromania.com/nucleu/v1/eu/notificari?limita=30" -H "Cookie: __Host-sesiune=$JETON_SESIUNE" # {"notificari":[...],"urmator":"MjAyNi0xMS0xMS..."} curl -s "https://app.ilustromania.com/nucleu/v1/eu/notificari?limita=30&dupa=MjAyNi0xMS0xMS..." -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Tratează cursorul ca pe un text opac: nu îl construi și nu îl modifica. Un cursor stricat nu dă eroare, ci întoarce prima pagină. > [!NOTE] > Paginarea cu cursor nu sare și nu dublează rânduri când apar notificări noi între două cereri. Paginarea cu decalaj poate, dacă lista se schimbă între cereri. ## Număr de pagină: `pagina` Listele modulelor Finder primesc `pagina` (de la 1) și au o mărime fixă de 50 de rânduri pe pagină. Răspunsul spune doar dacă mai există o pagină (`urmatoarea: true`), nu și câte sunt. | Rută | `pagina` maxim | | --- | --- | | `GET /v1/finder/joburi` | 100 000 | | `GET /v1/finder/case/anunturi` | 10 000 | Celelalte liste Finder (de exemplu `GET /v1/finder/case/brut`) folosesc cursorul: `limita` implicit 300 și `dupa` primit în `urmator`. ## Limitele, adunate | Rută | Implicit | Maxim | | --- | --- | --- | | `GET /v1/evidenta/tranzactii` | 50 | 500 | | `GET /v1/eu/notificari` | 30 | 100 | | listele Finder cu cursor | 300 | 500 (`brut`: 2000) | | listele Finder cu `pagina` | 50 pe pagină | — | > [!WARNING] > Peste maxim, rutele cu schemă (evidență, notificări) răspund `400`. Listele Finder cu cursor nu dau eroare: coboară singure `limita` la maxim. Nu te baza pe numărul de rânduri cerut, ci pe `urmator` sau pe total. --- # Date, ore și bani Cum arată pe fir o zi, un moment și o sumă, unde contează ora României și cum se calculează TVA-ul pe facturi. ## Pe scurt | Ce | Forma pe fir | Exemplu | | --- | --- | --- | | Zi | `AAAA-LL-ZZ` | `2026-11-11` | | Lună (evidență) | `AAAA-LL` | `2026-11` | | Moment | ISO 8601 cu fus orar | `2026-11-11T10:24:05.000Z` | | Sumă (meniu, prețuri) | text cu exact două zecimale, în lei | `"30.00"` | | Sumă (finanțe, evidență) | text cu exact două zecimale | `"4635.00"` | | Monedă (finanțe, facturi) | `RON`, `EUR` sau `USD` | `"RON"` | | Cotă TVA pe factură | `0`, `11` sau `21` | `21` | ## Zile și momente - O **zi** este o dată calendaristică fără oră: `AAAA-LL-ZZ`. - Un **moment** este ISO 8601 cu fus orar. Schemele acceptă orice decalaj (`Z`, `+02:00`, `+03:00`); serverul scrie de regulă în UTC, cu `Z` la final. - Excepție: lista de rapoarte (`GET /v1/rapoarte`) dă momentul fișierului cu decalajul orei României (`+02:00` iarna, `+03:00` vara). > [!NOTE] > Citește momentele ca momente, nu ca text: compară-le după ce le transformi în dată, ca un `+03:00` și un `Z` să fie tratate corect. ## Ora României și UTC Serverul rulează pe UTC. Tot ce înseamnă „azi” pentru restaurant se calculează însă pe ora României (`Europe/Bucharest`), cu trecerea la ora de vară făcută automat. | Unde | Ce înseamnă „ziua” | | --- | --- | | Meniul zilei, reducerile și produsele oprite azi | ziua curentă la București | | Ofertele | data de azi la București | | Avizele | după ora 20 la București se generează avizele zilei următoare | | Comenzile publice de catering | ziua de livrare se socotește la București, cel mult 90 de zile înainte | | Evidența zilnică | ziua României, nu ziua UTC | ### Sarcinile programate Codul platformei pune ora României pe fiecare sarcină cu oră fixă. Ce rulează de fapt pe serverul platformei s-a citit pe 12.11.2026 și e în [Ce rulează în fundal](#ce-ruleaza-in-fundal). | Sarcină | În cod | Pe server | | --- | --- | --- | | Evidența zilnică | în fiecare zi, la 06:10 ora României | nu e instalată | | Facturile din SPV (ANAF) | de luni până sâmbătă, la 08:30 ora României | o sincronizare SPV zilnică, la 03:00 ora României | | Statisticile complete ale rețelelor sociale | în fiecare zi, la 03:20 ora României | instalată, dar oprită | | Sincronizarea registrului cu FGO | la minutul 07 și 37 al fiecărei ore | nu e instalată | ## Bani Sumele se trimit ca **text cu exact două zecimale**, cu punct: `"30.00"`, `"2.50"`. Nu sunt bani întregi (nu `3000`) și nu sunt numere zecimale (nu `30.0`). Motivul: în baza de date sumele sunt `numeric`, iar textul trece neschimbat de la bază la pagină, fără rotunjirile unui număr cu virgulă mobilă. | Tip | Forma | Plaja | | --- | --- | --- | | Sumă de meniu (`Bani`) | `^\d{1,8}\.\d{2}$` | 0.00 – 99999999.99 | | Sumă financiară | `^\d{1,12}\.\d{2}$` | până la 12 cifre înainte de punct | | Sumă din evidență | ca suma financiară, dar mai mare ca zero | — | ```json title="Corect și greșit" { "pret": "30.00" } // corect { "pret": "30" } // 400: lipsesc zecimalele { "pret": 30.0 } // 400: număr, nu text ``` > [!WARNING] > Nu toate modulele folosesc textul. Liniile facturilor și ale documentelor fiscale primesc `cantitate` și `pret` ca **numere** JSON, iar prețul poate avea cel mult 4 zecimale. Verifică schema rutei în [referința API](#api-prezentare). ## TVA pe facturi Ciornele de factură și proformă (`facturi-documente`) au TVA pe fiecare linie: - cota se alege pe linie din `0`, `11` sau `21`; serverul nu pune o cotă implicită; - clientul trimite doar `cantitate`, `pret` și `cota_tva`; valoarea, TVA-ul și totalurile le calculează serverul; - valoarea liniei = `cantitate × pret`, rotunjită la ban; TVA-ul = valoare × cotă, rotunjit la ban; - totalurile sunt suma liniilor: `total_fara_tva`, `total_tva`, `total`; - un preț negativ înseamnă discount, dar baza pe fiecare cotă trebuie să rămână cel puțin zero; altfel `422` cu motivul `baza_negativa_cota_`. Serviciile din catalogul de clienți au o `cotaTva` între 0 și 100, aleasă de firmă. ## Puncte de fidelitate Un punct la fiecare leu, rotunjit în jos: `85,05` lei dau 85 de puncte, `20,00` lei dau 20. --- # Încărcări și mărimi Cât de mare poate fi o cerere, cum se încarcă o poză sau un document, ce tipuri de fișier se acceptă și unde ajung fișierele. ## Pe scurt | Ce trimiți | Rută | Cum | Mărime maximă | Tipuri | | --- | --- | --- | --- | --- | | Corp JSON obișnuit | orice rută | `application/json` | 1 MiB (1 048 576 octeți) | — | | Poza de profil | `PUT /v1/profil/poza` | `multipart/form-data` | 5 MB | JPEG, PNG, WebP | | Poza unui angajat | `PUT /v1/angajati/:id/poza` | `multipart/form-data` | 5 MB | JPEG, PNG, WebP | | Poza unei fișe „Vânzare Produse SH” | `POST /v1/vanzare-sh/poze` | `multipart/form-data` | 15 MB | JPEG, PNG, WebP | | Factură primită, de scanat | `POST /v1/facturi/scanare` | JSON, fișier în base64 | 9 000 000 de caractere base64 | JPEG, PNG, WebP, PDF | | Document atașat unei tranzacții | `POST /v1/evidenta/tranzactii/:id/documente` | `multipart/form-data` | 10 MB | PDF, JPEG, PNG, WebP | | Import de document din DLS | `POST /v1/facturi-din-bon/importa` | JSON | 10 MB | — | Un MB aici înseamnă 1 048 576 de octeți. ## Corpul JSON Serverul nu își schimbă limita implicită a cadrului (Fastify): un corp mai mare de 1 MiB primește `413`. Doar rutele care au nevoie de mai mult își declară propria limită, ca în tabelul de mai sus. ```json title="Răspunsul la un corp prea mare" { "eroare": "cerere invalida" } ``` ## Încărcări `multipart/form-data` Pozele de profil, pozele angajaților, pozele „Vânzare Produse SH” și documentele din evidență se trimit ca formular `multipart/form-data`, cu **un singur fișier**. Serverul ia primul câmp care are `filename`; restul se ignoră. ```bash title="Poza de profil" curl -X PUT https://app.ilustromania.com/nucleu/v1/profil/poza \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -F "poza=@eu.jpg" ``` Tipul se recunoaște **după primii octeți ai fișierului**, nu după extensie și nici după tipul declarat în cerere. Un `.jpg` care nu e de fapt JPEG este refuzat. | Răspuns | Când | | --- | --- | | `400 fara_poza` / `400 fara_fisier` | corpul nu are niciun fișier | | `400 nu_e_imagine` / `400 tip_neacceptat` | fișierul nu e de un tip acceptat | | `413 poza_prea_mare` / `413 prea_mare` | fișierul trece de limită | | `403 interzis` | nu ai acces la firmă sau dreptul de scriere | ## Încărcări în base64 Pozele produselor și facturile de scanat se trimit în JSON, cu fișierul codat base64. Base64 crește mărimea cu o treime, de aceea limita corpului e mai mare decât a fișierului (21 MB de corp pentru 15 MB de poză). ```json title="Poza unui produs" { "continut": "", "nume": "ciorba.jpg" } ``` ```json title="Factură de scanat" { "tip": "application/pdf", "date": "JVBERi0xLjcK...", "nume_fisier": "factura.pdf" } ``` - La scanare, `tip` trebuie să se potrivească cu octeții fișierului; altfel `415 tip_nepotrivit`. Un PDF trebuie să înceapă cu `%PDF` (în base64, `JVBERi0`). - La pozele de produs, doar sloturile pozelor reale primesc fișier de mână; un slot AI dă `400 slot_fara_upload`. - O poză de produs pe care serverul nu o poate citi dă `400 poza_refuzata`, cu motivul în `mesaj`. ## Ce face serverul cu poza - **Poza de produs** se transformă în WebP (calitate 80), în două mărimi, cu fundal alb; originalul se păstrează separat. - **Poza „Vânzare Produse SH”** se transformă în JPEG (calitate 85). - Imaginile de peste 80 000 000 de pixeli sunt refuzate, oricât de mic ar fi fișierul. ## Unde se păstrează fișierele | Fișier | Unde | Observații | | --- | --- | --- | | Poza de profil și a angajaților | pe disc, în dosarul pozelor de profil (`PROFIL_POZE_DIR`, implicit `/var/lib/platforma/profil-poze`) | nume aleator; la „Scoate poza” fișierul se mută în arhivă, nu se șterge | | Poza „Vânzare Produse SH” | pe disc, `VANZARE_SH_POZE_DIR` (implicit `/var/lib/platforma/profil-poze/vanzare-sh`) | — | | Pozele produselor | pe disc: dosarul servit, dosarul originalelor și coșul (`/srv/poze-produs`, `/srv/poze-produs-orig`, `/srv/poze-produs-cos`) | ștergerea mută în coș | | Factura scanată | pe disc, sub `FACTURI_DOCUMENTE_DIR`: întâi temporar 2 ore, apoi la aprobare în `/facturi_primite//` | calea o alege serverul, niciodată clientul | | Documentele din evidență | în baza de date | fără ștergere: „scoate” doar le arhivează | > [!NOTE] > Pozele de profil și ale angajaților se scriu pe disc abia după ce serverul a verificat accesul la firmă. O cerere fără drept nu lasă niciun fișier în urmă. --- # Cereri repetate fără dubluri Cum trimiți din nou o scriere după o eroare de rețea fără să creezi același document de două ori: antetul Idempotency-Key. ## La ce folosește Când o cerere care creează ceva (o ofertă, un document, o tranzacție) rămâne fără răspuns din cauza rețelei, nu știi dacă a ajuns. Dacă o trimiți din nou, poți crea același lucru de două ori. Cu antetul `Idempotency-Key`, a doua trimitere cu aceeași cheie nu mai rulează operația: primești răspunsul salvat de la prima. Antetul urmează propunerea IETF [draft-ietf-httpapi-idempotency-key-header-08](https://datatracker.ietf.org/doc/draft-ietf-httpapi-idempotency-key-header/). Este opțional: fără el, ruta merge exact ca înainte. ## Rutele care îl acceptă Antetul contează doar pe rutele de mai jos (9). Pe celelalte este ignorat. În referința API, aceste rute au mențiunea „acceptă `Idempotency-Key`”. | Metodă | Cale | | --- | --- | | `POST` | [`/v1/avize`](#api-facturari--post-v1-avize) | | `POST` | [`/v1/avize/genereaza-zi`](#api-facturari--post-v1-avize-genereaza-zi) | | `POST` | [`/v1/evidenta/cereri`](#api-evidenta--post-v1-evidenta-cereri) | | `POST` | [`/v1/evidenta/recurente`](#api-evidenta--post-v1-evidenta-recurente) | | `POST` | [`/v1/evidenta/tranzactii`](#api-evidenta--post-v1-evidenta-tranzactii) | | `POST` | [`/v1/oferte`](#api-oferte--post-v1-oferte) | | `POST` | [`/v1/oferte/{id}/duplica`](#api-oferte--post-v1-oferte-id-duplica) | | `POST` | [`/v1/registru/numar`](#api-registru--post-v1-registru-numar) | | `POST` | [`/v1/registru/oferte`](#api-registru--post-v1-registru-oferte) | ## Cum îl folosești ### Alege o cheie nouă pentru fiecare operație Cheia are între 1 și 255 de caractere ASCII vizibile, fără spațiu, ghilimele (`"`) sau bară inversă (`\`). Un UUID nou pentru fiecare operație este alegerea simplă. O poți trimite simplă (`abc`) sau între ghilimele (`"abc"`), cum cere propunerea IETF. ### Trimite cererea cu antetul ```bash title="Prima trimitere" curl -X POST https://app.ilustromania.com/nucleu/v1/oferte \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: 8f14e45f-ea8e-4c2b-9b1a-2f1c0d3e4a5b" \ -d '{ … }' ``` ### La o eroare de rețea, trimite din nou aceeași cerere Aceeași cheie, același corp, aceeași adresă. Dacă prima cerere s-a terminat, primești același cod și același corp, cu antetul `Idempotency-Replayed: true`. Operația nu rulează a doua oară. ```http title="Răspunsul la repetare" HTTP/1.1 201 Created Idempotency-Replayed: true Cache-Control: no-store Content-Type: application/json; charset=utf-8 { … același corp ca la prima trimitere … } ``` ## Răspunsurile posibile | Situație | Răspuns | | --- | --- | | Cheie nouă | ruta rulează normal; răspunsul se salvează | | Aceeași cheie, același corp, prima cerere terminată | răspunsul salvat, cu `Idempotency-Replayed: true` | | Aceeași cheie, alt corp | `422` cu `{"eroare": "idempotency_key_alt_corp"}` | | Aceeași cheie, prima cerere încă rulează | `409` cu `{"eroare": "idempotency_key_in_lucru"}`; încerci din nou puțin mai târziu | | Antet cu caractere nepermise, gol sau mai lung de 255 | `400` cu `{"eroare": "idempotency_key_invalid"}` | | Fără sesiune sau fără acces la firmă | antetul nu se citește; ruta răspunde singură `401` sau `403` | Cu `Accept: application/problem+json`, aceste erori vin în formatul RFC 9457, cu același `cod` (vezi [Erori](#api-erori)). ## Ce se păstrează și cât timp - O cheie se ține **24 de ore**. După aceea, aceeași cheie este din nou o cheie nouă. - Se salvează răspunsurile JSON (sau fără corp) cu cod `2xx` și erorile `4xx` care nu depind de moment. - **Nu se salvează** `5xx` și nici `401`, `403`, `408`, `409`, `425`, `429`: cheia se eliberează, iar repetarea rulează din nou operația. - Repetările trec și ele prin [limita generală](#limite): fiecare trimitere se numără. > [!WARNING] > Corpul se compară exact, după JSON-ul primit, inclusiv ordinea câmpurilor. La repetare trimite același text, nu un obiect construit din nou cu câmpurile în altă ordine, altfel primești `422`. **Cum se formează cheia pe server** Cheia se ține separat pentru fiecare combinație de: - **firmă**: firma activă a sesiunii; pe ruta publică a comenzilor de catering, firma restaurantului; - **rută**: metoda, șablonul rutei și adresa exactă, cu parametri și interogare (`/v1/oferte/A/duplica` și `/v1/oferte/B/duplica` sunt chei diferite); - **cine trimite**: contul din sesiune, împreună cu rolul și funcțiile lui; pe ruta publică, adresa IP; - **valoarea antetului**. Două conturi care folosesc aceeași valoare nu se încurcă. Dacă rolul sau funcțiile contului se schimbă între două trimiteri, a doua este tratată ca o cheie nouă, iar ruta verifică din nou dreptul. Cheile stau în tabelul `chei_idempotenta`, izolat pe firmă, ca orice alt tabel al firmei. ## Când prima cerere s-a oprit la mijloc Dacă procesul s-a oprit în timpul primei cereri sau răspunsul ei nu s-a putut salva, cheia rămâne „în lucru”: orice repetare primește `409` până expiră, după 24 de ore. Platforma nu o reia singură, ca operația să nu ruleze de două ori. > [!TIP] > Dacă primești `409` mult timp, verifică întâi dacă obiectul s-a creat (de exemplu în lista ofertelor). Dacă nu există, trimite cererea cu o cheie nouă. --- # Versiuni API Ce înseamnă /v1, ce schimbări pot apărea fără o versiune nouă și cum se anunță o versiune care rupe compatibilitatea. ## Versiunea de azi Versiunea curentă este `v1` și este singura. Rutele produsului încep cu `/v1`; excepțiile (5) sunt descoperirea OAuth (`/.well-known/…`) și verificarea de sănătate (`/sanatate`). Nu există încă nicio rută `/v2`. ## Regula - **`/v1` rămâne stabil.** O integrare scrisă azi pe `/v1` continuă să meargă. - **Adăugirile nu schimbă versiunea.** O rută nouă, un câmp nou în răspuns sau un antet nou, opțional, apar direct în `/v1`. - **O schimbare care rupe ceva apare doar într-o versiune nouă (`/v2`).** Scoaterea sau redenumirea unei rute ori a unui câmp, schimbarea tipului unui câmp sau un câmp nou obligatoriu în cerere nu se fac în `/v1`. - **Cel puțin 6 luni merg amândouă.** Când apare `/v2`, `/v1` rămâne activă alături de ea cel puțin 6 luni. - **Orice versiune nouă se anunță** în [Noutăți](#note-versiune), cu data de la care merge și data până la care rămâne activă cea veche. ## Adăugiri care au venit deja în `v1` Schimbările din noiembrie 2026 arată cum arată o adăugire: au intrat în `/v1` fără să schimbe ce primește un client existent. | Schimbare | De ce nu rupe nimic | | --- | --- | | [Formatul de eroare RFC 9457](#api-erori--formatul-rfc-9457-la-cerere) | vine doar când clientul trimite `Accept: application/problem+json`; câmpul `eroare` rămâne | | [Antetul `Idempotency-Key`](#idempotenta) | este opțional; fără el, ruta merge ca înainte | | [Cheile API](#chei-api) | un mod nou de autentificare, alături de sesiune; cererile cu cookie merg ca înainte | | [Webhook-urile](#webhook-uri) | rute noi (`/v1/webhookuri`); nimic nu se trimite până nu adaugă un Administrator o destinație | ## Cum te pregătești - **Ignoră câmpurile pe care nu le cunoști** în răspunsuri: pot apărea câmpuri noi oricând, fără o versiune nouă. - **Nu te baza pe ordinea câmpurilor** dintr-un răspuns JSON. - **Compară erorile după cod**, nu după textul mesajului: în formatul RFC 9457, câmpul `cod` este stabil. - Urmărește [Noutăți](#note-versiune) pentru anunțuri. --- # Chei API Cheile API ale firmei: cum creezi una, cum o trimiți, ce poate face, cât durează și cum o revoci. ## La ce folosește O cheie API lasă un script sau un server al firmei să citească date fără sesiunea unui om. Cheia aparține firmei, are un rol de firmă, o dată de expirare și se poate revoca oricând, fără să schimbi parola cuiva. Cheia arată așa: `ilk_` urmat de 43 de caractere (32 de octeți aleatori în base64url). Platforma păstrează doar amprenta ei SHA-256, nu cheia. De aceea cheia întreagă **se vede o singură dată**, în răspunsul la creare. > [!WARNING] > Ține cheia într-o variabilă de mediu sau într-un seif, nu în cod, în commituri sau în mesaje. Dacă ai pierdut-o sau a ajuns unde nu trebuie, revoc-o și creează alta. ## Cum o trimiți În antetul `Authorization`, cu schema `Bearer`: ```bash title="O citire cu cheia API" curl https://app.ilustromania.com/nucleu/v1/stoc \ -H "Authorization: Bearer $CHEIE_API" ``` O cerere cu `Bearer ilk_…` se judecă **doar** după cheie: cookie-ul de sesiune din aceeași cerere nu se mai citește. Firma și rolul vin din cheie; antetul `X-Firma-Tab` poate numi doar firma cheii, altfel răspunsul este `403`. ## Ce poate face o cheie Accesul este închis implicit: o cheie trece doar pe rutele dintr-o listă fixă, numai cu metodele din listă. Pe orice altă rută răspunsul este `403` cu `{"eroare": "interzis_cu_cheie_api"}`. | Rută | Metode | Ce primești | | --- | --- | --- | | `/v1/stoc` | `GET`, `HEAD` | stocul firmei, citit din casa de marcat; cere un rol cu drept de citire a stocului (`bucatar`, `patron`, `gestiune`, `finante`); pe o firmă fără stoc legat, `404` | | `/v1/info` | `GET`, `HEAD` | este pe listă, dar ruta cere rolul `admin`, pe care o cheie nu îl are niciodată: răspunsul este `403` | În referința API, rutele care merg cu o cheie au mențiunea „merge și cu o cheie API”. Lista se lărgește doar cu rutele care decid accesul pe rolul și firma cererii, fără să recitească drepturile contului care a creat cheia; o rută nouă pe listă apare în [Noutăți](#note-versiune). Pe lângă listă, mai contează două lucruri: - **Doar citire.** O cheie creată cu `doarCitire: true` (implicit) primește `403` cu `{"eroare": "cheie_doar_citire"}` la orice metodă în afară de `GET` și `HEAD`. - **Rolul nu trece peste cel al creatorului.** La fiecare cerere, contul care a creat cheia trebuie să fie încă activ pe firmă și să fie `admin` sau să aibă chiar rolul cheii. Dacă nu mai este, cheia nu mai merge (`401`). ## Gestionarea cheilor Cheile se creează, se văd și se revocă doar de un **Administrator al firmei**, doar din sesiune (cookie). O cerere făcută cu o cheie API pe aceste rute primește `403`: o cheie nu poate crea altă cheie. | Rută | Ce face | | --- | --- | | `GET /v1/chei-api` | lista cheilor firmei (cel mult 200), întâi cele care încă merg; fără cheia întreagă | | `POST /v1/chei-api` | creează o cheie; răspunsul `201` conține cheia întreagă, o singură dată | | `POST /v1/chei-api/{id}/revoca` | revocă cheia; de acum orice cerere cu ea primește `401` | ### Creează cheia ```bash title="Creare" curl -X POST https://app.ilustromania.com/nucleu/v1/chei-api \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{"nume": "Raport stoc zilnic", "rol": "gestiune", "doarCitire": true, "zile": 90}' ``` | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `nume` | `string` | da | între 1 și 80 de caractere, fără caractere de control; te ajută să recunoști cheia în listă | | `rol` | `string` | da | una din: `bucatar`, `casier`, `gestiune`, `patron`, `marketing`, `angajat`, `finante`; niciodată `admin` | | `doarCitire` | `boolean` | nu | implicit `true`; `false` = cheia poate și scrie, în limita rolului | | `zile` | `integer` | nu | după câte zile expiră: între 1 și 365, implicit 90 | Câmpurile necunoscute sunt refuzate (`400` cu `{"eroare": "corp_invalid"}`). ### Salvează cheia din răspuns ```json title="Răspuns 201" { "cheie": "ilk_…", "detalii": { "id": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "nume": "Raport stoc zilnic", "prefix": "ilk_Ab12Cd34", "rol": "gestiune", "doarCitire": true, "creataLa": "2026-11-12T09:30:00.000Z", "expiraLa": "2027-02-10T09:30:00.000Z", "revocataLa": null, "ultimaFolosire": null, "creataDe": "9b2e4c1a-7d3f-4e8b-a1c2-5f6e7d8c9b0a" } } ``` `cheie` nu mai apare nicăieri după acest răspuns. În listă rămâne doar `prefix` (`ilk_` și primele 8 caractere), ca să știi despre ce cheie e vorba. ### Folosește-o, apoi revoc-o când nu mai e nevoie ```bash title="Revocare" curl -X POST https://app.ilustromania.com/nucleu/v1/chei-api/$ID_CHEIE/revoca \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspunsul conține cheia cu `revocataLa` completat. O cheie deja revocată sau a altei firme dă `404` cu `{"eroare": "negasit"}`. ## Câmpurile din listă | Câmp | Ce înseamnă | | --- | --- | | `id` | identificatorul cheii (pentru revocare) | | `nume`, `rol`, `doarCitire` | cum a fost creată | | `prefix` | începutul cheii (`ilk_` + 8 caractere) | | `creataLa`, `expiraLa` | momentele ISO 8601, în UTC | | `revocataLa` | momentul revocării sau `null` | | `ultimaFolosire` | ultima dată când cheia a fost acceptată la o cerere, actualizată cel mult o dată pe minut, sau `null` | | `creataDe` | contul Administratorului care a creat-o | ## Limite și răspunsuri - **Expirarea este obligatorie:** cel mult 365 de zile de la creare. O cheie expirată dă `401`; pentru continuitate creezi una nouă înainte de expirare și o schimbi în script. - **Cel mult 20 de chei active** pe firmă (nerevocate și neexpirate). A 21-a primește `409` cu `{"eroare": "plafon_chei"}`. - **Limita de cereri:** fiecare cheie are numărătoarea ei, cu aceleași praguri ca o sesiune (vezi [Limite](#limite)); adresa IP se numără și ea. | Situație | Răspuns | | --- | --- | | Cheie necunoscută, revocată sau expirată; creatorul nu mai are rolul | `401` cu `{"eroare": "neautentificat"}`, fără alte detalii | | Rută care nu e pe lista cheilor | `403` cu `{"eroare": "interzis_cu_cheie_api"}` | | Scriere cu o cheie doar de citire | `403` cu `{"eroare": "cheie_doar_citire"}` | | `X-Firma-Tab` cu altă firmă decât a cheii | `403` cu `{"eroare": "interzis"}` | | Gestionarea cheilor fără sesiune / fără rolul `admin` / cu o cheie | `401` / `403` / `403` | **Cum se verifică o cheie pe server** La fiecare cerere, serverul calculează SHA-256 al cheii primite și caută amprenta în tabelul `chei_api`. Cheia contează doar dacă nu e revocată, nu a expirat, iar contul care a creat-o este încă activ pe firmă, cu rolul `admin` sau cu rolul cheii. Căutarea se face printr-o funcție a bazei (`cheie_api_verifica`), fiindcă firma nu se știe încă; după ea, cererea lucrează sub izolarea firmei, ca orice altă cerere. Tabelul este izolat pe firmă: un Administrator vede și revocă doar cheile firmei lui. --- # Meniul zilei 5 rute, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ## Modulul `meniul-zilei-public` ### GET /v1/public/meniul-zilei Acces: `public` **Meniul zilei public** Meniul zilei pentru site, citit dintr-un fișier generat și ținut scurt în memorie; CORS doar pentru originile permise. Fără cont. ```bash curl https://app.ilustromania.com/nucleu/v1/public/meniul-zilei ``` ## Modulul `arhiva` ### GET /v1/arhiva Acces: `rol:admin` **Documentele din arhivă** Lista documentelor din arhivă la care are drept omul din sesiune. ```bash curl https://app.ilustromania.com/nucleu/v1/arhiva \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/arhiva/{id}/{nume} Acces: `rol:admin` **Un fișier din arhivă** Servește un fișier al unui document din arhivă (index.html sau resursele lui); 404 document necunoscut. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | | `nume` | `string` | valoarea din cale | ```bash curl https://app.ilustromania.com/nucleu/v1/arhiva/{id}/{nume} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/arhiva/stare/{id} Acces: `rol:admin` **Starea salvată a unui document** Starea și versiunea salvate de om pentru un document interactiv din arhivă. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl https://app.ilustromania.com/nucleu/v1/arhiva/stare/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PUT /v1/arhiva/stare/{id} Acces: `sesiune` **Salvează starea unui document** Scrie starea omului pentru un document din arhivă, cu versiune (corp limitat). Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/arhiva/stare/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` --- # Reduceri și coduri 6 rute, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ## Modulul `reduceri` ### GET /v1/reduceri/{platforma} Acces: `rol:admin` **Starea reducerilor pe platformă** Starea reducerilor pe Glovo sau Wolt; Bolt e doar citire (`poate_scrie: false`). 404 platformă necunoscută. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `platforma` | `string` | valoarea din cale | ```bash curl https://app.ilustromania.com/nucleu/v1/reduceri/{platforma} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/reduceri/{platforma} Acces: `sesiune` **Cere o reducere pe platformă** Pune o cerere de reducere pentru Glovo sau Wolt, preluată de scriptul de pe server; 202 `in_asteptare`, 409 cerere în curs sau Bolt. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `platforma` | `string` | valoarea din cale | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/reduceri/{platforma} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/reduceri/active Acces: `sesiune` **Reducerile active pe canale** Reducerile active citite din fișierul generat; doar firma platformei, administratorul sau cine are pagina. ```bash curl https://app.ilustromania.com/nucleu/v1/reduceri/active \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ## Modulul `coduri-reducere` ### GET /v1/coduri-reducere Acces: `rol:admin` **Codurile de reducere** Lista codurilor de reducere ale firmei (administrator). Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `coduri` | `object[]` | da | — | | `coduri[].id` | `string` | da | UUID | | `coduri[].cod` | `string` | da | între 3 și 24 caractere | | `coduri[].tip` | `string` | da | una din: `procent`, `suma` | | `coduri[].valoare` | `number` | da | cel puțin 0 | | `coduri[].maxFolosiri` | `integer` | da | între 0 și 9007199254740991 | | `coduri[].oDataPeClient` | `boolean` | da | — | | `coduri[].valabilDeLa` | `string` | da | moment ISO 8601 | | `coduri[].valabilPanaLa` | `string` | da | moment ISO 8601 | | `coduri[].activ` | `boolean` | da | — | | `coduri[].produseIds` | `string[]` | da | — | | `coduri[].creatDe` | `string` | da | UUID | | `coduri[].creatLa` | `string` | da | moment ISO 8601 | ```bash curl https://app.ilustromania.com/nucleu/v1/coduri-reducere \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "coduri": [ { "id": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "cod": "text", "tip": "procent", "valoare": 12.5, "maxFolosiri": 1, "oDataPeClient": true, "valabilDeLa": "2026-11-12T09:30:00.000Z", "valabilPanaLa": "2026-11-12T09:30:00.000Z", "activ": true, "produseIds": [], "creatDe": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "creatLa": "2026-11-12T09:30:00.000Z" } ] } ``` ### POST /v1/coduri-reducere Acces: `sesiune` **Adaugă un cod de reducere** Creează un cod nou; 409 cod existent, 400 produs invalid. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/coduri-reducere \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### PATCH /v1/coduri-reducere/{id} Acces: `sesiune` **Modifică un cod de reducere** Schimbă câmpurile unui cod de reducere existent. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X PATCH https://app.ilustromania.com/nucleu/v1/coduri-reducere/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` --- # Recenzii 16 rute, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ### GET /v1/reviews Acces: `sesiune` **Recenziile** Fișierul de recenzii al firmei, cu ciornele de răspuns. Admin sau patron. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `versiune` | `number` | da | una din: `1` | | `citit_la` | `string` | da | — | | `canale` | `object[]` | da | — | | `canale[].canal` | `string` | da | una din: `wolt`, `bolt`, `google`, `glovo`, `facebook`, `instagram`, `tiktok` | | `canale[].nota_medie` | `number` | da | între 0 și 10 | | `canale[].scala` | `integer` | da | între 0 și 9007199254740991 | | `canale[].total` | `integer` | da | între 0 și 9007199254740991 | | `canale[].citit_la` | `string` | da | — | | `feedback` | `object[]` | da | — | | `feedback[].id` | `string` | da | nevid | | `feedback[].canal` | `string` | da | una din: `wolt`, `bolt`, `google`, `glovo`, `facebook`, `instagram`, `tiktok` | | `feedback[].id_extern` | `string` | da | nevid | | `feedback[].autor` | `string` | da | nevid | | `feedback[].nota` | `integer` | da | între 1 și 10 | | `feedback[].text` | `string` | da | nevid | | `feedback[].primit_la` | `string` | da | — | | `feedback[].stare_raspuns` | `string` | da | una din: `fara_raspuns`, `ciorna`, `aprobat`, `trimis`, `eroare` | | `feedback[].text_raspuns` | `string` | da | nevid | | `feedback[].raspuns_de` | `string` | da | nevid | | `feedback[].raspuns_la` | `string` | da | — | | `sociale` | `object[]` | nu | — | | `sociale[].canal` | `string` | da | una din: `wolt`, `bolt`, `google`, `glovo`, `facebook`, `instagram`, `tiktok` | | `sociale[].stare` | `string` | da | una din: `ok`, `fara_drept`, `fara_acces` | | `sociale[].motiv` | `string` | da | — | | `sociale[].total` | `integer` | da | între 0 și 9007199254740991 | ```bash curl https://app.ilustromania.com/nucleu/v1/reviews \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "versiune": 1, "citit_la": "text", "canale": [ { "canal": "wolt", "nota_medie": 12.5, "scala": 1, "total": 1, "citit_la": "text" } ], "feedback": [ { "id": "text", "canal": "wolt", "id_extern": "text", "autor": "text", "nota": 1, "text": "text", "primit_la": "text", "stare_raspuns": "fara_raspuns", "text_raspuns": "text", "raspuns_de": "text", "raspuns_la": "text" } ], "sociale": [ { "canal": "wolt", "stare": "ok", "motiv": "text", "total": 1 } ] } ``` ### PUT /v1/reviews/{id}/ciorna Acces: `sesiune` **Ciorna de răspuns la recenzie** Scrie ciorna de răspuns a recenziei `:id`. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `text` | `string` | da | între 1 și 2000 caractere | | `aproba` | `boolean` | nu | implicit `false` | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `id` | `string` | da | nevid | | `canal` | `string` | da | una din: `wolt`, `bolt`, `google`, `glovo`, `facebook`, `instagram`, `tiktok` | | `id_extern` | `string` | da | nevid | | `autor` | `string` | da | nevid | | `nota` | `integer` | da | între 1 și 10 | | `text` | `string` | da | nevid | | `primit_la` | `string` | da | — | | `stare_raspuns` | `string` | da | una din: `fara_raspuns`, `ciorna`, `aprobat`, `trimis`, `eroare` | | `text_raspuns` | `string` | da | nevid | | `raspuns_de` | `string` | da | nevid | | `raspuns_la` | `string` | da | — | ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/reviews/{id}/ciorna \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ "text": "text", "aproba": false }' ``` Răspuns 200 (exemplu din schemă): ```json { "id": "text", "canal": "wolt", "id_extern": "text", "autor": "text", "nota": 1, "text": "text", "primit_la": "text", "stare_raspuns": "fara_raspuns", "text_raspuns": "text", "raspuns_de": "text", "raspuns_la": "text" } ``` ### PUT /v1/reviews/{id}/propunere Acces: `sesiune` **Hotărârea pe o propunere AI** Aprobă sau respinge propunerea AI de răspuns a recenziei `:id`. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `stare` | `string` | da | una din: `aprobat`, `respins` | | `text` | `string` | nu | între 1 și 2000 caractere | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `feedback_id` | `string` | da | nevid | | `text` | `string` | da | între 1 și 2000 caractere | | `de_verificat` | `string` | da | între 1 și 500 caractere | | `scris_de` | `string` | da | nevid | | `scris_la` | `string` | da | — | | `stare` | `string` | da | una din: `propus`, `aprobat`, `respins` | | `hotarat_la` | `string` | da | — | ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/reviews/{id}/propunere \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ "stare": "aprobat", "text": "text" }' ``` Răspuns 200 (exemplu din schemă): ```json { "feedback_id": "text", "text": "text", "de_verificat": "text", "scris_de": "text", "scris_la": "text", "stare": "propus", "hotarat_la": "text" } ``` ### POST /v1/reviews/{id}/reformuleaza Acces: `sesiune` **Reformulează ciorna cu AI** Primește textul scris de om și întoarce varianta reformulată, în același ton; nu scrie nimic. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `text` | `string` | da | între 1 și 2000 caractere | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `pornit` | `boolean` | da | — | | `text` | `string` | nu | — | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/reviews/{id}/reformuleaza \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ "text": "text" }' ``` Răspuns 200 (exemplu din schemă): ```json { "pornit": true, "text": "text" } ``` ### PUT /v1/reviews/{id}/trimitere Acces: `sesiune` **Rezultatul trimiterii unui răspuns** Executorul scrie starea trimiterii: `in_lucru`, `trimis` sau `eroare`. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `stare` | `string` | da | una din: `in_lucru`, `trimis`, `eroare` | | `eroare` | `string` | nu | între 1 și 500 caractere | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `feedback_id` | `string` | da | nevid | | `canal` | `string` | da | nevid | | `text` | `string` | da | între 1 și 2000 caractere | | `stare` | `string` | da | una din: `de_trimis`, `in_lucru`, `trimis`, `eroare`, `anulat` | | `incercari` | `integer` | da | între 0 și 9007199254740991 | | `eroare` | `string` | da | între 1 și 500 caractere | | `cerut_la` | `string` | da | — | | `trimis_la` | `string` | da | — | ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/reviews/{id}/trimitere \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ "stare": "in_lucru", "eroare": "text" }' ``` Răspuns 200 (exemplu din schemă): ```json { "feedback_id": "text", "canal": "text", "text": "text", "stare": "de_trimis", "incercari": 1, "eroare": "text", "cerut_la": "text", "trimis_la": "text" } ``` ### GET /v1/reviews/context Acces: `sesiune` **Contextul pentru AI** Întrebările de context ale firmei, cu răspunsurile folosite la generare. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `versiune` | `number` | da | una din: `1` | | `intrebari` | `object[]` | da | — | | `intrebari[].cheie` | `string` | da | — | | `intrebari[].intrebare` | `string` | da | între 1 și 1000 caractere | | `intrebari[].raspuns` | `string` | da | între 1 și 2000 caractere | | `intrebari[].modificat_la` | `string` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/reviews/context \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "versiune": 1, "intrebari": [ { "cheie": "text", "intrebare": "text", "raspuns": "text", "modificat_la": "text" } ] } ``` ### PUT /v1/reviews/context/{cheie} Acces: `sesiune` **Răspunde la o întrebare de context** Scrie răspunsul la întrebarea de context `:cheie`. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `cheie` | `string` | valoarea din cale | Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `raspuns` | `string` | da | între 1 și 2000 caractere | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `cheie` | `string` | da | — | | `intrebare` | `string` | da | între 1 și 1000 caractere | | `raspuns` | `string` | da | între 1 și 2000 caractere | | `modificat_la` | `string` | da | — | ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/reviews/context/{cheie} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ "raspuns": "text" }' ``` Răspuns 200 (exemplu din schemă): ```json { "cheie": "text", "intrebare": "text", "raspuns": "text", "modificat_la": "text" } ``` ### GET /v1/reviews/generator/stare Acces: `sesiune` **Starea generatorului AI** Dacă generatorul de propuneri e pornit. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `pornit` | `boolean` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/reviews/generator/stare \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "pornit": true } ``` ### GET /v1/reviews/instructiuni Acces: `sesiune` **Instrucțiunile pentru AI** Instrucțiunile active date AI-ului, folosite în fiecare generare. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `versiune` | `number` | da | una din: `1` | | `instructiuni` | `object[]` | da | — | | `instructiuni[].id` | `string` | da | nevid | | `instructiuni[].text` | `string` | da | între 1 și 500 caractere | | `instructiuni[].creat_la` | `string` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/reviews/instructiuni \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "versiune": 1, "instructiuni": [ { "id": "text", "text": "text", "creat_la": "text" } ] } ``` ### POST /v1/reviews/instructiuni Acces: `sesiune` **Adaugă o instrucțiune AI** Adaugă o instrucțiune pentru generarea răspunsurilor. Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `text` | `string` | da | între 1 și 500 caractere | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `id` | `string` | da | nevid | | `text` | `string` | da | între 1 și 500 caractere | | `creat_la` | `string` | da | — | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/reviews/instructiuni \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ "text": "text" }' ``` Răspuns 200 (exemplu din schemă): ```json { "id": "text", "text": "text", "creat_la": "text" } ``` ### DELETE /v1/reviews/instructiuni/{id} Acces: `sesiune` **Dezactivează o instrucțiune AI** Pune instrucțiunea pe `activ = false`; rândul rămâne. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `id` | `string` | da | — | | `activ` | `boolean` | da | una din: `false` | ```bash curl -X DELETE https://app.ilustromania.com/nucleu/v1/reviews/instructiuni/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "id": "text", "activ": false } ``` ### GET /v1/reviews/propuneri Acces: `sesiune` **Propunerile AI de răspuns** Propunerile AI de răspuns la recenzii, de aprobat. Admin sau patron; altă firmă 404. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `versiune` | `number` | da | una din: `1` | | `propuneri` | `object[]` | da | — | | `propuneri[].feedback_id` | `string` | da | nevid | | `propuneri[].text` | `string` | da | între 1 și 2000 caractere | | `propuneri[].de_verificat` | `string` | da | între 1 și 500 caractere | | `propuneri[].scris_de` | `string` | da | nevid | | `propuneri[].scris_la` | `string` | da | — | | `propuneri[].stare` | `string` | da | una din: `propus`, `aprobat`, `respins` | | `propuneri[].hotarat_la` | `string` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/reviews/propuneri \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "versiune": 1, "propuneri": [ { "feedback_id": "text", "text": "text", "de_verificat": "text", "scris_de": "text", "scris_la": "text", "stare": "propus", "hotarat_la": "text" } ] } ``` ### POST /v1/reviews/propuneri/{id}/regenereaza Acces: `sesiune` **Regenerează o propunere AI** Regenerează propunerea cu o instrucțiune, care se salvează implicit; o propunere deja hotărâtă dă 409. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `instructiune` | `string` | nu | între 1 și 500 caractere | | `tine_minte` | `boolean` | nu | implicit `true` | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `pornit` | `boolean` | da | — | | `regenerat` | `boolean` | nu | — | | `propunere` | `object` | nu | — | | `propunere.feedback_id` | `string` | da | nevid | | `propunere.text` | `string` | da | între 1 și 2000 caractere | | `propunere.de_verificat` | `string` | da | între 1 și 500 caractere | | `propunere.scris_de` | `string` | da | nevid | | `propunere.scris_la` | `string` | da | — | | `propunere.stare` | `string` | da | una din: `propus`, `aprobat`, `respins` | | `propunere.hotarat_la` | `string` | da | — | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/reviews/propuneri/{id}/regenereaza \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ "instructiune": "text", "tine_minte": true }' ``` Răspuns 200 (exemplu din schemă): ```json { "pornit": true, "regenerat": true, "propunere": { "feedback_id": "text", "text": "text", "de_verificat": "text", "scris_de": "text", "scris_la": "text", "stare": "propus", "hotarat_la": "text" } } ``` ### POST /v1/reviews/propuneri/genereaza Acces: `sesiune` **Generează propuneri AI** Generează propuneri pentru recenziile fără răspuns; 503 când sursa lipsește. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `generate` | `integer` | da | între -9007199254740991 și 9007199254740991 | | `ramase` | `integer` | da | între -9007199254740991 și 9007199254740991 | | `pornit` | `boolean` | nu | — | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/reviews/propuneri/genereaza \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` Răspuns 200 (exemplu din schemă): ```json { "generate": 1, "ramase": 1, "pornit": true } ``` ### POST /v1/reviews/propuneri/genereaza-intern Acces: `sesiune` **Generare internă de propuneri** Apelul temporizatorului de pe server, cu jeton de serviciu: generează cel mult 10 propuneri „de aprobat”, fără să aprobe sau să trimită. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `generate` | `integer` | da | între -9007199254740991 și 9007199254740991 | | `ramase` | `integer` | da | între -9007199254740991 și 9007199254740991 | | `pornit` | `boolean` | nu | — | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/reviews/propuneri/genereaza-intern \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` Răspuns 200 (exemplu din schemă): ```json { "generate": 1, "ramase": 1, "pornit": true } ``` ### GET /v1/reviews/trimiteri Acces: `sesiune` **Coada de trimitere a răspunsurilor** Starea răspunsurilor aprobate de trimis pe canalele cu API (azi doar Google). Citește omul cu drept sau executorul cu jeton de serviciu; nu trimite nimic. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `versiune` | `number` | da | una din: `1` | | `trimiteri` | `object[]` | da | — | | `trimiteri[].feedback_id` | `string` | da | nevid | | `trimiteri[].canal` | `string` | da | nevid | | `trimiteri[].text` | `string` | da | între 1 și 2000 caractere | | `trimiteri[].stare` | `string` | da | una din: `de_trimis`, `in_lucru`, `trimis`, `eroare`, `anulat` | | `trimiteri[].incercari` | `integer` | da | între 0 și 9007199254740991 | | `trimiteri[].eroare` | `string` | da | între 1 și 500 caractere | | `trimiteri[].cerut_la` | `string` | da | — | | `trimiteri[].trimis_la` | `string` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/reviews/trimiteri \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "versiune": 1, "trimiteri": [ { "feedback_id": "text", "canal": "text", "text": "text", "stare": "de_trimis", "incercari": 1, "eroare": "text", "cerut_la": "text", "trimis_la": "text" } ] } ``` --- # Bucătar personal 29 de rute, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ### GET /v1/bucatar/din-inventar Acces: `sesiune` **Ce pot găti din inventar** Rețetele care se pot face cu ce e în inventar, peste pragul cerut. ```bash curl https://app.ilustromania.com/nucleu/v1/bucatar/din-inventar \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/bucatar/genereaza-imagini-lipsa Acces: `sesiune` **Starea generării imaginilor** Progresul cozii care generează imaginile lipsă. ```bash curl https://app.ilustromania.com/nucleu/v1/bucatar/genereaza-imagini-lipsa \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/bucatar/genereaza-imagini-lipsa Acces: `sesiune` **Generează imaginile lipsă** Pornește coada care generează imaginile rețetelor fără poză; 202 dacă rulează deja. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/bucatar/genereaza-imagini-lipsa \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/bucatar/imagine/{fisier} Acces: `sesiune` **Imaginea unei rețete** Servește fișierul de imagine al unei rețete; 404 negăsit. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `fisier` | `string` | valoarea din cale | ```bash curl https://app.ilustromania.com/nucleu/v1/bucatar/imagine/{fisier} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/bucatar/ingrediente Acces: `sesiune` **Ingredientele** Caută ingredientele din catalogul bucătarului, pe categorie și text. ```bash curl https://app.ilustromania.com/nucleu/v1/bucatar/ingrediente \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/bucatar/ingrediente Acces: `sesiune` **Salvează un ingredient** Creează sau actualizează un ingredient în catalog. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/bucatar/ingrediente \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### POST /v1/bucatar/ingrediente/{id}/pret Acces: `sesiune` **Pune prețul unui ingredient** Scrie prețul pe kg și magazinul pentru ingredient; 404 ingredient lipsă. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/bucatar/ingrediente/{id}/pret \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### POST /v1/bucatar/ingrediente/{id}/pret-din-url Acces: `sesiune` **Preț din link pentru ingredient** Citește prețul de pe pagina magazinului și îl salvează pe ingredient. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/bucatar/ingrediente/{id}/pret-din-url \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/bucatar/inventar Acces: `sesiune` **Inventarul de acasă** Ce ingrediente sunt în inventar și în ce cantitate. ```bash curl https://app.ilustromania.com/nucleu/v1/bucatar/inventar \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/bucatar/inventar Acces: `sesiune` **Actualizează inventarul** Salvează cantitatea unui ingredient în inventar; 404 ingredient lipsă. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/bucatar/inventar \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/bucatar/jurnal Acces: `sesiune` **Jurnalul meselor** Ultimele mese notate, până la limita cerută. ```bash curl https://app.ilustromania.com/nucleu/v1/bucatar/jurnal \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/bucatar/jurnal Acces: `sesiune` **Notează o masă** Adaugă o masă în jurnal, cu nutriția din rețetă sau din valorile date. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/bucatar/jurnal \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### DELETE /v1/bucatar/jurnal/{id} Acces: `sesiune` **Șterge o masă din jurnal** Scoate o intrare din jurnalul meselor. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X DELETE https://app.ilustromania.com/nucleu/v1/bucatar/jurnal/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/bucatar/lista-cumparaturi Acces: `sesiune` **Lista de cumpărături** Calculează ce trebuie cumpărat pentru rețetele alese, scăzând inventarul. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/bucatar/lista-cumparaturi \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/bucatar/meta Acces: `sesiune` **Listele bucătarului** Categoriile de rețete și metodele de gătire. ```bash curl https://app.ilustromania.com/nucleu/v1/bucatar/meta \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/bucatar/pret-preview Acces: `sesiune` **Previzualizează un preț din link** Citește prețul unui produs de pe pagina magazinului (Freshful) fără să salveze. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/bucatar/pret-preview \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/bucatar/profil Acces: `sesiune` **Profilul bucătarului** Profilul nutrițional al omului din sesiune. ```bash curl https://app.ilustromania.com/nucleu/v1/bucatar/profil \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/bucatar/profil Acces: `sesiune` **Salvează profilul bucătarului** Salvează greutatea, înălțimea, vârsta, activitatea și obiectivul și calculează ținta de calorii și proteine. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/bucatar/profil \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### POST /v1/bucatar/reteta-preview Acces: `sesiune` **Previzualizează o rețetă din link** Citește o rețetă de pe o pagină externă și o potrivește cu ingredientele din catalog, fără să salveze. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/bucatar/reteta-preview \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/bucatar/retete Acces: `sesiune` **Rețetele** Rețetele pe categorie, fiecare cu nutriția calculată din catalog. ```bash curl https://app.ilustromania.com/nucleu/v1/bucatar/retete \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/bucatar/retete Acces: `sesiune` **Adaugă o rețetă** Creează o rețetă cu ingredientele ei și întoarce nutriția calculată. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/bucatar/retete \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### DELETE /v1/bucatar/retete/{id} Acces: `sesiune` **Șterge o rețetă** Scoate rețeta din catalog. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X DELETE https://app.ilustromania.com/nucleu/v1/bucatar/retete/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/bucatar/retete/{id} Acces: `sesiune` **O rețetă** Rețeta cu ingredientele și nutriția ei; 404 rețetă lipsă. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl https://app.ilustromania.com/nucleu/v1/bucatar/retete/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/bucatar/retete/{id}/aprobat Acces: `sesiune` **Comută rețeta aprobată** Marchează sau demarchează rețeta ca aprobată. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/bucatar/retete/{id}/aprobat \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### POST /v1/bucatar/retete/{id}/favorit Acces: `sesiune` **Comută rețeta favorită** Pune sau scoate rețeta din favorite. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/bucatar/retete/{id}/favorit \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### POST /v1/bucatar/retete/{id}/genereaza-imagine Acces: `sesiune` **Generează imaginea rețetei** Generează cu AI imaginea unei rețete; 503 dacă generatorul nu e configurat. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/bucatar/retete/{id}/genereaza-imagine \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/bucatar/sugestie Acces: `sesiune` **Sugestie de masă** Propune rețete potrivite profilului, inventarului și meselor recente. ```bash curl https://app.ilustromania.com/nucleu/v1/bucatar/sugestie \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/bucatar/tinta Acces: `sesiune` **Setează ținta zilnică** Scrie de mână ținta de calorii și proteine. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/bucatar/tinta \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/bucatar/zi/{data} Acces: `sesiune` **Jurnalul unei zile** Mesele din ziua dată, totalul zilei și profilul. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `data` | `string` | valoarea din cale | ```bash curl https://app.ilustromania.com/nucleu/v1/bucatar/zi/{data} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` --- # Înscriere fidelitate 10 rute, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ### GET /v1/fidelitate/card/{cod} Acces: `public` **Cardul de fidelitate** Cardul public după cod: nume și puncte; limită pe IP. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `cod` | `string` | valoarea din cale | ```bash curl https://app.ilustromania.com/nucleu/v1/fidelitate/card/{cod} ``` ### GET /v1/fidelitate/cifre Acces: `sesiune` **Cifrele programului de fidelitate** Totalurile programului de fidelitate pentru firmă. ```bash curl https://app.ilustromania.com/nucleu/v1/fidelitate/cifre \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/fidelitate/clienti Acces: `sesiune` **Clienții de fidelitate** Lista clienților cu card, cu telefonul mascat. ```bash curl https://app.ilustromania.com/nucleu/v1/fidelitate/clienti \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/fidelitate/clienti/{id} Acces: `sesiune` **Profilul unui client de fidelitate** Datele clientului și istoricul punctelor; 404 negăsit. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl https://app.ilustromania.com/nucleu/v1/fidelitate/clienti/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/fidelitate/clienti/{id}/sterge Acces: `sesiune` **Șterge un client de fidelitate** Șterge datele clientului și îi dă un cod de card nou (drept fidelitate.gestioneaza). Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/fidelitate/clienti/{id}/sterge \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/fidelitate/de-verificat Acces: `sesiune` **Scanări de verificat** Scanările care nu s-au potrivit cu un bon și cer verificare. ```bash curl https://app.ilustromania.com/nucleu/v1/fidelitate/de-verificat \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/fidelitate/firma/{slug} Acces: `public` **Denumirea firmei pentru card** Numele firmei pentru textul de acord de pe pagina publică; limită pe IP. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `slug` | `string` | valoarea din cale | ```bash curl https://app.ilustromania.com/nucleu/v1/fidelitate/firma/{slug} ``` ### POST /v1/fidelitate/inscriere Acces: `public` **Înscriere în programul de fidelitate** Clientul se înscrie de pe pagina publică și primește card; limită pe IP și pe telefon. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/fidelitate/inscriere \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### POST /v1/fidelitate/scanari Acces: `sesiune` **Scanează un card de fidelitate** Casierul trimite codul cardului și totalul; ora o pune serverul (drept fidelitate.scaneaza). ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/fidelitate/scanari \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### POST /v1/fidelitate/scanari/{id}/rezolva Acces: `sesiune` **Rezolvă o scanare** Omul decide ce se întâmplă cu o scanare nepotrivită (drept fidelitate.gestioneaza). Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/fidelitate/scanari/{id}/rezolva \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` --- # Agenție 12 rute, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ### GET /v1/agentie/branduri Acces: `rol:admin` **Brandurile agenției** Lista paginată și sortabilă a brandurilor urmărite de agenție. ```bash curl https://app.ilustromania.com/nucleu/v1/agentie/branduri \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/agentie/branduri/coada Acces: `rol:admin` **Coada brandurilor** Brandurile din coada de procesare a agenției, paginate. ```bash curl https://app.ilustromania.com/nucleu/v1/agentie/branduri/coada \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/agentie/branduri/hashtaguri Acces: `rol:admin` **Hashtagurile brandurilor** Hashtagurile urmărite pentru branduri, paginate. ```bash curl https://app.ilustromania.com/nucleu/v1/agentie/branduri/hashtaguri \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/agentie/campanii Acces: `rol:admin` **Campaniile agenției** Lista campaniilor agenției, filtrată din query. Parametri de interogare: | Nume | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `limita` | `integer` | nu | între 1 și 200; implicit `50` | | `deplasare` | `integer` | nu | între 0 și 9007199254740991; implicit `0` | | `cauta` | `string` | nu | cel mult 100 caractere; implicit `` | | `sortare` | `string` | nu | una din: `start`, `nume`, `diamante`, `meciuri`, `durata_live`; implicit `start` | | `directie` | `string` | nu | una din: `asc`, `desc`; implicit `desc` | ```bash curl https://app.ilustromania.com/nucleu/v1/agentie/campanii \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/agentie/campanii/{id} Acces: `rol:admin` **O campanie a agenției** Detaliul unei campanii a agenției; 404 dacă nu e a firmei. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl https://app.ilustromania.com/nucleu/v1/agentie/campanii/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/agentie/creatori Acces: `rol:admin` **Creatorii de conținut** Lista creatorilor, cu căutare, piață și tip. ```bash curl https://app.ilustromania.com/nucleu/v1/agentie/creatori \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/agentie/creatori/{id} Acces: `rol:admin` **Un creator de conținut** Fișa unui creator al firmei; 404 negăsit. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl https://app.ilustromania.com/nucleu/v1/agentie/creatori/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/agentie/manageri Acces: `rol:admin` **Managerii creatorilor** Lista managerilor de creatori, filtrată din query. ```bash curl https://app.ilustromania.com/nucleu/v1/agentie/manageri \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/agentie/notificari Acces: `rol:admin` **Notificările agenției** Notificările agenției pentru firma activă, filtrate din query. ```bash curl https://app.ilustromania.com/nucleu/v1/agentie/notificari \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/agentie/scraper Acces: `rol:admin` **Starea scraperului** Ultima stare raportată de scraperul agenției; cere drept de citire și modulul pornit pe firmă. ```bash curl https://app.ilustromania.com/nucleu/v1/agentie/scraper \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/agentie/stare Acces: `rol:admin` **Starea agenției** Starea modulului de agenție pentru firma din sesiune. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `creatori` | `object` | da | — | | `creatori.scraped` | `integer` | da | între 0 și 9007199254740991 | | `creatori.verificati` | `integer` | da | între 0 și 9007199254740991 | | `creatori.mesaje` | `integer` | da | între 0 și 9007199254740991 | | `branduri` | `object` | da | — | | `branduri.master` | `integer` | da | între 0 și 9007199254740991 | | `branduri.coada` | `integer` | da | între 0 și 9007199254740991 | | `branduri.verdicte` | `integer` | da | între 0 și 9007199254740991 | | `branduri.hashtaguri` | `integer` | da | între 0 și 9007199254740991 | | `campanii` | `integer` | da | între 0 și 9007199254740991 | | `manageri` | `integer` | da | între 0 și 9007199254740991 | | `notificari` | `integer` | da | între 0 și 9007199254740991 | ```bash curl https://app.ilustromania.com/nucleu/v1/agentie/stare \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "creatori": { "scraped": 1, "verificati": 1, "mesaje": 1 }, "branduri": { "master": 1, "coada": 1, "verdicte": 1, "hashtaguri": 1 }, "campanii": 1, "manageri": 1, "notificari": 1 } ``` ### GET /v1/agentie/sumar Acces: `rol:admin` **Sumarul agenției** Cifrele de sumar ale agenției pentru firma activă. ```bash curl https://app.ilustromania.com/nucleu/v1/agentie/sumar \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` --- # Backstage 2 rute, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ### POST /v1/backstage/scrie Acces: `sesiune` **Scrie starea backstage** Jetonul de serviciu al firmei de backstage scrie starea; fără jeton nu se citește corpul. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/backstage/scrie \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/backstage/stare Acces: `rol:admin` **Starea backstage** Starea scrisă de mașinile de backstage; doar administratorul firmei de backstage. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `ok` | `boolean` | da | una din: `true` | | `retea` | `string` | da | una din: `RO-112656` | | `oprit` | `boolean` | da | — | | `sectiuni` | `object` | da | — | | `sectiuni.retea` | `object` | da | — | | `sectiuni.retea.retea` | `string` | da | — | | `sectiuni.retea.sursa` | `string` | da | — | | `sectiuni.retea.la` | `string` | da | — | | `sectiuni.retea.date` | `object` | da | — | | `sectiuni.creatori` | `object` | da | — | | `sectiuni.creatori.retea` | `string` | da | — | | `sectiuni.creatori.sursa` | `string` | da | — | | `sectiuni.creatori.la` | `string` | da | — | | `sectiuni.creatori.date` | `object` | da | — | | `sectiuni.violari` | `object` | da | — | | `sectiuni.violari.retea` | `string` | da | — | | `sectiuni.violari.sursa` | `string` | da | — | | `sectiuni.violari.la` | `string` | da | — | | `sectiuni.violari.date` | `object` | da | — | | `sectiuni.campanii` | `object` | da | — | | `sectiuni.campanii.retea` | `string` | da | — | | `sectiuni.campanii.sursa` | `string` | da | — | | `sectiuni.campanii.la` | `string` | da | — | | `sectiuni.campanii.date` | `object` | da | — | | `sectiuni.meciuri` | `object` | da | — | | `sectiuni.meciuri.retea` | `string` | da | — | | `sectiuni.meciuri.sursa` | `string` | da | — | | `sectiuni.meciuri.la` | `string` | da | — | | `sectiuni.meciuri.date` | `object` | da | — | | `sectiuni.comunitati` | `object` | da | — | | `sectiuni.comunitati.retea` | `string` | da | — | | `sectiuni.comunitati.sursa` | `string` | da | — | | `sectiuni.comunitati.la` | `string` | da | — | | `sectiuni.comunitati.date` | `object` | da | — | | `sectiuni.legaturi_api` | `object` | da | — | | `sectiuni.legaturi_api.retea` | `string` | da | — | | `sectiuni.legaturi_api.sursa` | `string` | da | — | | `sectiuni.legaturi_api.la` | `string` | da | — | | `sectiuni.legaturi_api.date` | `object` | da | — | | `heartbeat` | `object` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/backstage/stare \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "ok": true, "retea": "RO-112656", "oprit": true, "sectiuni": { "retea": { "retea": "text", "sursa": "text", "la": "text", "date": {} }, "creatori": { "retea": "text", "sursa": "text", "la": "text", "date": {} }, "violari": { "retea": "text", "sursa": "text", "la": "text", "date": {} }, "campanii": { "retea": "text", "sursa": "text", "la": "text", "date": {} }, "meciuri": { "retea": "text", "sursa": "text", "la": "text", "date": {} }, "comunitati": { "retea": "text", "sursa": "text", "la": "text", "date": {} }, "legaturi_api": { "retea": "text", "sursa": "text", "la": "text", "date": {} } }, "heartbeat": { "ultima_scriere": "text" } } ``` --- # Social media 20 de rute, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ## Modulul `social` ### GET /v1/social/coperte/{fisier} Acces: `sesiune` **Coperta unei postări** Servește fișierul de copertă după nume validat; 404 la nume invalid sau fișier lipsă. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `fisier` | `string` | valoarea din cale | ```bash curl https://app.ilustromania.com/nucleu/v1/social/coperte/{fisier} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/social/fisiere Acces: `sesiune` **Fișierele social din Drive** Fișierele din folderul social de pe Drive (rădăcina sau `?folder=`). ```bash curl https://app.ilustromania.com/nucleu/v1/social/fisiere \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/social/fisiere/miniatura/{id} Acces: `sesiune` **Miniatura unui fișier social** Miniatura fișierului Drive `:id` din folderul social. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl https://app.ilustromania.com/nucleu/v1/social/fisiere/miniatura/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/social/idei Acces: `sesiune` **Ideile de conținut** Ideile de postări ale firmei. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `idei` | `object[]` | da | — | | `idei[].titlu` | `string` | da | între 1 și 140 caractere | | `idei[].descriere` | `string` | da | cel mult 4000 caractere | | `idei[].platforma` | `string` | da | una din: `instagram`, `facebook`, `tiktok`, `google` | | `idei[].referinta` | `string \| string` | da | — | | `idei[].id` | `string` | da | UUID | | `idei[].autorId` | `string` | da | UUID | | `idei[].autorNume` | `string` | da | — | | `idei[].stare` | `string` | da | una din: `noua`, `acceptata`, `in_lucru`, `realizata`, `respinsa` | | `idei[].postareId` | `string` | da | UUID | | `idei[].creatLa` | `string` | da | moment ISO 8601 | | `poateAdauga` | `boolean` | da | — | | `esteAdmin` | `boolean` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/social/idei \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "idei": [ { "titlu": "text", "descriere": "text", "platforma": "instagram", "referinta": "", "id": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "autorId": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "autorNume": "text", "stare": "noua", "postareId": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "creatLa": "2026-11-12T09:30:00.000Z" } ], "poateAdauga": true, "esteAdmin": true } ``` ### POST /v1/social/idei Acces: `sesiune` **Adaugă o idee** Creează o idee de postare; 201. Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `titlu` | `string` | da | între 1 și 140 caractere | | `descriere` | `string` | da | cel mult 4000 caractere | | `platforma` | `string` | da | una din: `instagram`, `facebook`, `tiktok`, `google` | | `referinta` | `string \| string` | da | — | Răspuns 201: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `titlu` | `string` | da | între 1 și 140 caractere | | `descriere` | `string` | da | cel mult 4000 caractere | | `platforma` | `string` | da | una din: `instagram`, `facebook`, `tiktok`, `google` | | `referinta` | `string \| string` | da | — | | `id` | `string` | da | UUID | | `autorId` | `string` | da | UUID | | `autorNume` | `string` | da | — | | `stare` | `string` | da | una din: `noua`, `acceptata`, `in_lucru`, `realizata`, `respinsa` | | `postareId` | `string` | da | UUID | | `creatLa` | `string` | da | moment ISO 8601 | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/social/idei \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ "titlu": "text", "descriere": "text", "platforma": "instagram", "referinta": "" }' ``` Răspuns 201 (exemplu din schemă): ```json { "titlu": "text", "descriere": "text", "platforma": "instagram", "referinta": "", "id": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "autorId": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "autorNume": "text", "stare": "noua", "postareId": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "creatLa": "2026-11-12T09:30:00.000Z" } ``` ### DELETE /v1/social/idei/{id} Acces: `sesiune` **Șterge ideea** Șterge ideea `:id`; 204. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | UUID | ```bash curl -X DELETE https://app.ilustromania.com/nucleu/v1/social/idei/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PUT /v1/social/idei/{id} Acces: `sesiune` **Modifică ideea** Rescrie ideea `:id`. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | UUID | Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `titlu` | `string` | da | între 1 și 140 caractere | | `descriere` | `string` | da | cel mult 4000 caractere | | `platforma` | `string` | da | una din: `instagram`, `facebook`, `tiktok`, `google` | | `referinta` | `string \| string` | da | — | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `titlu` | `string` | da | între 1 și 140 caractere | | `descriere` | `string` | da | cel mult 4000 caractere | | `platforma` | `string` | da | una din: `instagram`, `facebook`, `tiktok`, `google` | | `referinta` | `string \| string` | da | — | | `id` | `string` | da | UUID | | `autorId` | `string` | da | UUID | | `autorNume` | `string` | da | — | | `stare` | `string` | da | una din: `noua`, `acceptata`, `in_lucru`, `realizata`, `respinsa` | | `postareId` | `string` | da | UUID | | `creatLa` | `string` | da | moment ISO 8601 | ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/social/idei/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ "titlu": "text", "descriere": "text", "platforma": "instagram", "referinta": "" }' ``` Răspuns 200 (exemplu din schemă): ```json { "titlu": "text", "descriere": "text", "platforma": "instagram", "referinta": "", "id": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "autorId": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "autorNume": "text", "stare": "noua", "postareId": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "creatLa": "2026-11-12T09:30:00.000Z" } ``` ### POST /v1/social/idei/{id}/in-calendar Acces: `sesiune` **Pune ideea în calendar** Transformă ideea `:id` într-o postare programată. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | UUID | Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `programatLa` | `string` | da | moment ISO 8601 | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `postare` | `object` | da | — | | `postare.programatLa` | `string` | da | moment ISO 8601 | | `postare.platforme` | `string[]` | da | între 1 și 4 elemente | | `postare.tip` | `string` | da | una din: `postare`, `story`, `reel` | | `postare.text` | `string` | da | cel mult 4000 caractere | | `postare.media` | `string[]` | da | cel mult 10 elemente | | `postare.responsabilId` | `string` | da | UUID | | `postare.stare` | `string` | da | una din: `ciorna`, `de_aprobat`, `programata`, `publicata`, `anulata` | | `postare.eticheta` | `string` | da | cel mult 60 caractere | | `postare.note` | `string` | da | cel mult 2000 caractere | | `postare.id` | `string` | da | UUID | | `postare.ideeId` | `string` | da | UUID | | `postare.creatDe` | `string` | da | UUID | | `postare.creatLa` | `string` | da | moment ISO 8601 | | `idee` | `object` | da | — | | `idee.titlu` | `string` | da | între 1 și 140 caractere | | `idee.descriere` | `string` | da | cel mult 4000 caractere | | `idee.platforma` | `string` | da | una din: `instagram`, `facebook`, `tiktok`, `google` | | `idee.referinta` | `string \| string` | da | — | | `idee.id` | `string` | da | UUID | | `idee.autorId` | `string` | da | UUID | | `idee.autorNume` | `string` | da | — | | `idee.stare` | `string` | da | una din: `noua`, `acceptata`, `in_lucru`, `realizata`, `respinsa` | | `idee.postareId` | `string` | da | UUID | | `idee.creatLa` | `string` | da | moment ISO 8601 | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/social/idei/{id}/in-calendar \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ "programatLa": "2026-11-12T09:30:00.000Z" }' ``` Răspuns 200 (exemplu din schemă): ```json { "postare": { "programatLa": "2026-11-12T09:30:00.000Z", "platforme": [ "instagram" ], "tip": "postare", "text": "text", "media": [ "text" ], "responsabilId": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "stare": "ciorna", "eticheta": "text", "note": "text", "id": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "ideeId": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "creatDe": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f" }, "idee": { "titlu": "text", "descriere": "text", "platforma": "instagram", "referinta": "", "id": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "autorId": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "autorNume": "text", "stare": "noua", "postareId": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "creatLa": "2026-11-12T09:30:00.000Z" } } ``` ### PATCH /v1/social/idei/{id}/stare Acces: `sesiune` **Starea ideii** Schimbă starea ideii `:id`. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | UUID | Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `stare` | `string` | da | una din: `noua`, `acceptata`, `in_lucru`, `realizata`, `respinsa` | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `titlu` | `string` | da | între 1 și 140 caractere | | `descriere` | `string` | da | cel mult 4000 caractere | | `platforma` | `string` | da | una din: `instagram`, `facebook`, `tiktok`, `google` | | `referinta` | `string \| string` | da | — | | `id` | `string` | da | UUID | | `autorId` | `string` | da | UUID | | `autorNume` | `string` | da | — | | `stare` | `string` | da | una din: `noua`, `acceptata`, `in_lucru`, `realizata`, `respinsa` | | `postareId` | `string` | da | UUID | | `creatLa` | `string` | da | moment ISO 8601 | ```bash curl -X PATCH https://app.ilustromania.com/nucleu/v1/social/idei/{id}/stare \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ "stare": "noua" }' ``` Răspuns 200 (exemplu din schemă): ```json { "titlu": "text", "descriere": "text", "platforma": "instagram", "referinta": "", "id": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "autorId": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "autorNume": "text", "stare": "noua", "postareId": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "creatLa": "2026-11-12T09:30:00.000Z" } ``` ### GET /v1/social/oameni Acces: `sesiune` **Oamenii din social** Oamenii firmei care pot fi puși pe postări. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `oameni` | `object[]` | da | — | | `oameni[].contId` | `string` | da | UUID | | `oameni[].nume` | `string` | da | — | | `poateEdita` | `boolean` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/social/oameni \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "oameni": [ { "contId": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "nume": "text" } ], "poateEdita": true } ``` ### GET /v1/social/postari Acces: `sesiune` **Postările din calendar** Postările programate din calendarul social al firmei. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `postari` | `object[]` | da | — | | `postari[].programatLa` | `string` | da | moment ISO 8601 | | `postari[].platforme` | `string[]` | da | între 1 și 4 elemente | | `postari[].tip` | `string` | da | una din: `postare`, `story`, `reel` | | `postari[].text` | `string` | da | cel mult 4000 caractere | | `postari[].media` | `string[]` | da | cel mult 10 elemente | | `postari[].responsabilId` | `string` | da | UUID | | `postari[].stare` | `string` | da | una din: `ciorna`, `de_aprobat`, `programata`, `publicata`, `anulata` | | `postari[].eticheta` | `string` | da | cel mult 60 caractere | | `postari[].note` | `string` | da | cel mult 2000 caractere | | `postari[].id` | `string` | da | UUID | | `postari[].ideeId` | `string` | da | UUID | | `postari[].creatDe` | `string` | da | UUID | | `postari[].creatLa` | `string` | da | moment ISO 8601 | ```bash curl https://app.ilustromania.com/nucleu/v1/social/postari \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "postari": [ { "programatLa": "2026-11-12T09:30:00.000Z", "platforme": [], "tip": "postare", "text": "text", "media": [], "responsabilId": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "stare": "ciorna", "eticheta": "text", "note": "text", "id": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "ideeId": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "creatDe": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f" } ] } ``` ### POST /v1/social/postari Acces: `sesiune` **Adaugă o postare** Creează o postare în calendar; 201. Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `programatLa` | `string` | da | moment ISO 8601 | | `platforme` | `string[]` | da | între 1 și 4 elemente | | `tip` | `string` | da | una din: `postare`, `story`, `reel` | | `text` | `string` | da | cel mult 4000 caractere | | `media` | `string[]` | da | cel mult 10 elemente | | `responsabilId` | `string` | da | UUID | | `stare` | `string` | da | una din: `ciorna`, `de_aprobat`, `programata`, `publicata`, `anulata` | | `eticheta` | `string` | da | cel mult 60 caractere | | `note` | `string` | da | cel mult 2000 caractere | Răspuns 201: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `programatLa` | `string` | da | moment ISO 8601 | | `platforme` | `string[]` | da | între 1 și 4 elemente | | `tip` | `string` | da | una din: `postare`, `story`, `reel` | | `text` | `string` | da | cel mult 4000 caractere | | `media` | `string[]` | da | cel mult 10 elemente | | `responsabilId` | `string` | da | UUID | | `stare` | `string` | da | una din: `ciorna`, `de_aprobat`, `programata`, `publicata`, `anulata` | | `eticheta` | `string` | da | cel mult 60 caractere | | `note` | `string` | da | cel mult 2000 caractere | | `id` | `string` | da | UUID | | `ideeId` | `string` | da | UUID | | `creatDe` | `string` | da | UUID | | `creatLa` | `string` | da | moment ISO 8601 | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/social/postari \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ "programatLa": "2026-11-12T09:30:00.000Z", "platforme": [ "instagram" ], "tip": "postare", "text": "text", "media": [ "text" ], "responsabilId": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "stare": "ciorna", "eticheta": "text", "note": "text" }' ``` Răspuns 201 (exemplu din schemă): ```json { "programatLa": "2026-11-12T09:30:00.000Z", "platforme": [ "instagram" ], "tip": "postare", "text": "text", "media": [ "text" ], "responsabilId": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "stare": "ciorna", "eticheta": "text", "note": "text", "id": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "ideeId": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "creatDe": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f" } ``` ### DELETE /v1/social/postari/{id} Acces: `sesiune` **Șterge postarea** Șterge postarea `:id`; 204. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | UUID | ```bash curl -X DELETE https://app.ilustromania.com/nucleu/v1/social/postari/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PUT /v1/social/postari/{id} Acces: `sesiune` **Modifică postarea** Rescrie postarea `:id`. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | UUID | Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `programatLa` | `string` | da | moment ISO 8601 | | `platforme` | `string[]` | da | între 1 și 4 elemente | | `tip` | `string` | da | una din: `postare`, `story`, `reel` | | `text` | `string` | da | cel mult 4000 caractere | | `media` | `string[]` | da | cel mult 10 elemente | | `responsabilId` | `string` | da | UUID | | `stare` | `string` | da | una din: `ciorna`, `de_aprobat`, `programata`, `publicata`, `anulata` | | `eticheta` | `string` | da | cel mult 60 caractere | | `note` | `string` | da | cel mult 2000 caractere | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `programatLa` | `string` | da | moment ISO 8601 | | `platforme` | `string[]` | da | între 1 și 4 elemente | | `tip` | `string` | da | una din: `postare`, `story`, `reel` | | `text` | `string` | da | cel mult 4000 caractere | | `media` | `string[]` | da | cel mult 10 elemente | | `responsabilId` | `string` | da | UUID | | `stare` | `string` | da | una din: `ciorna`, `de_aprobat`, `programata`, `publicata`, `anulata` | | `eticheta` | `string` | da | cel mult 60 caractere | | `note` | `string` | da | cel mult 2000 caractere | | `id` | `string` | da | UUID | | `ideeId` | `string` | da | UUID | | `creatDe` | `string` | da | UUID | | `creatLa` | `string` | da | moment ISO 8601 | ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/social/postari/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ "programatLa": "2026-11-12T09:30:00.000Z", "platforme": [ "instagram" ], "tip": "postare", "text": "text", "media": [ "text" ], "responsabilId": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "stare": "ciorna", "eticheta": "text", "note": "text" }' ``` Răspuns 200 (exemplu din schemă): ```json { "programatLa": "2026-11-12T09:30:00.000Z", "platforme": [ "instagram" ], "tip": "postare", "text": "text", "media": [ "text" ], "responsabilId": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "stare": "ciorna", "eticheta": "text", "note": "text", "id": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "ideeId": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "creatDe": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f" } ``` ### PATCH /v1/social/postari/{id}/muta Acces: `sesiune` **Mută postarea** Mută postarea `:id` pe altă dată în calendar. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | UUID | Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `programatLa` | `string` | da | moment ISO 8601 | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `programatLa` | `string` | da | moment ISO 8601 | | `platforme` | `string[]` | da | între 1 și 4 elemente | | `tip` | `string` | da | una din: `postare`, `story`, `reel` | | `text` | `string` | da | cel mult 4000 caractere | | `media` | `string[]` | da | cel mult 10 elemente | | `responsabilId` | `string` | da | UUID | | `stare` | `string` | da | una din: `ciorna`, `de_aprobat`, `programata`, `publicata`, `anulata` | | `eticheta` | `string` | da | cel mult 60 caractere | | `note` | `string` | da | cel mult 2000 caractere | | `id` | `string` | da | UUID | | `ideeId` | `string` | da | UUID | | `creatDe` | `string` | da | UUID | | `creatLa` | `string` | da | moment ISO 8601 | ```bash curl -X PATCH https://app.ilustromania.com/nucleu/v1/social/postari/{id}/muta \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ "programatLa": "2026-11-12T09:30:00.000Z" }' ``` Răspuns 200 (exemplu din schemă): ```json { "programatLa": "2026-11-12T09:30:00.000Z", "platforme": [ "instagram" ], "tip": "postare", "text": "text", "media": [ "text" ], "responsabilId": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "stare": "ciorna", "eticheta": "text", "note": "text", "id": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "ideeId": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "creatDe": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f" } ``` ### PATCH /v1/social/postari/{id}/stare Acces: `sesiune` **Starea postării** Schimbă starea postării `:id`; 409 la o trecere nepermisă. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | UUID | Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `stare` | `string` | da | una din: `ciorna`, `de_aprobat`, `programata`, `publicata`, `anulata` | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `programatLa` | `string` | da | moment ISO 8601 | | `platforme` | `string[]` | da | între 1 și 4 elemente | | `tip` | `string` | da | una din: `postare`, `story`, `reel` | | `text` | `string` | da | cel mult 4000 caractere | | `media` | `string[]` | da | cel mult 10 elemente | | `responsabilId` | `string` | da | UUID | | `stare` | `string` | da | una din: `ciorna`, `de_aprobat`, `programata`, `publicata`, `anulata` | | `eticheta` | `string` | da | cel mult 60 caractere | | `note` | `string` | da | cel mult 2000 caractere | | `id` | `string` | da | UUID | | `ideeId` | `string` | da | UUID | | `creatDe` | `string` | da | UUID | | `creatLa` | `string` | da | moment ISO 8601 | ```bash curl -X PATCH https://app.ilustromania.com/nucleu/v1/social/postari/{id}/stare \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ "stare": "ciorna" }' ``` Răspuns 200 (exemplu din schemă): ```json { "programatLa": "2026-11-12T09:30:00.000Z", "platforme": [ "instagram" ], "tip": "postare", "text": "text", "media": [ "text" ], "responsabilId": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "stare": "ciorna", "eticheta": "text", "note": "text", "id": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "ideeId": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "creatDe": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f" } ``` ### GET /v1/social/statistici Acces: `sesiune` **Statisticile social** Statisticile conturilor sociale pe 7, 28 sau 90 de zile. Doar citire, pentru oricine are sesiune în firmă. Parametri de interogare: | Nume | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `perioada` | `string` | da | una din: `7`, `28`, `90` | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `actualizatLa` | `string` | da | moment ISO 8601 | | `perioada` | `number \| number \| number` | da | — | | `conturi` | `object[]` | da | — | | `conturi[].platforma` | `string` | da | una din: `facebook`, `instagram`, `tiktok` | | `conturi[].idExtern` | `string` | da | — | | `conturi[].nume` | `string` | da | — | | `conturi[].urmaritori` | `integer` | da | între 0 și 9007199254740991 | | `conturi[].aprecieri` | `integer` | da | între 0 și 9007199254740991 | | `conturi[].continutTotal` | `integer` | da | între 0 și 9007199254740991 | | `conturi[].stare` | `string` | da | una din: `ok`, `atentie` | | `conturi[].motiv` | `string` | da | cel mult 300 caractere | | `conturi[].actualizatLa` | `string` | da | moment ISO 8601 | | `zile` | `object[]` | da | — | | `zile[].platforma` | `string` | da | una din: `facebook`, `instagram`, `tiktok` | | `zile[].zi` | `string` | da | dată `AAAA-LL-ZZ` | | `zile[].metrica` | `string` | da | una din: `vizualizari`, `persoane`, `urmariri`, `dezabonari`, `urmaritori`, `interactiuni`, `vizite`, `vizualizari_video`, `aprecieri` | | `zile[].valoare` | `integer` | da | între 0 și 9007199254740991 | | `perioade` | `object[]` | da | — | | `perioade[].platforma` | `string` | da | una din: `facebook`, `instagram`, `tiktok` | | `perioade[].perioada` | `number \| number \| number` | da | — | | `perioade[].metrica` | `string` | da | una din: `vizualizari`, `persoane`, `urmariri`, `dezabonari`, `urmaritori`, `interactiuni`, `vizite`, `vizualizari_video`, `aprecieri`, `conturi_implicate`, `vizite_profil`, `clickuri_site` … | | `perioade[].valoare` | `integer` | da | între 0 și 9007199254740991 | | `perioade[].actualizatLa` | `string` | da | moment ISO 8601 | | `continut` | `object[]` | da | cel mult 50 elemente | | `continut[].platforma` | `string` | da | una din: `facebook`, `instagram`, `tiktok` | | `continut[].idExtern` | `string` | da | — | | `continut[].tip` | `string` | da | una din: `reel`, `video`, `foto`, `multi_foto`, `link`, `text`, `story` | | `continut[].text` | `string` | da | cel mult 500 caractere | | `continut[].publicatLa` | `string` | da | moment ISO 8601 | | `continut[].permalink` | `string` | da | adresă URL | | `continut[].miniaturaUrl` | `string \| string` | da | — | | `continut[].vizualizari` | `integer` | da | între 0 și 9007199254740991 | | `continut[].persoane` | `integer` | da | între 0 și 9007199254740991 | | `continut[].aprecieri` | `integer` | da | între 0 și 9007199254740991 | | `continut[].comentarii` | `integer` | da | între 0 și 9007199254740991 | | `continut[].distribuiri` | `integer` | da | între 0 și 9007199254740991 | | `continut[].salvari` | `integer` | da | între 0 și 9007199254740991 | | `continut[].actualizatLa` | `string` | da | moment ISO 8601 | | `demografie` | `object[]` | da | — | | `demografie[].platforma` | `string` | da | una din: `facebook`, `instagram`, `tiktok` | ```bash curl https://app.ilustromania.com/nucleu/v1/social/statistici \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "actualizatLa": "2026-11-12T09:30:00.000Z", "perioada": 7, "conturi": [ { "platforma": "facebook", "idExtern": "text", "nume": "text", "urmaritori": 1, "aprecieri": 1, "continutTotal": 1, "stare": "ok", "motiv": "text", "actualizatLa": "2026-11-12T09:30:00.000Z" } ], "zile": [ { "platforma": "facebook", "zi": "2026-11-12", "metrica": "vizualizari", "valoare": 1 } ], "perioade": [ { "platforma": "facebook", "perioada": 7, "metrica": "vizualizari", "valoare": 1, "actualizatLa": "2026-11-12T09:30:00.000Z" } ], "continut": [ { "platforma": "facebook", "idExtern": "text", "tip": "reel", "text": "text", "publicatLa": "2026-11-12T09:30:00.000Z", "permalink": "https://exemplu.ro", "miniaturaUrl": "https://exemplu.ro", "vizualizari": 1, "persoane": 1, "aprecieri": 1, "comentarii": 1, "distribuiri": 1 } ], "demografie": [ { "platforma": "facebook", "tip": "varsta_sex", "cheie": "text", "valoare": 12.5, "actualizatLa": "2026-11-12T09:30:00.000Z" } ] } ``` ## Modulul `social-idei` ### GET /v1/social/idei-link Acces: `sesiune` **Ideile din linkuri** Linkurile salvate ca idei de conținut, cu `poate_sterge` pentru fiecare. ```bash curl https://app.ilustromania.com/nucleu/v1/social/idei-link \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/social/idei-link Acces: `sesiune` **Salvează un link ca idee** Adaugă un link ca idee de conținut; 400 la link invalid. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/social/idei-link \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### DELETE /v1/social/idei-link/{id} Acces: `sesiune` **Șterge ideea din link** Șterge ideea `:id`, doar de cine are voie; 404 dacă nu există. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X DELETE https://app.ilustromania.com/nucleu/v1/social/idei-link/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` --- # Ofertar 10 rute, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ## Modulul `oferte` ### GET /v1/oferte Acces: `sesiune` **Ofertele mele** Ofertele omului logat în firmă, cu numărul lor. ```bash curl https://app.ilustromania.com/nucleu/v1/oferte \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/oferte Acces: `sesiune` · acceptă `Idempotency-Key` **Creează o ofertă** Creează o ofertă nouă; răspunde 201 cu oferta. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/oferte \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### DELETE /v1/oferte/{id} Acces: `sesiune` **Șterge oferta** Șterge oferta `:id`; 204, sau 404 dacă nu există. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X DELETE https://app.ilustromania.com/nucleu/v1/oferte/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/oferte/{id} Acces: `sesiune` **O ofertă** Oferta `:id`; 404 dacă nu e a omului. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl https://app.ilustromania.com/nucleu/v1/oferte/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PUT /v1/oferte/{id} Acces: `sesiune` **Salvează oferta** Rescrie oferta `:id`; 404 dacă nu există. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/oferte/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### POST /v1/oferte/{id}/duplica Acces: `sesiune` · acceptă `Idempotency-Key` **Duplică oferta** Creează o copie a ofertei `:id`; 201 cu copia, 404 dacă originalul nu există. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/oferte/{id}/duplica \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### POST /v1/oferte/{id}/status Acces: `sesiune` **Schimbă statusul ofertei** Setează statusul ofertei `:id`; 404 dacă nu există. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/oferte/{id}/status \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ## Modulul `catalog` ### GET /v1/catalog/sabloane Acces: `sesiune` **Șabloanele de pachete** Lista șabloanelor de pachete din catalog. ```bash curl https://app.ilustromania.com/nucleu/v1/catalog/sabloane \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/catalog/servicii Acces: `sesiune` **Serviciile din catalog** Lista serviciilor firmei din catalog. ```bash curl https://app.ilustromania.com/nucleu/v1/catalog/servicii \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/catalog/servicii Acces: `sesiune` **Adaugă un serviciu** Creează un serviciu în catalogul firmei (201). ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/catalog/servicii \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` --- # Website Builder 11 rute, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ## Modulul `wtso` ### POST /v1/wtso/clona Acces: `rol:admin` **Clonează un site în WTSO** Pune în coadă clonarea unui site sursă (https) într-un proiect. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/wtso/clona \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/wtso/proiecte Acces: `rol:admin` **Proiectele de traducere și SEO** Proiectele WTSO cu fazele și fazele care cer confirmare. Administratorul firmei platformei. ```bash curl https://app.ilustromania.com/nucleu/v1/wtso/proiecte \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/wtso/rulare Acces: `rol:admin` **Pune fazele la rulat** Pune în coadă fazele cerute ale proiectului; `deploy` și `dns-domain` cer `confirmat: true`, altfel 409. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/wtso/rulare \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/wtso/rulare/{id} Acces: `rol:admin` **O rulare WTSO** Rularea `:id` cu proiectul și jurnalul ei. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl https://app.ilustromania.com/nucleu/v1/wtso/rulare/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/wtso/stare Acces: `rol:admin` **Starea unui proiect WTSO** Proiectul `?proiect=` cu rulările lui; 404 dacă lipsește. ```bash curl https://app.ilustromania.com/nucleu/v1/wtso/stare \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ## Modulul `publicare` ### POST /v1/publicare/oficial Acces: `sesiune` **Publică pe oficial** Trece pe oficial versiunea aflată pe probă; 409 când proba e goală sau publicarea nu e activată. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/publicare/oficial \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### POST /v1/publicare/oficial/revino Acces: `sesiune` **Revino la versiunea anterioară** Pune înapoi pe oficial versiunea anterioară; 409 `fara_versiune_anterioara`. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/publicare/oficial/revino \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### POST /v1/publicare/proba Acces: `sesiune` **Pune o versiune pe probă** Pune pe probă o versiune deja urcată; 409 `versiune_necunoscuta`. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/publicare/proba \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/publicare/staging Acces: `sesiune` **Comutatorul Staging** Starea comutatorului Staging citită din `staging.json`. ```bash curl https://app.ilustromania.com/nucleu/v1/publicare/staging \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/publicare/staging Acces: `sesiune` **Schimbă comutatorul Staging** Scrie și jurnalizează starea comutatorului; scripturile de urcare o citesc. Doar administratorul publicării. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/publicare/staging \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/publicare/stare Acces: `sesiune` **Starea publicării** Versiunile urcate pe probă și pe oficial, cu starea fiecăreia. Doar administratorul publicării. ```bash curl https://app.ilustromania.com/nucleu/v1/publicare/stare \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` --- # Evidența financiară 30 de rute, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ## Modulul `evidenta` ### GET /v1/evidenta/categorii Acces: `rol:admin` **Categoriile din evidență** Categoriile de tranzacții cu totalurile lor. ```bash curl https://app.ilustromania.com/nucleu/v1/evidenta/categorii \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/evidenta/cereri Acces: `rol:admin` **Cererile trimise** Cererile de documente trimise din evidență. ```bash curl https://app.ilustromania.com/nucleu/v1/evidenta/cereri \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/evidenta/cereri Acces: `sesiune` · acceptă `Idempotency-Key` **Trimite o cerere** Înregistrează o cerere nouă de document (201). ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/evidenta/cereri \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### POST /v1/evidenta/cereri/{id}/primit Acces: `sesiune` **Marchează cererea primită** Marchează că documentul cerut a sosit. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/evidenta/cereri/{id}/primit \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/evidenta/conturi Acces: `rol:admin` **Conturile din evidență** Conturile bancare din evidență cu soldurile lor. ```bash curl https://app.ilustromania.com/nucleu/v1/evidenta/conturi \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/evidenta/import Acces: `sesiune` **Importă un extras de cont** Importă un CSV de extras BT (cel mult 5 MB) pe entitatea firmă sau personal. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/evidenta/import \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/evidenta/panou Acces: `rol:admin` **Panoul evidenței** Cifrele de sumar ale evidenței pe perioada cerută. ```bash curl https://app.ilustromania.com/nucleu/v1/evidenta/panou \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/evidenta/recurente Acces: `rol:admin` **Plățile recurente** Plățile recurente, cu cele plătite luna curentă; opțional și cele arhivate. ```bash curl https://app.ilustromania.com/nucleu/v1/evidenta/recurente \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/evidenta/recurente Acces: `sesiune` · acceptă `Idempotency-Key` **Adaugă o plată recurentă** Creează o plată recurentă nouă (201). ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/evidenta/recurente \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### PATCH /v1/evidenta/recurente/{id} Acces: `sesiune` **Modifică o plată recurentă** Schimbă câmpurile unei plăți recurente. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X PATCH https://app.ilustromania.com/nucleu/v1/evidenta/recurente/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### POST /v1/evidenta/recurente/{id}/arhiveaza Acces: `sesiune` **Arhivează o plată recurentă** Scoate plata recurentă din listă. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/evidenta/recurente/{id}/arhiveaza \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### PUT /v1/evidenta/recurente/{id}/plata Acces: `sesiune` **Bifează plata lunii** Marchează plata recurentă ca plătită pentru luna dată. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/evidenta/recurente/{id}/plata \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/evidenta/setari Acces: `rol:admin` **Setările evidenței** Setările proprietarului pentru evidență. ```bash curl https://app.ilustromania.com/nucleu/v1/evidenta/setari \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PUT /v1/evidenta/setari Acces: `sesiune` **Salvează setările evidenței** Scrie setările proprietarului pentru evidență. ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/evidenta/setari \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/evidenta/tranzactii Acces: `rol:admin` **Tranzacțiile din evidență** Tranzacțiile proprietarului, filtrate și paginate. ```bash curl https://app.ilustromania.com/nucleu/v1/evidenta/tranzactii \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/evidenta/tranzactii Acces: `sesiune` · acceptă `Idempotency-Key` **Adaugă o tranzacție** Scrie o tranzacție nouă în evidență (201). ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/evidenta/tranzactii \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### PATCH /v1/evidenta/tranzactii/{id} Acces: `sesiune` **Modifică o tranzacție** Schimbă parțial o tranzacție (categorie, entitate firmă sau personal). Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X PATCH https://app.ilustromania.com/nucleu/v1/evidenta/tranzactii/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### PUT /v1/evidenta/tranzactii/{id} Acces: `sesiune` **Modifică o tranzacție (PUT)** Aceeași modificare parțială ca PATCH, pentru clienții care trimit PUT. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/evidenta/tranzactii/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### POST /v1/evidenta/tranzactii/{id}/arhiveaza Acces: `sesiune` **Arhivează o tranzacție** Scoate tranzacția din listă fără să o șteargă. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/evidenta/tranzactii/{id}/arhiveaza \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/evidenta/tranzactii/{id}/documente Acces: `rol:admin` **Documentele unei tranzacții** Documentele atașate tranzacției. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl https://app.ilustromania.com/nucleu/v1/evidenta/tranzactii/{id}/documente \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/evidenta/tranzactii/{id}/documente Acces: `sesiune` **Atașează un document** Încarcă un PDF sau o imagine (cel mult 10 MB) la tranzacție. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/evidenta/tranzactii/{id}/documente \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/evidenta/tranzactii/{id}/documente/{doc} Acces: `rol:admin` **Descarcă un document atașat** Fișierul documentului atașat tranzacției. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | | `doc` | `string` | valoarea din cale | ```bash curl https://app.ilustromania.com/nucleu/v1/evidenta/tranzactii/{id}/documente/{doc} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/evidenta/tranzactii/{id}/documente/{doc}/arhiveaza Acces: `sesiune` **Arhivează un document atașat** Scoate documentul de pe tranzacție. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | | `doc` | `string` | valoarea din cale | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/evidenta/tranzactii/{id}/documente/{doc}/arhiveaza \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### PUT /v1/evidenta/tranzactii/confirma Acces: `sesiune` **Confirmă tranzacțiile** Confirmă toate tranzacțiile din interval pentru entitatea aleasă. ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/evidenta/tranzactii/confirma \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ## Modulul `documente-costuri` ### GET /v1/evidenta/documente-costuri Acces: `sesiune` **Documentele de costuri** Documentele de cost ale firmei, pentru rolurile care au voie. ```bash curl https://app.ilustromania.com/nucleu/v1/evidenta/documente-costuri \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ## Modulul `finante` ### GET /v1/obligatii Acces: `sesiune` **Obligațiile fiscale** Obligațiile de plată ale firmei. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `obligatii` | `object[]` | da | — | | `obligatii[].id` | `string` | da | UUID | | `obligatii[].cod` | `string` | da | nevid | | `obligatii[].nume` | `string` | da | nevid | | `obligatii[].frecventa` | `string` | da | una din: `lunar`, `trimestrial`, `anual`, `la_operatiune`, `continuu` | | `obligatii[].termen` | `string` | da | — | | `obligatii[].seAplica` | `string` | da | una din: `da`, `nu`, `conditionat`, `de_verificat` | | `obligatii[].conditie` | `string` | da | — | | `obligatii[].cumDepun` | `string` | da | — | | `obligatii[].sursa` | `string` | da | — | | `obligatii[].note` | `string` | da | — | | `obligatii[].scadente` | `object[]` | da | — | | `obligatii[].scadente[].id` | `string` | da | UUID | | `obligatii[].scadente[].perioada` | `string` | da | nevid | | `obligatii[].scadente[].scadenta` | `string` | da | dată `AAAA-LL-ZZ` | | `obligatii[].scadente[].status` | `string` | da | una din: `de_facut`, `in_lucru`, `depus`, `nu_e_cazul`, `intarziat` | | `obligatii[].scadente[].dovada` | `string` | da | — | | `obligatii[].scadente[].suma` | `string` | da | — | | `obligatii[].scadente[].note` | `string` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/obligatii \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "obligatii": [ { "id": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "cod": "text", "nume": "text", "frecventa": "lunar", "termen": "text", "seAplica": "da", "conditie": "text", "cumDepun": "text", "sursa": "text", "note": "text", "scadente": [] } ] } ``` ### GET /v1/plati Acces: `sesiune` **Plățile** Plățile firmei, filtrate din query. Parametri de interogare: | Nume | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `luna` | `string` | da | — | | `entitate` | `string` | nu | una din: `firma`, `personal`, `toate`; implicit `toate` | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `luna` | `string` | da | — | | `entitate` | `string` | da | una din: `firma`, `personal`, `toate` | | `plati` | `object[]` | da | — | | `plati[].id` | `string` | da | între 1 și 120 caractere | | `plati[].numar` | `string` | da | — | | `plati[].furnizor` | `string` | da | — | | `plati[].data` | `string` | da | dată `AAAA-LL-ZZ` | | `plati[].scadenta` | `string` | da | dată `AAAA-LL-ZZ` | | `plati[].suma` | `string` | da | — | | `plati[].moneda` | `string` | da | una din: `RON`, `EUR`, `USD` | | `plati[].de_plata` | `string` | da | — | | `plati[].stare` | `string` | da | una din: `inbox`, `programat`, `platit`, `restant` | | `plati[].tip` | `string` | da | una din: `lunara`, `unica` | | `plati[].entitate` | `string` | da | una din: `firma`, `personal` | | `plati[].sursa` | `string` | da | una din: `factura`, `fiscal`, `recurenta` | | `plati[].factura_url` | `string` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/plati \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "luna": "text", "entitate": "firma", "plati": [ { "id": "text", "numar": "text", "furnizor": "text", "data": "2026-11-12", "scadenta": "2026-11-12", "suma": "text", "moneda": "RON", "de_plata": "text", "stare": "inbox", "tip": "lunara", "entitate": "firma", "sursa": "factura" } ] } ``` ### GET /v1/plati-recurente Acces: `sesiune` **Plățile recurente** Plățile recurente ale firmei, filtrate din query. Parametri de interogare: | Nume | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `stare` | `string` | nu | una din: `activ`, `anulat`; implicit `activ` | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `plati` | `object[]` | da | — | | `plati[].id` | `string` | da | UUID | | `plati[].idSursa` | `string` | da | nevid | | `plati[].nume` | `string` | da | nevid | | `plati[].furnizor` | `string` | da | — | | `plati[].categorie` | `string` | da | — | | `plati[].tip` | `string` | da | una din: `intrare`, `iesire` | | `plati[].frecventa` | `string` | da | una din: `lunar`, `trimestrial`, `semestrial`, `anual` | | `plati[].zi` | `integer` | da | între 1 și 31 | | `plati[].lunaAncora` | `integer` | da | între 1 și 12 | | `plati[].suma` | `string` | da | — | | `plati[].moneda` | `string` | da | una din: `RON`, `EUR`, `USD` | | `plati[].metoda` | `string` | da | — | | `plati[].activ` | `boolean` | da | — | | `plati[].sursa` | `string` | da | — | | `plati[].anulatLa` | `string` | nu | — | ```bash curl https://app.ilustromania.com/nucleu/v1/plati-recurente \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "plati": [ { "id": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "idSursa": "text", "nume": "text", "furnizor": "text", "categorie": "text", "tip": "intrare", "frecventa": "lunar", "zi": 1, "lunaAncora": 1, "suma": "text", "moneda": "RON", "metoda": "text" } ] } ``` ### GET /v1/sarcini-contabile Acces: `sesiune` **Sarcinile contabile** Sarcinile contabile deschise ale firmei. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `sarcini` | `object[]` | da | — | | `sarcini[].id` | `string` | da | UUID | | `sarcini[].idSursa` | `integer` | da | între -9007199254740991 și 9007199254740991 | | `sarcini[].titlu` | `string` | da | nevid | | `sarcini[].detaliu` | `string` | da | — | | `sarcini[].agent` | `string` | da | — | | `sarcini[].prioritate` | `integer` | da | între 0 și 5 | | `sarcini[].status` | `string` | da | una din: `de_facut`, `in_lucru`, `blocat`, `gata` | | `sarcini[].blocaj` | `string` | da | — | | `sarcini[].rezultat` | `string` | da | — | | `sarcini[].faza` | `string` | da | una din: `tranzitie`, `setup`, `lunar`, `anual` | | `intrebari` | `object[]` | da | — | | `intrebari[].id` | `string` | da | UUID | | `intrebari[].idSursa` | `integer` | da | între -9007199254740991 și 9007199254740991 | | `intrebari[].intrebare` | `string` | da | nevid | | `intrebari[].deCe` | `string` | da | — | | `intrebari[].raspuns` | `string` | da | — | | `intrebari[].status` | `string` | da | una din: `deschis`, `lamurit` | | `intrebari[].sursa` | `string` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/sarcini-contabile \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "sarcini": [ { "id": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "idSursa": 1, "titlu": "text", "detaliu": "text", "agent": "text", "prioritate": 1, "status": "de_facut", "blocaj": "text", "rezultat": "text", "faza": "tranzitie" } ], "intrebari": [ { "id": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "idSursa": 1, "intrebare": "text", "deCe": "text", "raspuns": "text", "status": "deschis", "sursa": "text" } ] } ``` ### GET /v1/tranzactii Acces: `sesiune` **Tranzacțiile bancare** Tranzacțiile firmei, filtrate din query. Parametri de interogare: | Nume | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `de` | `string` | nu | dată `AAAA-LL-ZZ` | | `pana` | `string` | nu | dată `AAAA-LL-ZZ` | | `tip` | `string` | nu | una din: `intrare`, `iesire` | | `moneda` | `string` | nu | una din: `RON`, `EUR`, `USD` | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `tranzactii` | `object[]` | da | — | | `tranzactii[].id` | `string` | da | UUID | | `tranzactii[].idSursa` | `string` | da | nevid | | `tranzactii[].data` | `string` | da | dată `AAAA-LL-ZZ` | | `tranzactii[].dataPos` | `string` | da | dată `AAAA-LL-ZZ` | | `tranzactii[].descriere` | `string` | da | — | | `tranzactii[].furnizor` | `string` | da | — | | `tranzactii[].categorie` | `string` | da | nevid | | `tranzactii[].tip` | `string` | da | una din: `intrare`, `iesire` | | `tranzactii[].suma` | `string` | da | — | | `tranzactii[].moneda` | `string` | da | una din: `RON`, `EUR`, `USD` | | `tranzactii[].metoda` | `string` | da | — | | `tranzactii[].ref` | `string` | da | — | | `tranzactii[].sursa` | `string` | da | — | | `tranzactii[].perioada` | `string` | da | — | | `tranzactii[].idRecurenta` | `string` | da | — | | `tranzactii[].detalii` | `object` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/tranzactii \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "tranzactii": [ { "id": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "idSursa": "text", "data": "2026-11-12", "dataPos": "2026-11-12", "descriere": "text", "furnizor": "text", "categorie": "text", "tip": "intrare", "suma": "text", "moneda": "RON", "metoda": "text", "ref": "text" } ] } ``` --- # Registru 8 rute, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ### GET /v1/registru Acces: `sesiune` **Starea Registrului** Starea celor 7 colecții ale Registrului pentru firma din sesiune. Citire pentru admin, patron, finanțe; fără cache. ```bash curl https://app.ilustromania.com/nucleu/v1/registru \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/registru/col Acces: `sesiune` **Documentele unei colecții din Registru** Documentele colecției `?c=`, opțional filtrate `camp=egal`; colecția `acte_data` cere filtru. ```bash curl https://app.ilustromania.com/nucleu/v1/registru/col \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/registru/col/scrie Acces: `sesiune` **Scrie în Registru** Operații `set`, `update` sau `delete` pe un document al colecției, cu versiune: 409 `versiune_veche`. Schimbarea numerelor de factură cere `REGISTRU_EMITE`; 409 `numar_dublat`. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/registru/col/scrie \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/registru/drept-emite Acces: `sesiune` **Pot emite din Registru** `{emite}`: dacă contul din sesiune are dreptul `REGISTRU_EMITE` pe firma activă (`false`, nu 403, pentru cine nu îl are). ```bash curl https://app.ilustromania.com/nucleu/v1/registru/drept-emite \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/registru/numar Acces: `sesiune` · acceptă `Idempotency-Key` **Alocă numărul de proformă sau factură** Alocă atomic următorul număr pe firmă pentru `proforma`/`factura` și an; 403 fără `REGISTRU_EMITE`. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/registru/numar \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### DELETE /v1/registru/oferte Acces: `sesiune` **Șterge o ofertă din Registru** Șterge oferta `?id=`; 204. ```bash curl -X DELETE https://app.ilustromania.com/nucleu/v1/registru/oferte \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/registru/oferte Acces: `sesiune` · acceptă `Idempotency-Key` **Adaugă o ofertă în Registru** Creează o ofertă pentru un client, cu id și dată puse pe server. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/registru/oferte \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### PUT /v1/registru/oferte Acces: `sesiune` **Schimbă o ofertă din Registru** Rescrie o ofertă existentă din Registru. ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/registru/oferte \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` --- # Facturări și avize 20 de rute, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ## Modulul `facturi` ### GET /v1/facturi Acces: `sesiune` **Facturile din DLS** Facturile citite din casa DLS pentru firma activă. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `actualizat` | `string` | da | moment ISO 8601 | | `numar` | `object` | da | — | | `numar.total` | `integer` | da | între 0 și 9007199254740991 | | `numar.facturi` | `integer` | da | între 0 și 9007199254740991 | | `numar.proforme` | `integer` | da | între 0 și 9007199254740991 | | `facturi` | `object[]` | da | — | | `facturi[].id` | `string` | da | nevid | | `facturi[].tip` | `string` | da | una din: `factura`, `proforma` | | `facturi[].numar` | `string` | da | nevid | | `facturi[].stadiu` | `string` | da | nevid | | `facturi[].client` | `string` | da | nevid | | `facturi[].total` | `number` | da | — | | `facturi[].tva` | `number` | da | — | | `facturi[].moneda` | `string` | da | nevid | | `facturi[].emisa` | `string` | da | dată `AAAA-LL-ZZ` | | `facturi[].scadenta` | `string` | da | dată `AAAA-LL-ZZ` | | `facturi[].platita` | `string` | da | dată `AAAA-LL-ZZ` | | `facturi[].stare` | `string` | nu | una din: `incasata`, `neincasata`, `storno`, `anulata` | | `facturi[].incasat` | `number` | nu | — | | `facturi[].rest` | `number` | nu | — | | `totaluri` | `object[]` | nu | — | | `totaluri[].moneda` | `string` | da | nevid | | `totaluri[].valoare` | `number` | da | — | | `totaluri[].incasat` | `number` | da | — | | `totaluri[].neincasat` | `number` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/facturi \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "actualizat": "2026-11-12T09:30:00.000Z", "numar": { "total": 1, "facturi": 1, "proforme": 1 }, "facturi": [ { "id": "text", "tip": "factura", "numar": "text", "stadiu": "text", "client": "text", "total": 12.5, "tva": 12.5, "moneda": "text", "emisa": "2026-11-12", "scadenta": "2026-11-12", "platita": "2026-11-12", "stare": "incasata" } ], "totaluri": [ { "moneda": "text", "valoare": 12.5, "incasat": 12.5, "neincasat": 12.5 } ] } ``` ## Modulul `avize` ### GET /v1/avize Acces: `sesiune` **Avizele de însoțire** Lista avizelor firmei, filtrate pe zi, interval sau client (drept avize.citeste). ```bash curl https://app.ilustromania.com/nucleu/v1/avize \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/avize Acces: `sesiune` · acceptă `Idempotency-Key` **Emite un aviz** Emite un aviz de însoțire nou (drept avize.emite). ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/avize \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/avize/{id} Acces: `sesiune` **Un aviz** Detaliul unui aviz; 404 negăsit. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl https://app.ilustromania.com/nucleu/v1/avize/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PATCH /v1/avize/{id} Acces: `sesiune` **Modifică un aviz** Schimbă câmpurile unui aviz (drept avize.emite); 404 negăsit, 409 dacă avizul e anulat. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X PATCH https://app.ilustromania.com/nucleu/v1/avize/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### POST /v1/avize/{id}/anuleaza Acces: `sesiune` **Anulează un aviz** Marchează avizul ca anulat; 404 negăsit. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/avize/{id}/anuleaza \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/avize/clienti Acces: `sesiune` **Clienții de avize** Caută clienții pentru avize după text. ```bash curl https://app.ilustromania.com/nucleu/v1/avize/clienti \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/avize/clienti Acces: `sesiune` **Adaugă un client de avize** Creează un client pentru avize (201). ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/avize/clienti \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### PATCH /v1/avize/clienti/{id} Acces: `sesiune` **Modifică un client de avize** Schimbă câmpurile unui client de avize; 404 negăsit. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X PATCH https://app.ilustromania.com/nucleu/v1/avize/clienti/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/avize/emitent Acces: `sesiune` **Emitentul avizelor** Datele firmei care emite avizele; 404 dacă nu sunt completate. ```bash curl https://app.ilustromania.com/nucleu/v1/avize/emitent \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PUT /v1/avize/emitent Acces: `sesiune` **Salvează emitentul avizelor** Scrie datele firmei care emite avizele. ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/avize/emitent \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### POST /v1/avize/genereaza-zi Acces: `sesiune` · acceptă `Idempotency-Key` **Generează avizele zilei** Emite avizele pentru ziua și punctele de livrare alese. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/avize/genereaza-zi \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/avize/meniu-camine/{zi} Acces: `sesiune` **Meniul căminelor pe zi** Liniile de meniu ale căminelor pentru ziua dată. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `zi` | `string` | valoarea din cale | ```bash curl https://app.ilustromania.com/nucleu/v1/avize/meniu-camine/{zi} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PUT /v1/avize/meniu-camine/{zi} Acces: `sesiune` **Salvează meniul căminelor pe zi** Înlocuiește liniile de meniu ale căminelor pentru ziua dată. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `zi` | `string` | valoarea din cale | ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/avize/meniu-camine/{zi} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/avize/puncte Acces: `sesiune` **Punctele de livrare** Lista punctelor de livrare pentru avize. ```bash curl https://app.ilustromania.com/nucleu/v1/avize/puncte \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/avize/puncte Acces: `sesiune` **Adaugă un punct de livrare** Creează un punct de livrare pentru avize (201). ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/avize/puncte \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### PATCH /v1/avize/puncte/{id} Acces: `sesiune` **Modifică un punct de livrare** Schimbă câmpurile unui punct de livrare; 404 negăsit. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X PATCH https://app.ilustromania.com/nucleu/v1/avize/puncte/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/avize/serie Acces: `sesiune` **Seria avizelor** Seria și ultimul număr folosit pentru avize. ```bash curl https://app.ilustromania.com/nucleu/v1/avize/serie \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PUT /v1/avize/serie Acces: `sesiune` **Setează ultimul număr din serie** Mută seria avizelor la ultimul număr dat; emiterea sare numerele folosite. ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/avize/serie \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/avize/serie/urmator Acces: `sesiune` **Următorul număr de aviz** Numărul propus pentru formularul de emitere (aceleași drepturi ca emiterea). ```bash curl https://app.ilustromania.com/nucleu/v1/avize/serie/urmator \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` --- # Clienți O rută, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ### GET /v1/clienti Acces: `sesiune` **Clienții firmei** Lista clienților (drept clienti.citeste). Firma cu DLS o primește din cache-ul DLS, doar citire (503 când sursa lipsește); orice altă firmă o primește din tabelul `clienti`, prin firma din sesiune. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `citit_la` | `string` | da | moment ISO 8601 | | `varsta_minute` | `number` | da | cel puțin 0 | | `total` | `integer` | da | între 0 și 9007199254740991 | | `parteneri` | `object[]` | da | — | | `parteneri[].nr` | `integer` | da | între 0 și 9007199254740991 | | `parteneri[].nume` | `string` | da | nevid | | `parteneri[].cui` | `string` | da | — | | `parteneri[].oras` | `string` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/clienti \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "citit_la": "2026-11-12T09:30:00.000Z", "varsta_minute": 12.5, "total": 1, "parteneri": [ { "nr": 1, "nume": "text", "cui": "text", "oras": "text" } ] } ``` --- # Furnizori 2 rute, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ### GET /v1/catalog Acces: `sesiune` **Catalogul de articole DLS** Articolele din casa DLS ale firmei. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `citit_la` | `string` | da | moment ISO 8601 | | `varsta_minute` | `number` | da | cel puțin 0 | | `total` | `integer` | da | între 0 și 9007199254740991 | | `articole` | `object[]` | da | — | | `articole[].nr` | `integer` | da | între 0 și 9007199254740991 | | `articole[].denumire` | `string` | da | nevid | | `articole[].departament` | `string` | da | — | | `articole[].um` | `string` | da | — | | `articole[].moneda` | `string` | da | — | | `articole[].pret` | `number` | da | cel puțin 0 | | `articole[].pret_fara_tva` | `number` | da | cel puțin 0 | | `articole[].pret_achizitie` | `number` | da | cel puțin 0 | | `articole[].stoc` | `number` | da | — | | `articole[].stoc_minim` | `number` | da | — | | `articole[].stoc_maxim` | `number` | da | — | | `articole[].modificat_la` | `string` | da | moment ISO 8601 | ```bash curl https://app.ilustromania.com/nucleu/v1/catalog \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "citit_la": "2026-11-12T09:30:00.000Z", "varsta_minute": 12.5, "total": 1, "articole": [ { "nr": 1, "denumire": "text", "departament": "text", "um": "text", "moneda": "text", "pret": 12.5, "pret_fara_tva": 12.5, "pret_achizitie": 12.5, "stoc": 12.5, "stoc_minim": 12.5, "stoc_maxim": 12.5, "modificat_la": "2026-11-12T09:30:00.000Z" } ] } ``` ### GET /v1/furnizori Acces: `sesiune` **Furnizorii din DLS** Lista furnizorilor din casa DLS ale firmei. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `citit_la` | `string` | da | moment ISO 8601 | | `varsta_minute` | `number` | da | cel puțin 0 | | `total` | `integer` | da | între 0 și 9007199254740991 | | `parteneri` | `object[]` | da | — | | `parteneri[].nr` | `integer` | da | între 0 și 9007199254740991 | | `parteneri[].nume` | `string` | da | nevid | | `parteneri[].cui` | `string` | da | — | | `parteneri[].oras` | `string` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/furnizori \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "citit_la": "2026-11-12T09:30:00.000Z", "varsta_minute": 12.5, "total": 1, "parteneri": [ { "nr": 1, "nume": "text", "cui": "text", "oras": "text" } ] } ``` --- # Statistici agenție O rută, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ### GET /v1/statistici-firma Acces: `rol:admin` **Statisticile firmei** Statisticile firmei din sesiune pe ultimele `?luni=` (1–36, implicit 12), agregate pe server. Doar administratorul. ```bash curl https://app.ilustromania.com/nucleu/v1/statistici-firma \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` --- # Angajați și departamente 12 rute, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ## Modulul `angajati-echipa` ### GET /v1/angajati Acces: `sesiune` **Fișele angajaților** Lista angajaților firmei; salariile apar doar pentru rolurile care au voie să le vadă. ```bash curl https://app.ilustromania.com/nucleu/v1/angajati \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/angajati Acces: `sesiune` **Adaugă un angajat** Creează fișa unui angajat; câmpurile de salariu cer drept de salarii (403 interzis_salarii). ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/angajati \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### PATCH /v1/angajati/{id} Acces: `sesiune` **Modifică un angajat** Schimbă câmpurile din fișa angajatului; salariile cer drept de salarii. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X PATCH https://app.ilustromania.com/nucleu/v1/angajati/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/angajati/{id}/poza Acces: `sesiune` **Poza angajatului** Poza fișei sau, dacă lipsește, poza de profil a contului legat; orice membru al firmei. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl https://app.ilustromania.com/nucleu/v1/angajati/{id}/poza \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PUT /v1/angajati/{id}/poza Acces: `sesiune` **Încarcă poza angajatului** Pune poza pe fișa angajatului (multipart, mărime limitată). Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/angajati/{id}/poza \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ## Modulul `departamente` ### GET /v1/departamente Acces: `sesiune` **Departamentele** Departamentele firmei cu oamenii din ele. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `departamente` | `object[]` | da | — | | `departamente[].id` | `string` | da | UUID | | `departamente[].nume` | `string` | da | — | | `departamente[].ordine` | `integer` | da | între -9007199254740991 și 9007199254740991 | | `departamente[].numarOameni` | `integer` | da | între 0 și 9007199254740991 | | `departamente[].oameni` | `object[]` | da | — | | `departamente[].oameni[].contId` | `string` | da | UUID | | `departamente[].oameni[].nume` | `string` | da | — | | `departamente[].oameni[].rol` | `string` | da | una din: `admin`, `bucatar`, `casier`, `gestiune`, `patron`, `marketing`, `angajat`, `finante` | | `departamente[].oameni[].arePoza` | `boolean` | da | — | | `faraDepartament` | `object[]` | da | — | | `faraDepartament[].contId` | `string` | da | UUID | | `faraDepartament[].nume` | `string` | da | — | | `faraDepartament[].rol` | `string` | da | una din: `admin`, `bucatar`, `casier`, `gestiune`, `patron`, `marketing`, `angajat`, `finante` | | `faraDepartament[].arePoza` | `boolean` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/departamente \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "departamente": [ { "id": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "nume": "text", "ordine": 1, "numarOameni": 1, "oameni": [] } ], "faraDepartament": [ { "contId": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "nume": "text", "rol": "admin", "arePoza": true } ] } ``` ### POST /v1/departamente Acces: `sesiune` **Adaugă un departament** Creează un departament; 409 nume existent. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/departamente \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### DELETE /v1/departamente/{id} Acces: `sesiune` **Șterge un departament** Șterge departamentul; 404 negăsit. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X DELETE https://app.ilustromania.com/nucleu/v1/departamente/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PATCH /v1/departamente/{id} Acces: `sesiune` **Modifică un departament** Schimbă numele sau setările unui departament; 404 negăsit, 409 nume existent. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X PATCH https://app.ilustromania.com/nucleu/v1/departamente/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### DELETE /v1/departamente/{id}/oameni/{contId} Acces: `sesiune` **Scoate un om din departament** Scoate contul din departament; 404 negăsit. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | | `contId` | `string` | valoarea din cale | ```bash curl -X DELETE https://app.ilustromania.com/nucleu/v1/departamente/{id}/oameni/{contId} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PUT /v1/departamente/{id}/oameni/{contId} Acces: `sesiune` **Mută un om în departament** Pune contul în departamentul dat. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | | `contId` | `string` | valoarea din cale | ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/departamente/{id}/oameni/{contId} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ## Modulul `angajat` ### GET /v1/angajat/meniul-zilei Acces: `sesiune` **Meniul zilei pentru angajat** Meniul zilei văzut de angajatul din sesiune. ```bash curl https://app.ilustromania.com/nucleu/v1/angajat/meniul-zilei \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` --- # Prezență 8 rute, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ## Modulul `prezenta` ### GET /v1/prezenta Acces: `sesiune` **Prezența online** Entitățile prezenței online (profiluri, listări) vizibile omului, cu vârsta datelor, citite din fișierul generat. ```bash curl https://app.ilustromania.com/nucleu/v1/prezenta \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/prezenta/istoric Acces: `sesiune` **Istoricul prezenței online** Istoricul cifrelor prezenței online pe interval. Parametri de interogare: | Nume | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `entitate` | `string` | nu | — | | `zile` | `integer` | nu | între 1 și 366; implicit `30` | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `randuri` | `object[]` | da | — | | `randuri[].entitate` | `string` | da | nevid | | `randuri[].firma_id` | `integer` | da | între 0 și 9007199254740991 | | `randuri[].zi` | `string` | da | dată `AAAA-LL-ZZ` | | `randuri[].citit_la` | `string` | da | moment ISO 8601 | | `randuri[].cifre` | `object` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/prezenta/istoric \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "randuri": [ { "entitate": "text", "firma_id": 1, "zi": "2026-11-12", "citit_la": "2026-11-12T09:30:00.000Z", "cifre": {} } ] } ``` ### GET /v1/prezenta/mini/{fisier} Acces: `sesiune` **Miniatura unei postări** Servește miniatura JPEG a unei postări, doar cu nume `[a-z0-9-]+.jpg` din folderul de miniaturi; 404 la alt nume. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `fisier` | `string` | valoarea din cale | ```bash curl https://app.ilustromania.com/nucleu/v1/prezenta/mini/{fisier} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/prezenta/plecat Acces: `sesiune` **Pagina s-a ascuns** Notează că omul a ascuns pagina (trimis prin `sendBeacon`, fără corp); contul vine doar din sesiune. 409 când scrierea e oprită. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/prezenta/plecat \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/prezenta/social Acces: `sesiune` **Conturile sociale** Detaliul conturilor sociale vizibile omului, cu vârsta datelor. ```bash curl https://app.ilustromania.com/nucleu/v1/prezenta/social \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/prezenta/urme Acces: `sesiune` **Lot de urme de navigare** Primește un lot de urme din pagină (JSON sau `text/plain`); 400/413 la lot invalid sau prea mare, 429 prea des. Contul fără uuid primește 200 fără scriere. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/prezenta/urme \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ## Modulul `shell` ### GET /v1/alerte Acces: `sesiune` **Alertele mele** Alertele omului după firma și rolul lui; dreptul `alerte.citeste`, 503 sursă indisponibilă. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `scris_la` | `string` | da | moment ISO 8601 | | `alerte` | `object[]` | da | — | | `alerte[].id` | `string` | da | — | | `alerte[].t` | `string` | da | — | | `alerte[].ton` | `string` | da | una din: ``, `is-pericol` | | `alerte[].n` | `integer` | da | între 0 și 9007199254740991 | | `alerte[].ora` | `string` | da | — | | `alerte[].lista` | `string[]` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/alerte \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "scris_la": "2026-11-12T09:30:00.000Z", "alerte": [ { "id": "text", "t": "text", "ton": "", "n": 1, "ora": "text", "lista": [] } ] } ``` ### GET /v1/prezenta/oameni Acces: `sesiune` **Oamenii prezenți acum** Colegii din firma activă cu prezența lor în aplicație (fără omul care cere); vedere completă sau redusă după drept. ```bash curl https://app.ilustromania.com/nucleu/v1/prezenta/oameni \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` --- # De făcut și sarcini 6 rute, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ## Modulul `de-facut` ### GET /v1/de-facut Acces: `sesiune` **Ce e de făcut** Lista de sarcini deschise pentru omul din sesiune. ```bash curl https://app.ilustromania.com/nucleu/v1/de-facut \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/de-facut/liste-mihai Acces: `sesiune` **Listele lui Mihai** Listele de lucru ale lui Mihai, doar pentru cine are acces pe firma lor. ```bash curl https://app.ilustromania.com/nucleu/v1/de-facut/liste-mihai \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ## Modulul `sarcini` ### GET /v1/sarcini Acces: `sesiune` **Sarcinile** Sarcinile firmei, filtrate pe stare și responsabil (`eu` = omul logat), cu lista responsabililor. ```bash curl https://app.ilustromania.com/nucleu/v1/sarcini \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/sarcini Acces: `sesiune` **Adaugă o sarcină** Creează o sarcină; 201, 422 `responsabil_invalid`. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/sarcini \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### DELETE /v1/sarcini/{id} Acces: `sesiune` **Șterge sarcina** Șterge sarcina `:id`; 204, 404 dacă nu există. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X DELETE https://app.ilustromania.com/nucleu/v1/sarcini/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PATCH /v1/sarcini/{id} Acces: `sesiune` **Modifică sarcina** Schimbă sarcina; cine nu e administrator poate schimba doar starea (403 altfel). Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X PATCH https://app.ilustromania.com/nucleu/v1/sarcini/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` --- # Proceduri 2 rute, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ## Modulul `proceduri` ### GET /v1/proceduri Acces: `rol:admin` **Procedurile** Lista procedurilor firmei platformei. Doar administratorul; 404 pe altă firmă, 503 sursă indisponibilă. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `actualizat` | `string` | da | — | | `surse_verificate` | `string` | da | — | | `surse_vechi` | `boolean` | da | — | | `proceduri` | `object[]` | da | — | | `proceduri[].id` | `string` | da | — | | `proceduri[].nume` | `string` | da | — | | `proceduri[].grup` | `string` | da | — | | `proceduri[].stare` | `string` | da | una din: `merge`, `blocat`, `așteaptă`, `de verificat` | | `proceduri[].ultimul_pas` | `object` | da | — | | `proceduri[].ultimul_pas.text` | `string` | nu | — | | `proceduri[].ultimul_pas.data` | `string` | nu | — | | `proceduri[].ultimul_pas.cine` | `string` | nu | — | | `proceduri[].urmatorul_pas` | `object` | da | — | | `proceduri[].urmatorul_pas.text` | `string` | nu | — | | `proceduri[].urmatorul_pas.data` | `string` | nu | — | | `proceduri[].urmatorul_pas.cine` | `string` | nu | — | | `proceduri[].termen` | `string` | da | — | | `proceduri[].termen_ce` | `string` | da | — | | `proceduri[].salvata` | `boolean` | da | — | | `proceduri[].surse` | `object[]` | da | — | | `proceduri[].surse[].eticheta` | `string` | da | — | | `proceduri[].surse[].exista` | `boolean` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/proceduri \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "actualizat": "text", "surse_verificate": "text", "surse_vechi": true, "proceduri": [ { "id": "text", "nume": "text", "grup": "text", "stare": "merge", "ultimul_pas": {}, "urmatorul_pas": {}, "termen": "text", "termen_ce": "text", "salvata": true, "surse": [] } ] } ``` ## Modulul `info` ### GET /v1/info Acces: `rol:admin` **Informațiile firmei din DLS** Date de rezumat din casa DLS (doar administrator). Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `modul` | `string` | da | una din: `intretinere-comenzi` | | `fapte` | `object[]` | da | — | | `fapte[].eticheta` | `string` | da | — | | `fapte[].valoare` | `string` | da | — | | `campuri` | `object[]` | da | — | | `campuri[].cheie` | `string` | da | — | | `campuri[].eticheta` | `string` | da | — | | `campuri[].valoare` | `string` | da | — | | `citit_la` | `number` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/info \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "modul": "intretinere-comenzi", "fapte": [ { "eticheta": "text", "valoare": "text" } ], "campuri": [ { "cheie": "text", "eticheta": "text", "valoare": "text" } ], "citit_la": 12.5 } ``` --- # Seif 4 rute, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ### GET /v1/seif Acces: `sesiune` **Intrările din seif** Lista intrărilor din seif vizibile omului, fără valorile secrete; fără cache. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `intrari` | `object[]` | da | — | | `intrari[].id` | `string` | da | UUID | | `intrari[].eticheta` | `string` | da | nevid | | `intrari[].utilizator` | `string` | da | nevid | | `intrari[].url` | `string` | da | — | | `intrari[].actualizatLa` | `string` | da | moment ISO 8601 | ```bash curl https://app.ilustromania.com/nucleu/v1/seif \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "intrari": [ { "id": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "eticheta": "text", "utilizator": "text", "url": "text", "actualizatLa": "2026-11-12T09:30:00.000Z" } ] } ``` ### POST /v1/seif Acces: `sesiune` **Adaugă o intrare în seif** Creează o intrare nouă în seif; 201, fără cache. Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `eticheta` | `string` | da | între 1 și 200 caractere | | `utilizator` | `string` | da | între 1 și 300 caractere | | `url` | `string` | nu | între 1 și 500 caractere | | `secret` | `string` | da | între 1 și 4096 caractere | Răspuns 201: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `id` | `string` | da | UUID | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/seif \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ "eticheta": "text", "utilizator": "text", "url": "text", "secret": "text" }' ``` Răspuns 201 (exemplu din schemă): ```json { "id": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f" } ``` ### PUT /v1/seif/{id} Acces: `sesiune` **Modifică o intrare din seif** Schimbă intrarea `:id` din seif. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `eticheta` | `string` | nu | între 1 și 200 caractere | | `utilizator` | `string` | nu | între 1 și 300 caractere | | `url` | `string` | nu | între 1 și 500 caractere | | `secret` | `string` | nu | între 1 și 4096 caractere | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `id` | `string` | da | UUID | ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/seif/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ "eticheta": "text", "utilizator": "text", "url": "text", "secret": "text" }' ``` Răspuns 200 (exemplu din schemă): ```json { "id": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f" } ``` ### POST /v1/seif/{id}/dezvaluie Acces: `sesiune` **Dezvăluie valoarea din seif** Întoarce valoarea secretă a intrării `:id`, doar omului cu drept; răspunsul nu se păstrează în cache. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `secret` | `string` | da | — | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/seif/{id}/dezvaluie \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` Răspuns 200 (exemplu din schemă): ```json { "secret": "text" } ``` --- # Datele firmei 11 rute, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ## Modulul `firma-date` ### GET /v1/firma/{firmaId}/date Acces: `sesiune` **Datele firmei** Datele de identificare ale firmei (CUI, adresă, logo). Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `firmaId` | `string` | UUID | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `nume` | `string` | da | între 1 și 200 caractere | | `cui` | `string` | da | — | | `cif` | `string` | da | între 1 și 20 caractere | | `regCom` | `string` | da | între 1 și 40 caractere | | `euid` | `string` | da | între 1 și 60 caractere | | `duns` | `string` | da | — | | `infiintata` | `string` | da | — | | `capital` | `string` | da | între 1 și 40 caractere | | `caenPrincipal` | `string` | da | — | | `caenSecundare` | `string[]` | da | cel mult 50 elemente | | `sediu` | `string` | da | între 1 și 500 caractere | | `telefon` | `string` | da | între 1 și 40 caractere | | `email` | `string` | da | între 1 și 200 caractere | | `emailSecundar` | `string` | da | adresă de email | | `site` | `string` | da | între 1 și 200 caractere | | `titular` | `string` | da | între 1 și 200 caractere | | `iban` | `string` | da | — | | `banca` | `string` | da | între 1 și 200 caractere | | `bic` | `string` | da | între 1 și 20 caractere | | `valuta` | `string` | da | între 1 și 10 caractere | | `firmaId` | `string` | da | UUID | | `logo` | `string` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/firma/{firmaId}/date \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "nume": "text", "cui": "text", "cif": "text", "regCom": "text", "euid": "text", "duns": "text", "infiintata": "text", "capital": "text", "caenPrincipal": "text", "caenSecundare": [ "text" ], "sediu": "text", "telefon": "text" } ``` ### PUT /v1/firma/{firmaId}/date Acces: `sesiune` **Salvează datele firmei** Scrie datele de identificare ale firmei; 409 la conflict. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `firmaId` | `string` | UUID | Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `nume` | `string` | da | între 1 și 200 caractere | | `cui` | `string` | da | — | | `cif` | `string` | da | între 1 și 20 caractere | | `regCom` | `string` | da | între 1 și 40 caractere | | `euid` | `string` | da | între 1 și 60 caractere | | `duns` | `string` | da | — | | `infiintata` | `string` | da | — | | `capital` | `string` | da | între 1 și 40 caractere | | `caenPrincipal` | `string` | da | — | | `caenSecundare` | `string[]` | da | cel mult 50 elemente | | `sediu` | `string` | da | între 1 și 500 caractere | | `telefon` | `string` | da | între 1 și 40 caractere | | `email` | `string` | da | între 1 și 200 caractere | | `emailSecundar` | `string` | da | cel mult 200 caractere | | `site` | `string` | da | între 1 și 200 caractere | | `titular` | `string` | da | între 1 și 200 caractere | | `iban` | `string` | da | — | | `banca` | `string` | da | între 1 și 200 caractere | | `bic` | `string` | da | între 1 și 20 caractere | | `valuta` | `string` | da | între 1 și 10 caractere | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `nume` | `string` | da | între 1 și 200 caractere | | `cui` | `string` | da | — | | `cif` | `string` | da | între 1 și 20 caractere | | `regCom` | `string` | da | între 1 și 40 caractere | | `euid` | `string` | da | între 1 și 60 caractere | | `duns` | `string` | da | — | | `infiintata` | `string` | da | — | | `capital` | `string` | da | între 1 și 40 caractere | | `caenPrincipal` | `string` | da | — | | `caenSecundare` | `string[]` | da | cel mult 50 elemente | | `sediu` | `string` | da | între 1 și 500 caractere | | `telefon` | `string` | da | între 1 și 40 caractere | | `email` | `string` | da | între 1 și 200 caractere | | `emailSecundar` | `string` | da | adresă de email | | `site` | `string` | da | între 1 și 200 caractere | | `titular` | `string` | da | între 1 și 200 caractere | | `iban` | `string` | da | — | | `banca` | `string` | da | între 1 și 200 caractere | | `bic` | `string` | da | între 1 și 20 caractere | | `valuta` | `string` | da | între 1 și 10 caractere | | `firmaId` | `string` | da | UUID | | `logo` | `string` | da | — | ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/firma/{firmaId}/date \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ "nume": "text", "cui": "text", "cif": "text", "regCom": "text", "euid": "text", "duns": "text", "infiintata": "text", "capital": "text", "caenPrincipal": "text", "caenSecundare": [ "text" ], "sediu": "text", "telefon": "text" }' ``` Răspuns 200 (exemplu din schemă): ```json { "nume": "text", "cui": "text", "cif": "text", "regCom": "text", "euid": "text", "duns": "text", "infiintata": "text", "capital": "text", "caenPrincipal": "text", "caenSecundare": [ "text" ], "sediu": "text", "telefon": "text" } ``` ### DELETE /v1/firma/{firmaId}/logo Acces: `sesiune` **Șterge logoul firmei** Scoate logoul și întoarce datele firmei. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `firmaId` | `string` | UUID | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `nume` | `string` | da | între 1 și 200 caractere | | `cui` | `string` | da | — | | `cif` | `string` | da | între 1 și 20 caractere | | `regCom` | `string` | da | între 1 și 40 caractere | | `euid` | `string` | da | între 1 și 60 caractere | | `duns` | `string` | da | — | | `infiintata` | `string` | da | — | | `capital` | `string` | da | între 1 și 40 caractere | | `caenPrincipal` | `string` | da | — | | `caenSecundare` | `string[]` | da | cel mult 50 elemente | | `sediu` | `string` | da | între 1 și 500 caractere | | `telefon` | `string` | da | între 1 și 40 caractere | | `email` | `string` | da | între 1 și 200 caractere | | `emailSecundar` | `string` | da | adresă de email | | `site` | `string` | da | între 1 și 200 caractere | | `titular` | `string` | da | între 1 și 200 caractere | | `iban` | `string` | da | — | | `banca` | `string` | da | între 1 și 200 caractere | | `bic` | `string` | da | între 1 și 20 caractere | | `valuta` | `string` | da | între 1 și 10 caractere | | `firmaId` | `string` | da | UUID | | `logo` | `string` | da | — | ```bash curl -X DELETE https://app.ilustromania.com/nucleu/v1/firma/{firmaId}/logo \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "nume": "text", "cui": "text", "cif": "text", "regCom": "text", "euid": "text", "duns": "text", "infiintata": "text", "capital": "text", "caenPrincipal": "text", "caenSecundare": [ "text" ], "sediu": "text", "telefon": "text" } ``` ### PUT /v1/firma/{firmaId}/logo Acces: `sesiune` **Încarcă logoul firmei** Logo PNG, JPEG sau WebP ca data-URL, cel mult 200 KB (413 prea mare). Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `firmaId` | `string` | UUID | Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `logo` | `string` | da | nevid | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `nume` | `string` | da | între 1 și 200 caractere | | `cui` | `string` | da | — | | `cif` | `string` | da | între 1 și 20 caractere | | `regCom` | `string` | da | între 1 și 40 caractere | | `euid` | `string` | da | între 1 și 60 caractere | | `duns` | `string` | da | — | | `infiintata` | `string` | da | — | | `capital` | `string` | da | între 1 și 40 caractere | | `caenPrincipal` | `string` | da | — | | `caenSecundare` | `string[]` | da | cel mult 50 elemente | | `sediu` | `string` | da | între 1 și 500 caractere | | `telefon` | `string` | da | între 1 și 40 caractere | | `email` | `string` | da | între 1 și 200 caractere | | `emailSecundar` | `string` | da | adresă de email | | `site` | `string` | da | între 1 și 200 caractere | | `titular` | `string` | da | între 1 și 200 caractere | | `iban` | `string` | da | — | | `banca` | `string` | da | între 1 și 200 caractere | | `bic` | `string` | da | între 1 și 20 caractere | | `valuta` | `string` | da | între 1 și 10 caractere | | `firmaId` | `string` | da | UUID | | `logo` | `string` | da | — | ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/firma/{firmaId}/logo \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ "logo": "text" }' ``` Răspuns 200 (exemplu din schemă): ```json { "nume": "text", "cui": "text", "cif": "text", "regCom": "text", "euid": "text", "duns": "text", "infiintata": "text", "capital": "text", "caenPrincipal": "text", "caenSecundare": [ "text" ], "sediu": "text", "telefon": "text" } ``` ## Modulul `firma-conturi` ### GET /v1/firma/{firmaId}/conturi Acces: `sesiune` **Conturile bancare ale firmei** Lista conturilor bancare ale firmei (administratorul ei). Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `firmaId` | `string` | valoarea din cale | ```bash curl https://app.ilustromania.com/nucleu/v1/firma/{firmaId}/conturi \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/firma/{firmaId}/conturi Acces: `sesiune` **Adaugă un cont bancar** Adaugă un cont bancar pe firmă (administratorul ei). Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `firmaId` | `string` | valoarea din cale | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/firma/{firmaId}/conturi \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### DELETE /v1/firma/{firmaId}/conturi/{contId} Acces: `sesiune` **Șterge un cont bancar** Scoate contul bancar de pe firmă; 404 negăsit. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `firmaId` | `string` | valoarea din cale | | `contId` | `string` | valoarea din cale | ```bash curl -X DELETE https://app.ilustromania.com/nucleu/v1/firma/{firmaId}/conturi/{contId} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PUT /v1/firma/{firmaId}/conturi/{contId} Acces: `sesiune` **Modifică un cont bancar** Schimbă datele unui cont bancar al firmei. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `firmaId` | `string` | valoarea din cale | | `contId` | `string` | valoarea din cale | ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/firma/{firmaId}/conturi/{contId} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ## Modulul `conturi-conectate` ### GET /v1/conturi-conectate Acces: `sesiune` **Conturile conectate** Conturile externe conectate la firma activă. ```bash curl https://app.ilustromania.com/nucleu/v1/conturi-conectate \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ## Modulul `setari-ordine` ### GET /v1/setari/ordine Acces: `sesiune` **Ordinea grupurilor din Setări** Grupurile și ordinea lor în pagina Setări, pentru omul logat; `null` fără salvare. ```bash curl https://app.ilustromania.com/nucleu/v1/setari/ordine \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PUT /v1/setari/ordine Acces: `rol:admin` **Salvează ordinea din Setări** Scrie ordinea grupurilor din Setări; doar administratorul. ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/setari/ordine \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` --- # Asistent 16 rute, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ## Modulul `asistent` ### GET /v1/asistent/acces Acces: `sesiune` **Are omul asistent?** Răspunde doar da/nu dacă shell-ul arată asistentul omului din sesiune. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `acces` | `boolean` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/asistent/acces \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "acces": true } ``` ### GET /v1/asistent/conversatii Acces: `sesiune` **Conversațiile mele cu asistentul** Lista conversațiilor omului cu asistentul. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `conversatii` | `object[]` | da | — | | `conversatii[].id` | `string` | da | UUID | | `conversatii[].titlu` | `string` | da | cel mult 200 caractere | | `conversatii[].actualizatLa` | `string` | da | moment ISO 8601 | ```bash curl https://app.ilustromania.com/nucleu/v1/asistent/conversatii \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "conversatii": [ { "id": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "titlu": "text", "actualizatLa": "2026-11-12T09:30:00.000Z" } ] } ``` ### DELETE /v1/asistent/conversatii/{id} Acces: `sesiune` **Șterge o conversație** Șterge o conversație a omului cu asistentul; 404 negăsită. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X DELETE https://app.ilustromania.com/nucleu/v1/asistent/conversatii/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PATCH /v1/asistent/conversatii/{id} Acces: `sesiune` **Redenumește o conversație** Schimbă titlul unei conversații cu asistentul. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `titlu` | `string` | da | între 1 și 200 caractere | ```bash curl -X PATCH https://app.ilustromania.com/nucleu/v1/asistent/conversatii/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ "titlu": "text" }' ``` ### GET /v1/asistent/conversatii/{id}/mesaje Acces: `sesiune` **Mesajele unei conversații** Istoricul mesajelor unei conversații a omului cu asistentul. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `mesaje` | `object[]` | da | — | | `mesaje[].id` | `integer` | da | între 0 și 9007199254740991 | | `mesaje[].rol` | `string` | da | una din: `utilizator`, `asistent` | | `mesaje[].titlu` | `string` | da | cel mult 200 caractere | | `mesaje[].text` | `string` | da | cel mult 20000 caractere | | `mesaje[].carduri` | `object[]` | da | cel mult 20 elemente | | `mesaje[].carduri[].tip` | `string` | da | una din: `produs`, `vanzari`, `comparatie`, `recenzie`, `reducere` | | `mesaje[].carduri[].titlu` | `string` | da | între 1 și 200 caractere | | `mesaje[].carduri[].produsId` | `string` | nu | între 1 și 60 caractere | | `mesaje[].carduri[].categorie` | `string` | nu | între 1 și 100 caractere | | `mesaje[].carduri[].marcat` | `string` | nu | între 1 și 60 caractere | | `mesaje[].carduri[].poza` | `string` | nu | cel mult 500 caractere | | `mesaje[].carduri[].etichete` | `string[]` | da | cel mult 4 elemente | | `mesaje[].carduri[].cifre` | `object[]` | da | cel mult 4 elemente | | `mesaje[].carduri[].stare` | `object` | nu | — | | `mesaje[].carduri[].link` | `object` | da | — | | `mesaje[].carduri[].actiune` | `string` | nu | între 1 și 40 caractere | | `mesaje[].carduri[].motiv` | `string` | nu | cel mult 300 caractere | | `mesaje[].urmatorul` | `string[]` | da | cel mult 3 elemente | ```bash curl https://app.ilustromania.com/nucleu/v1/asistent/conversatii/{id}/mesaje \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "mesaje": [ { "id": 1, "rol": "utilizator", "titlu": "text", "text": "text", "carduri": [], "urmatorul": [] } ] } ``` ### POST /v1/asistent/feedback Acces: `sesiune` **Votează un răspuns** Omul dă vot unui mesaj al asistentului. Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `mesajId` | `integer` | da | între 0 și 9007199254740991 | | `vot` | `number \| number` | da | — | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/asistent/feedback \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ "mesajId": 1, "vot": 1 }' ``` ### POST /v1/asistent/mesaj Acces: `sesiune` **Trimite un mesaj asistentului** Întrebarea omului merge la asistentul AI, cu limită pe utilizator (429); 503 fără furnizor configurat. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/asistent/mesaj \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/asistent/stare Acces: `sesiune` **Sănătatea asistentului** Ce furnizori AI sunt configurați, cine a răspuns ultima dată și de ce s-a trecut pe rezervă. ```bash curl https://app.ilustromania.com/nucleu/v1/asistent/stare \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/asistent/voce/sesiune Acces: `sesiune` **Deschide vocea asistentului** Primește oferta SDP a browserului și întoarce răspunsul SDP al sesiunii vocale (OpenAI Realtime prin WebRTC). Aceleași porți ca la mesaj: sesiune, lista asistentului (403), limita pe zi (429); 503 fără cheie, 502 când furnizorul cade. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/asistent/voce/sesiune \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ## Modulul `chat` ### GET /v1/chat/conversatii Acces: `rol:admin` **Conversațiile din chat** Conversațiile echipei în care e omul din sesiune. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `conversatii` | `object[]` | da | — | | `conversatii[].id` | `string` | da | UUID | | `conversatii[].tip` | `string` | da | una din: `direct`, `grup`, `general` | | `conversatii[].nume` | `string` | da | — | | `conversatii[].membri` | `string[]` | da | — | | `conversatii[].ultimulMesaj` | `object` | da | — | | `conversatii[].ultimulMesaj.seq` | `integer` | da | între 0 și 9007199254740991 | | `conversatii[].ultimulMesaj.id` | `string` | da | UUID | | `conversatii[].ultimulMesaj.conversatieId` | `string` | da | UUID | | `conversatii[].ultimulMesaj.autorId` | `string` | da | UUID | | `conversatii[].ultimulMesaj.text` | `string` | da | între 1 și 4000 caractere | | `conversatii[].ultimulMesaj.creatLa` | `string` | da | moment ISO 8601 | | `conversatii[].necitite` | `integer` | da | între 0 și 9007199254740991 | ```bash curl https://app.ilustromania.com/nucleu/v1/chat/conversatii \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "conversatii": [ { "id": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "tip": "direct", "nume": "text", "membri": [], "ultimulMesaj": {}, "necitite": 1 } ] } ``` ### POST /v1/chat/conversatii Acces: `rol:admin` **Deschide o conversație** Creează o conversație directă sau un grup (201 grup nou). ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/chat/conversatii \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### POST /v1/chat/conversatii/{id}/citit Acces: `rol:admin` **Marchează conversația citită** Marchează mesajele citite până la un punct și întoarce totalul necitit. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/chat/conversatii/{id}/citit \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/chat/conversatii/{id}/mesaje Acces: `rol:admin` **Mesajele unei conversații** Mesajele conversației, paginate înapoi de la un cursor. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl https://app.ilustromania.com/nucleu/v1/chat/conversatii/{id}/mesaje \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/chat/conversatii/{id}/mesaje Acces: `rol:admin` **Trimite un mesaj în chat** Adaugă un mesaj în conversație și anunță fluxul live; limită pe cont (429). Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/chat/conversatii/{id}/mesaje \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/chat/flux Acces: `rol:admin` **Fluxul live al chatului** Flux SSE cu mesajele noi, reluat după Last-Event-ID sau cursor. ```bash curl https://app.ilustromania.com/nucleu/v1/chat/flux \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/chat/necitite Acces: `rol:admin` **Mesaje necitite** Totalul mesajelor necitite ale omului. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `total` | `integer` | da | între 0 și 9007199254740991 | ```bash curl https://app.ilustromania.com/nucleu/v1/chat/necitite \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "total": 1 } ``` --- # Centrul Claude 27 de rute, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ## Modulul `claude` ### GET /v1/claude/alocare Acces: `rol:admin` **Alocarea Codex/Claude** Procentul de lucru alocat Codex și Claude, pentru sesiunea autorizată. ```bash curl https://app.ilustromania.com/nucleu/v1/claude/alocare \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PUT /v1/claude/alocare Acces: `sesiune` **Schimbă alocarea Codex/Claude** Scrie procentul de lucru alocat Codex (restul la Claude). ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/claude/alocare \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### POST /v1/claude/alocare/codex Acces: `sesiune` **Raportează consumul Codex** Jetonul de serviciu scrie statistica de consum Codex folosită la alocare. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/claude/alocare/codex \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/claude/alocare/garda Acces: `sesiune` **Alocarea pentru gardă** Aceeași alocare Codex/Claude, citită de garda de pe mașini. ```bash curl https://app.ilustromania.com/nucleu/v1/claude/alocare/garda \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/claude/cereri Acces: `rol:admin` **Registrul cererilor** Cererile lui Mihai și starea lor, așa cum le-au scris mașinile. ```bash curl https://app.ilustromania.com/nucleu/v1/claude/cereri \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/claude/cereri Acces: `sesiune` **Scrie registrul cererilor** Mașinile publică instantaneul registrului de cereri (corp până la 512 KB). ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/claude/cereri \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/claude/costuri Acces: `rol:admin` **Costurile agenților** Costurile de folosire raportate de agenți. ```bash curl https://app.ilustromania.com/nucleu/v1/claude/costuri \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/claude/costuri Acces: `sesiune` **Scrie costurile** Mașinile publică costurile de folosire ale agenților. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/claude/costuri \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/claude/harta Acces: `rol:admin` **Harta agenților** Nodurile și legăturile din hărțile publicate de mașini, unite într-un graf. ```bash curl https://app.ilustromania.com/nucleu/v1/claude/harta \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/claude/harta Acces: `sesiune` **Scrie harta unei mașini** O mașină își publică harta (noduri și legături). ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/claude/harta \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/claude/pasi Acces: `rol:admin` **Rulările cu pași** Lista rulărilor de agent pentru care s-au salvat pași. ```bash curl https://app.ilustromania.com/nucleu/v1/claude/pasi \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/claude/pasi Acces: `sesiune` **Scrie pașii unei rulări** Mașinile salvează pașii unei rulări de agent. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/claude/pasi \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/claude/pasi/{rulare} Acces: `rol:admin` **Pașii unei rulări** Pașii salvați pentru o rulare de agent; 400 identificator invalid. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `rulare` | `string` | valoarea din cale | ```bash curl https://app.ilustromania.com/nucleu/v1/claude/pasi/{rulare} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/claude/porniri Acces: `rol:admin` **Acțiunile de pornire** Acțiunile de pornire disponibile pentru agenți și coada lor. ```bash curl https://app.ilustromania.com/nucleu/v1/claude/porniri \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/claude/porniri Acces: `rol:admin` **Cere o pornire** Pune în coadă o acțiune de pornire cunoscută; 404 acțiune necunoscută, 503 fără dosar. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/claude/porniri \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/claude/stare Acces: `rol:admin` **Starea mașinilor** Ultimele stări raportate de mașini, doar cele proaspete. ```bash curl https://app.ilustromania.com/nucleu/v1/claude/stare \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/claude/stare Acces: `sesiune` **Scrie starea unei mașini** O mașină își publică starea curentă; 404 fără depozit. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/claude/stare \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/claude/taskuri Acces: `rol:admin` **Taskurile agenților** Lista taskurilor agenților; 404 fără depozit. ```bash curl https://app.ilustromania.com/nucleu/v1/claude/taskuri \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/claude/taskuri Acces: `sesiune` **Scrie taskuri** Mașinile adaugă sau actualizează unul sau mai multe taskuri. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/claude/taskuri \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/claude/taskuri/flux Acces: `rol:admin` **Fluxul live al taskurilor** Flux SSE cu schimbările din lista de taskuri. ```bash curl https://app.ilustromania.com/nucleu/v1/claude/taskuri/flux \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/claude/urcari Acces: `rol:admin` **Urcările pe app** Urcările recente pe aplicație, cu graficul din jurnalul de urcări. ```bash curl https://app.ilustromania.com/nucleu/v1/claude/urcari \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/claude/urcari Acces: `sesiune` **Scrie urcările** Mașinile publică lista urcărilor pe aplicație. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/claude/urcari \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ## Modulul `agenti` ### GET /v1/agenti Acces: `sesiune` **Agenții firmei** Registrul agenților firmei din sesiune (administrator sau patron). Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `agenti` | `object[]` | da | — | | `agenti[].id` | `string` | da | nevid | | `agenti[].nume` | `string` | da | nevid | | `agenti[].titlu` | `string` | da | nevid | | `agenti[].total` | `integer` | da | între 0 și 9007199254740991 | | `agenti[].erori` | `integer` | da | între 0 și 9007199254740991 | | `agenti[].ultima` | `string` | da | dată `AAAA-LL-ZZ` | | `agenti[].clone` | `integer` | da | între 0 și 9007199254740991; implicit `0` | | `agenti[].taskuri` | `string[]` | da | — | | `agenti[].responsabilitati` | `string[]` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/agenti \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "agenti": [ { "id": "text", "nume": "text", "titlu": "text", "total": 1, "erori": 1, "ultima": "2026-11-12", "clone": 0, "taskuri": [], "responsabilitati": [] } ] } ``` ### GET /v1/agenti/{id}/istoric Acces: `sesiune` **Istoricul unui agent** Ultimele 20 de rapoarte ale unui agent al firmei. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `id` | `string` | da | — | | `istoric` | `object[]` | da | — | | `istoric[].ora` | `string` | da | — | | `istoric[].text` | `string` | da | — | | `istoric[].eticheta` | `string` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/agenti/{id}/istoric \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "id": "text", "istoric": [ { "ora": "text", "text": "text", "eticheta": "text" } ] } ``` ### GET /v1/agenti/lucru Acces: `sesiune` **Ce lucrează agenții acum** Starea instanțelor agenților firmei din sesiune (administrator sau patron); 404 fără registru de lucru. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `ok` | `boolean` | da | una din: `true` | | `acum` | `string` | da | — | | `lucreaza_acum` | `string[]` | da | — | | `lucru` | `object` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/agenti/lucru \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "ok": true, "acum": "text", "lucreaza_acum": [ "text" ], "lucru": {} } ``` ### POST /v1/agenti/lucru Acces: `sesiune` **Semnal de lucru al unui agent** Agentul anunță start, bătaie sau stop, cu sarcina și ultimul raport; cu jeton de executor sau sesiune de administrator/patron. Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `id` | `string` | da | între 1 și 40 caractere | | `stare` | `string` | da | — | | `task` | `string` | nu | — | | `eticheta` | `string` | nu | — | | `preia` | `string` | nu | — | | `ultimul_raport_ts` | `number \| string` | nu | — | | `ultimul_raport_text` | `string` | nu | — | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `ok` | `boolean` | da | una din: `true` | | `id` | `string` | da | — | | `nume` | `string` | da | — | | `stare` | `string` | da | — | | `eticheta` | `string` | da | — | | `lucru` | `object` | da | — | | `lucru.lucreaza` | `boolean` | da | — | | `lucru.task` | `string` | da | — | | `lucru.de_la` | `string` | da | — | | `lucru.pana_la` | `string` | da | — | | `lucru.ultim_semn` | `string` | da | — | | `lucru.expira_la` | `string` | da | — | | `lucru.ultimul_raport` | `string` | da | — | | `lucru.ultimul_raport_text` | `string` | da | — | | `lucru.instante` | `integer` | da | între 0 și 9007199254740991 | | `lucru.etichete` | `string[]` | da | — | | `lucru.instante_detalii` | `object[]` | da | — | | `lucru.instante_detalii[].lucreaza` | `boolean` | da | — | | `lucru.instante_detalii[].task` | `string` | da | — | | `lucru.instante_detalii[].de_la` | `string` | da | — | | `lucru.instante_detalii[].pana_la` | `string` | da | — | | `lucru.instante_detalii[].ultim_semn` | `string` | da | — | | `lucru.instante_detalii[].expira_la` | `string` | da | — | | `lucru.instante_detalii[].ultimul_raport` | `string` | da | — | | `lucru.instante_detalii[].ultimul_raport_text` | `string` | da | — | | `lucru.instante_detalii[].eticheta` | `string` | da | — | | `lucreaza_acum` | `string[]` | da | — | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/agenti/lucru \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ "id": "text", "stare": "text", "task": "text", "eticheta": "text", "preia": "text", "ultimul_raport_ts": 12.5, "ultimul_raport_text": "text" }' ``` Răspuns 200 (exemplu din schemă): ```json { "ok": true, "id": "text", "nume": "text", "stare": "text", "eticheta": "text", "lucru": { "lucreaza": true, "task": "text", "de_la": "text", "pana_la": "text", "ultim_semn": "text", "expira_la": "text", "ultimul_raport": "text", "ultimul_raport_text": "text", "instante": 1, "etichete": [ "text" ], "instante_detalii": [ {} ] }, "lucreaza_acum": [ "text" ] } ``` ## Modulul `jurnal-live` ### GET /v1/jurnal-live Acces: `rol:admin` **Jurnalul live al platformei** Citește jurnalul live al executorilor. Doar administratorul firmei platformei; altfel 401/403. ```bash curl https://app.ilustromania.com/nucleu/v1/jurnal-live \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` --- # Automatizări 8 rute, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ## Modulul `automatizari` ### GET /v1/automatizari Acces: `rol:admin` **Automatizările firmei** Registrul automatizărilor cu fluxurile, orele, starea ultimei rulări și alegerile firmei (administrator). ```bash curl https://app.ilustromania.com/nucleu/v1/automatizari \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### DELETE /v1/automatizari/{id} Acces: `rol:admin` **Șterge o automatizare din arhivă** Șterge alegerile unei automatizări deja oprite (doar din arhivă). Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X DELETE https://app.ilustromania.com/nucleu/v1/automatizari/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/automatizari/{id} Acces: `rol:admin` **Modifică o automatizare** Administratorul salvează setările alese pentru o automatizare. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/automatizari/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### POST /v1/automatizari/{id}/activ Acces: `rol:admin` **Pornește sau oprește o automatizare** Administratorul schimbă starea activ a unei automatizări. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/automatizari/{id}/activ \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ## Modulul `update-uri` ### GET /v1/update-uri Acces: `sesiune` **Update-urile aplicației** Noutățile aplicației pentru omul logat, cu numărul celor nevăzute. ```bash curl https://app.ilustromania.com/nucleu/v1/update-uri \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/update-uri Acces: `sesiune` **Adaugă un update** Publică o noutate în aplicație; doar administratorul, 409 `versiune_existenta`. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/update-uri \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### POST /v1/update-uri/{id}/vazut Acces: `sesiune` **Marchează update-ul văzut** Notează că omul a văzut update-ul `:id`; 404 dacă nu există. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/update-uri/{id}/vazut \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/update-uri/poza Acces: `sesiune` **Captura unui update** Servește captura de ecran a unui update după nume de fișier validat; doar rolurile de vânzări. ```bash curl https://app.ilustromania.com/nucleu/v1/update-uri/poza \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` --- # Profil și preferințe 37 de rute, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ## Modulul `eu` ### GET /v1/eu Acces: `sesiune` **Cine sunt eu** Contul din sesiune: identitate, firmă activă, rol și paginile permise. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `contId` | `string` | da | nevid | | `nume` | `string` | da | — | | `rol` | `string` | da | una din: `admin`, `bucatar`, `casier`, `gestiune`, `patron`, `marketing`, `angajat`, `finante` | | `admin` | `boolean` | da | — | | `sursa` | `string` | da | una din: `baza`, `sesiune` | | `firma` | `object` | da | — | | `firma.id` | `string` | da | UUID | | `firma.nume` | `string` | da | — | | `firma.tip` | `string` | da | una din: `restaurant`, `agentie`, `gol` | | `firma.module` | `string[]` | da | — | | `firma.cui` | `string` | nu | — | | `firma.regCom` | `string` | nu | — | | `firma.sediu` | `string` | nu | — | | `trebuieParolaNoua` | `boolean` | da | implicit `false` | | `trebuieBunVenit` | `boolean` | da | implicit `false` | | `asistentPrezentat` | `boolean` | da | implicit `true` | | `areEmail` | `boolean` | da | implicit `true` | | `poza` | `string` | da | cel mult 200 caractere; implicit `null` | | `pagini` | `string[]` | da | implicit `[]` | | `rolurileFirmei` | `object[]` | da | implicit `[]` | | `rolurileFirmei[].cheie` | `string` | da | — | | `rolurileFirmei[].nume` | `string` | da | între 1 și 60 caractere | | `rolurileFirmei[].sistem` | `boolean` | da | — | | `adminReal` | `boolean` | da | implicit `false` | | `caRol` | `string` | da | implicit `null` | | `vedeEvidenta` | `boolean` | da | implicit `false` | | `vedeArhiva` | `boolean` | da | implicit `false` | | `vedeFinder` | `boolean` | da | implicit `false` | | `vedeDispozitive` | `boolean` | da | implicit `false` | | `vedePlante` | `boolean` | da | implicit `false` | | `vedeStatisticiLivrare` | `boolean` | da | implicit `false` | ```bash curl https://app.ilustromania.com/nucleu/v1/eu \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "contId": "text", "nume": "text", "rol": "admin", "admin": true, "sursa": "baza", "firma": { "id": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "nume": "text", "tip": "restaurant", "module": [ "text" ], "cui": "text", "regCom": "text", "sediu": "text" }, "trebuieParolaNoua": false, "trebuieBunVenit": false, "asistentPrezentat": true, "areEmail": true, "poza": null, "pagini": [] } ``` ### POST /v1/eu/firma Acces: `sesiune` **Comută firma activă** Schimbă firma activă a sesiunii; 403 fără acces la firma cerută. Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `firmaId` | `string` | da | UUID | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `firmaId` | `string` | da | UUID | | `rol` | `string` | da | una din: `admin`, `bucatar`, `casier`, `gestiune`, `patron`, `marketing`, `angajat`, `finante` | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/eu/firma \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ "firmaId": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f" }' ``` Răspuns 200 (exemplu din schemă): ```json { "firmaId": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "rol": "admin" } ``` ### GET /v1/eu/firme Acces: `sesiune` **Firmele contului** Firmele la care are acces contul, pentru comutatorul de firmă. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `firme` | `object[]` | da | — | | `firme[].firmaId` | `string` | da | UUID | | `firme[].nume` | `string` | da | — | | `firme[].rol` | `string` | da | una din: `admin`, `bucatar`, `casier`, `gestiune`, `patron`, `marketing`, `angajat`, `finante` | | `firme[].activa` | `boolean` | da | — | | `firme[].tip` | `string` | da | una din: `restaurant`, `agentie`, `gol` | | `firme[].module` | `string[]` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/eu/firme \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "firme": [ { "firmaId": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "nume": "text", "rol": "admin", "activa": true, "tip": "restaurant", "module": [] } ] } ``` ### GET /v1/eu/flux Acces: `sesiune` **Fluxul live al contului** Flux SSE care retrimite răspunsul /v1/eu/tot când se schimbă ceva. ```bash curl https://app.ilustromania.com/nucleu/v1/eu/flux \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/eu/pagini-noi Acces: `sesiune` **Paginile noi pentru mine** Paginile omului pe firma activă pe care nu le-a văzut încă. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `pagini` | `string[]` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/eu/pagini-noi \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "pagini": [ "text" ] } ``` ### POST /v1/eu/pagini-noi/vazut Acces: `sesiune` **Marchează paginile văzute** Marchează paginile date ca văzute, pe toate dispozitivele omului. Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `pagini` | `string[]` | da | cel mult 200 elemente | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `marcate` | `integer` | da | între 0 și 9007199254740991 | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/eu/pagini-noi/vazut \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ "pagini": [ "text" ] }' ``` Răspuns 200 (exemplu din schemă): ```json { "marcate": 1 } ``` ### GET /v1/eu/tot Acces: `sesiune` **Tot ce trebuie shell-ului** Contul, firmele, paginile și prezența într-un singur răspuns pentru shell. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `eu` | `object` | da | — | | `eu.contId` | `string` | da | nevid | | `eu.nume` | `string` | da | — | | `eu.rol` | `string` | da | una din: `admin`, `bucatar`, `casier`, `gestiune`, `patron`, `marketing`, `angajat`, `finante` | | `eu.admin` | `boolean` | da | — | | `eu.sursa` | `string` | da | una din: `baza`, `sesiune` | | `eu.firma` | `object` | da | — | | `eu.firma.id` | `string` | da | UUID | | `eu.firma.nume` | `string` | da | — | | `eu.firma.tip` | `string` | da | una din: `restaurant`, `agentie`, `gol` | | `eu.firma.module` | `string[]` | da | — | | `eu.firma.cui` | `string` | nu | — | | `eu.firma.regCom` | `string` | nu | — | | `eu.firma.sediu` | `string` | nu | — | | `eu.trebuieParolaNoua` | `boolean` | da | implicit `false` | | `eu.trebuieBunVenit` | `boolean` | da | implicit `false` | | `eu.asistentPrezentat` | `boolean` | da | implicit `true` | | `eu.areEmail` | `boolean` | da | implicit `true` | | `eu.poza` | `string` | da | cel mult 200 caractere; implicit `null` | | `eu.pagini` | `string[]` | da | implicit `[]` | | `eu.rolurileFirmei` | `object[]` | da | implicit `[]` | | `eu.rolurileFirmei[].cheie` | `string` | da | — | | `eu.rolurileFirmei[].nume` | `string` | da | între 1 și 60 caractere | | `eu.rolurileFirmei[].sistem` | `boolean` | da | — | | `eu.adminReal` | `boolean` | da | implicit `false` | | `eu.caRol` | `string` | da | implicit `null` | | `eu.vedeEvidenta` | `boolean` | da | implicit `false` | | `eu.vedeArhiva` | `boolean` | da | implicit `false` | | `eu.vedeFinder` | `boolean` | da | implicit `false` | | `eu.vedeDispozitive` | `boolean` | da | implicit `false` | | `eu.vedePlante` | `boolean` | da | implicit `false` | | `eu.vedeStatisticiLivrare` | `boolean` | da | implicit `false` | | `firme` | `object` | da | — | | `firme.firme` | `object[]` | da | — | | `firme.firme[].firmaId` | `string` | da | UUID | | `firme.firme[].nume` | `string` | da | — | | `firme.firme[].rol` | `string` | da | una din: `admin`, `bucatar`, `casier`, `gestiune`, `patron`, `marketing`, `angajat`, `finante` | | `firme.firme[].activa` | `boolean` | da | — | | `firme.firme[].tip` | `string` | da | una din: `restaurant`, `agentie`, `gol` | | `firme.firme[].module` | `string[]` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/eu/tot \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "eu": { "contId": "text", "nume": "text", "rol": "admin", "admin": true, "sursa": "baza", "firma": { "id": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "nume": "text", "tip": "restaurant", "module": [], "cui": "text", "regCom": "text", "sediu": "text" }, "trebuieParolaNoua": false, "trebuieBunVenit": false, "asistentPrezentat": true, "areEmail": true, "poza": null, "pagini": [] }, "firme": { "firme": [ {} ] }, "alerte": { "scris_la": "text", "alerte": [ {} ] }, "alerteStare": "ok", "oameni": { "prezentaLa": "2026-11-12T09:30:00.000Z", "oameni": [ {} ] } } ``` ### POST /v1/eu/vezi-ca Acces: `sesiune` **Vezi aplicația ca alt rol** Administratorul real comută sesiunea curentă să vadă aplicația ca alt rol sau revine (null). Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `rol` | `string` | da | — | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `caRol` | `string` | da | — | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/eu/vezi-ca \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ "rol": "text" }' ``` Răspuns 200 (exemplu din schemă): ```json { "caRol": "text" } ``` ### GET /v1/oameni Acces: `sesiune` **Oamenii firmei active** Lista oamenilor din firma activă, cu prezența lor. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `prezentaLa` | `string` | da | moment ISO 8601 | | `oameni` | `object[]` | da | — | | `oameni[].contId` | `string` | da | UUID | | `oameni[].nume` | `string` | da | nevid | | `oameni[].initiale` | `string` | da | — | | `oameni[].activ` | `boolean` | da | — | | `oameni[].esteEu` | `boolean` | da | — | | `oameni[].agent` | `boolean` | nu | — | | `oameni[].rol` | `string` | nu | una din: `admin`, `bucatar`, `casier`, `gestiune`, `patron`, `marketing`, `angajat`, `finante` | | `oameni[].ultimaActivitate` | `string` | nu | moment ISO 8601 | | `oameni[].email` | `string` | nu | — | | `oameni[].pagina` | `string` | nu | — | | `oameni[].sesiuneDin` | `string` | nu | moment ISO 8601 | ```bash curl https://app.ilustromania.com/nucleu/v1/oameni \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "prezentaLa": "2026-11-12T09:30:00.000Z", "oameni": [ { "contId": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "nume": "text", "initiale": "text", "activ": true, "esteEu": true, "agent": true, "rol": "admin", "ultimaActivitate": "2026-11-12T09:30:00.000Z", "email": "text", "pagina": "text", "sesiuneDin": "2026-11-12T09:30:00.000Z" } ] } ``` ### POST /v1/prezenta/bataie Acces: `sesiune` **Sunt aici** Bătaia de prezență a contului din sesiune; cel mult o scriere pe interval. Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `pagina` | `string` | da | — | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `scrisa` | `boolean` | da | — | | `bataieLa` | `string` | da | moment ISO 8601 | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/prezenta/bataie \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ "pagina": "text" }' ``` Răspuns 200 (exemplu din schemă): ```json { "scrisa": true, "bataieLa": "2026-11-12T09:30:00.000Z" } ``` ## Modulul `profil` ### GET /v1/profil/bun-venit Acces: `sesiune` **Profilul de bun venit** Profilul de bun venit al omului logat (inclusiv ciorna); 403 fără profil în firmă. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `firma` | `string` | da | — | | `numeComplet` | `string` | da | — | | `functie` | `string` | da | — | | `program` | `object[]` | da | cel mult 7 elemente | | `program[].zi` | `integer` | da | între 1 și 7 | | `program[].de` | `string` | da | — | | `program[].pana` | `string` | da | — | | `pozaUrl` | `string` | da | — | | `terminat` | `boolean` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/profil/bun-venit \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "firma": "text", "numeComplet": "text", "functie": "text", "program": [ { "zi": 1, "de": "text", "pana": "text" } ], "pozaUrl": "text", "terminat": true } ``` ### PATCH /v1/profil/bun-venit Acces: `sesiune` **Salvează ciorna de bun venit** Scrie ciorna profilului de bun venit, fără să o finalizeze; 204. ```bash curl -X PATCH https://app.ilustromania.com/nucleu/v1/profil/bun-venit \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### PUT /v1/profil/bun-venit Acces: `sesiune` **Finalizează profilul de bun venit** Scrie profilul de bun venit complet și îl marchează terminat; 204, 400 date nevalide. ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/profil/bun-venit \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### DELETE /v1/profil/poza Acces: `sesiune` **Scoate poza de profil** Scoate poza proprie; fișierul se mută în arhivă, nu se șterge. 204. ```bash curl -X DELETE https://app.ilustromania.com/nucleu/v1/profil/poza \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/profil/poza Acces: `sesiune` **Poza mea de profil** Poza proprie, aflată din rândul contului; 404 fără poză. ```bash curl https://app.ilustromania.com/nucleu/v1/profil/poza \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PUT /v1/profil/poza Acces: `sesiune` **Încarcă poza de profil** Primește poza proprie (multipart, cu plafon de mărime) și o pune pe cont. ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/profil/poza \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/profil/poza/{contId} Acces: `sesiune` **Poza unui coleg** Poza unui om din aceeași firmă activă (panoul „cine are acces”); nimic altceva din profil. 404 fără poză. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `contId` | `string` | valoarea din cale | ```bash curl https://app.ilustromania.com/nucleu/v1/profil/poza/{contId} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ## Modulul `preferinte` ### GET /v1/preferinte/accent Acces: `sesiune` **Accentul meu** Culoarea de accent aleasă din Setări, pe cont și firma activă, sau `null`. ```bash curl https://app.ilustromania.com/nucleu/v1/preferinte/accent \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PUT /v1/preferinte/accent Acces: `sesiune` **Salvează accentul** Scrie culoarea de accent; 400 accent necunoscut. ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/preferinte/accent \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### PUT /v1/preferinte/asistent_prezentat Acces: `sesiune` **Asistentul a fost prezentat** Notează că prezentarea asistentului nu se mai arată pe firma activă. ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/preferinte/asistent_prezentat \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/preferinte/coloane Acces: `sesiune` **Coloanele alese într-un tabel** Coloanele alese de om pentru tabelul `?tabel=`, sau `null` pentru cele implicite. ```bash curl https://app.ilustromania.com/nucleu/v1/preferinte/coloane \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PUT /v1/preferinte/coloane Acces: `sesiune` **Salvează coloanele unui tabel** Scrie coloanele alese pentru un tabel; `null` revine la implicit. ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/preferinte/coloane \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/preferinte/design-system.de-sters Acces: `sesiune` **Elementele marcate de șters în design system** Lista elementelor din design system marcate de om pentru ștergere. ```bash curl https://app.ilustromania.com/nucleu/v1/preferinte/design-system.de-sters \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PUT /v1/preferinte/design-system.de-sters Acces: `sesiune` **Salvează elementele de șters** Înlocuiește lista elementelor marcate de șters (cel mult 200, fără dubluri). ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/preferinte/design-system.de-sters \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/preferinte/meniu_inchise Acces: `sesiune` **Grupurile închise din meniul lateral** Grupurile pe care omul le ține închise în meniul lateral. ```bash curl https://app.ilustromania.com/nucleu/v1/preferinte/meniu_inchise \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PUT /v1/preferinte/meniu_inchise Acces: `sesiune` **Salvează grupurile închise** Scrie lista grupurilor închise, fără dubluri; 400 listă invalidă. ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/preferinte/meniu_inchise \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/preferinte/notificari_citite Acces: `sesiune` **Notificările marcate citite** Id-urile notificărilor marcate citite de om. ```bash curl https://app.ilustromania.com/nucleu/v1/preferinte/notificari_citite \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PUT /v1/preferinte/notificari_citite Acces: `sesiune` **Salvează notificările citite** Înlocuiește lista notificărilor citite (cel mult 200, fără dubluri; 400 peste plafon). ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/preferinte/notificari_citite \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/preferinte/tema Acces: `sesiune` **Tema mea** Tema aleasă (luminoasă/întunecată) pe cont și firma activă, sau `null`. ```bash curl https://app.ilustromania.com/nucleu/v1/preferinte/tema \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PUT /v1/preferinte/tema Acces: `sesiune` **Salvează tema** Scrie tema pe cont și firma activă; 400 temă necunoscută. ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/preferinte/tema \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/preferinte/vedere Acces: `sesiune` **Vederea aleasă pe o pagină** Vederea `galerie` sau `lista` aleasă pentru pagina `?pagina=`, sau `null`. ```bash curl https://app.ilustromania.com/nucleu/v1/preferinte/vedere \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PUT /v1/preferinte/vedere Acces: `sesiune` **Salvează vederea paginii** Scrie vederea aleasă pentru o pagină. ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/preferinte/vedere \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ## Modulul `preferinte-cont` ### GET /v1/eu/aranjament/dashboard Acces: `sesiune` **Aranjamentul dashboardului meu** Ordinea cardurilor din dashboard pentru omul logat; fără salvare, lista goală. ```bash curl https://app.ilustromania.com/nucleu/v1/eu/aranjament/dashboard \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PUT /v1/eu/aranjament/dashboard Acces: `sesiune` **Salvează aranjamentul dashboardului** Scrie ordinea cardurilor din dashboard; 400 aranjament invalid, 403 pe contul fără preferințe. ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/eu/aranjament/dashboard \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/eu/preferinte Acces: `sesiune` **Preferințele contului meu** Preferințele contului logat, sau valorile implicite. ```bash curl https://app.ilustromania.com/nucleu/v1/eu/preferinte \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PUT /v1/eu/preferinte Acces: `sesiune` **Salvează preferințele contului** Scrie preferințele contului și le întoarce recitite din bază; 403 pe contul fără preferințe. ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/eu/preferinte \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ## Modulul `flaguri` ### GET /v1/flaguri Acces: `sesiune` **Flagurile active** Funcțiile pornite pentru omul din sesiune. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `flaguri` | `object` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/flaguri \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "flaguri": {} } ``` --- # Notificări 8 rute, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ### GET /v1/eu/notificari Acces: `sesiune` **Notificările mele** Pagina de notificări a omului logat, cu cursor `urmator`; contul de probă primește lista goală. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `notificari` | `object[]` | da | — | | `notificari[].id` | `string` | da | UUID | | `notificari[].tip` | `string` | da | una din: `mentiune`, `activitate` | | `notificari[].cineNume` | `string` | da | între 1 și 120 caractere | | `notificari[].cineContId` | `string` | da | UUID | | `notificari[].text` | `string` | da | între 1 și 500 caractere | | `notificari[].legatura` | `string` | da | cel mult 500 caractere | | `notificari[].creataLa` | `string` | da | moment ISO 8601 | | `notificari[].citita` | `boolean` | da | — | | `urmator` | `string` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/eu/notificari \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "notificari": [ { "id": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "tip": "mentiune", "cineNume": "text", "cineContId": "3f0c2a8e-6b1d-4c3a-9e2f-1a2b3c4d5e6f", "text": "text", "legatura": "text", "creataLa": "2026-11-12T09:30:00.000Z", "citita": true } ], "urmator": "text" } ``` ### POST /v1/eu/notificari/{id}/citita Acces: `sesiune` **Marchează o notificare citită** Marchează notificarea `:id` ca citită; 404 dacă nu e a omului. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `marcate` | `integer` | da | între 0 și 9007199254740991 | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/eu/notificari/{id}/citita \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` Răspuns 200 (exemplu din schemă): ```json { "marcate": 1 } ``` ### POST /v1/eu/notificari/citite Acces: `sesiune` **Marchează toate notificările citite** Marchează ca citite toate notificările omului (sau pe cele din corp); 403 pe contul de probă. Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `tip` | `string` | nu | una din: `mentiune`, `activitate` | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `marcate` | `integer` | da | între 0 și 9007199254740991 | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/eu/notificari/citite \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ "tip": "mentiune" }' ``` Răspuns 200 (exemplu din schemă): ```json { "marcate": 1 } ``` ### GET /v1/eu/notificari/istoric Acces: `sesiune` **Istoricul alertelor mele** Alertele omului logat, opțional pe un grup (`?grup=`); 400 la grup necunoscut. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `ok` | `boolean` | da | una din: `true` | | `necitite` | `integer` | da | între 0 și 9007199254740991 | | `vedeTehnice` | `boolean` | da | — | | `notificari` | `object[]` | da | — | | `notificari[].cand` | `string` | da | moment ISO 8601 | | `notificari[].tip` | `string` | da | — | | `notificari[].nume` | `string` | da | — | | `notificari[].grup` | `string` | da | una din: `tehnic`, `utilizator` | | `notificari[].titlu` | `string` | da | — | | `notificari[].text` | `string` | da | — | | `notificari[].url` | `string` | da | — | | `notificari[].nou` | `boolean` | da | — | | `notificari[].n` | `integer` | da | între 1 și 9007199254740991 | | `notificari[].randuri` | `any[]` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/eu/notificari/istoric \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "ok": true, "necitite": 1, "vedeTehnice": true, "notificari": [ { "cand": "2026-11-12T09:30:00.000Z", "tip": "text", "nume": "text", "grup": "tehnic", "titlu": "text", "text": "text", "url": "text", "nou": true, "n": 1, "randuri": [] } ] } ``` ### GET /v1/eu/notificari/necitite Acces: `sesiune` **Numărul de notificări necitite** Câte notificări necitite are omul logat, opțional pe un tip. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `necitite` | `integer` | da | între 0 și 9007199254740991 | ```bash curl https://app.ilustromania.com/nucleu/v1/eu/notificari/necitite \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "necitite": 1 } ``` ### GET /v1/eu/notificari/preferinte Acces: `sesiune` **Preferințele mele de notificare** Ce notificări primește omul logat; contul de probă primește valorile implicite. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `oprite` | `string[]` | da | — | | `linisteDe` | `string` | da | — | | `linistePana` | `string` | da | — | | `vazutLa` | `string` | da | moment ISO 8601 | ```bash curl https://app.ilustromania.com/nucleu/v1/eu/notificari/preferinte \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "oprite": [ "imagine" ], "linisteDe": "text", "linistePana": "text", "vazutLa": "2026-11-12T09:30:00.000Z" } ``` ### POST /v1/eu/notificari/preferinte Acces: `sesiune` **Salvează preferințele de notificare** Scrie preferințele de notificare ale omului logat; 403 pe contul de probă. Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `oprite` | `string[]` | nu | cel mult 64 elemente | | `linisteDe` | `string` | nu | — | | `linistePana` | `string` | nu | — | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `oprite` | `string[]` | da | — | | `linisteDe` | `string` | da | — | | `linistePana` | `string` | da | — | | `vazutLa` | `string` | da | moment ISO 8601 | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/eu/notificari/preferinte \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ "oprite": [ "imagine" ], "linisteDe": "text", "linistePana": "text" }' ``` Răspuns 200 (exemplu din schemă): ```json { "oprite": [ "imagine" ], "linisteDe": "text", "linistePana": "text", "vazutLa": "2026-11-12T09:30:00.000Z" } ``` ### POST /v1/eu/notificari/vazut Acces: `sesiune` **Marchează notificările ca văzute** Notează momentul în care omul a văzut lista de notificări; 403 pe contul de probă. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `vazutLa` | `string` | da | moment ISO 8601 | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/eu/notificari/vazut \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` Răspuns 200 (exemplu din schemă): ```json { "vazutLa": "2026-11-12T09:30:00.000Z" } ``` --- # Căutare O rută, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ### GET /v1/cautare/categorii Acces: `sesiune` **Categoriile de căutare** Categoriile din căutarea globală pe care le poate folosi omul, după paginile lui. Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `categorii` | `string[]` | da | — | ```bash curl https://app.ilustromania.com/nucleu/v1/cautare/categorii \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` Răspuns 200 (exemplu din schemă): ```json { "categorii": [ "toate" ] } ``` --- # Autentificare 8 rute de platformă, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ### POST /v1/auth/cod-email/trimite Acces: `public` **Trimite codul pe email** Trimite un cod de intrare pe emailul dat; răspunde la fel indiferent dacă emailul există. Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `scop` | `string` | da | una din: `parola-noua`, `parola-uitata` | | `email` | `string` | nu | cel mult 254 caractere | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/auth/cod-email/trimite \ -H "Content-Type: application/json" \ -d '{ "scop": "parola-noua", "email": "text" }' ``` ### POST /v1/auth/cod-email/verifica Acces: `public` **Verifică codul din email** Schimbă codul primit pe email pe un jeton; 410 cod expirat, 429 prea multe încercări. Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `scop` | `string` | da | una din: `parola-noua`, `parola-uitata` | | `email` | `string` | nu | cel mult 254 caractere | | `cod` | `string` | da | — | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `jeton` | `string` | da | — | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/auth/cod-email/verifica \ -H "Content-Type: application/json" \ -d '{ "scop": "parola-noua", "email": "text", "cod": "text" }' ``` Răspuns 200 (exemplu din schemă): ```json { "jeton": "text" } ``` ### GET /v1/auth/google Acces: `public` **Configurarea intrării cu Google** Datele publice pentru butonul Google; activ: false dacă nu e configurat. ```bash curl https://app.ilustromania.com/nucleu/v1/auth/google ``` ### POST /v1/auth/intrare Acces: `public` **Intrare cu email și parolă** Verifică datele și deschide sesiunea; limită pe IP (429). Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `utilizator` | `string` | da | între 3 și 320 caractere | | `parola` | `string` | da | între 1 și 256 caractere | | `tineConectat` | `boolean` | nu | — | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `trebuieSchimbataParola` | `boolean` | da | — | | `urmator` | `string` | da | una din: `aplicatie`, `schimba-parola` | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/auth/intrare \ -H "Content-Type: application/json" \ -d '{ "utilizator": "text", "parola": "text", "tineConectat": true }' ``` Răspuns 200 (exemplu din schemă): ```json { "trebuieSchimbataParola": true, "urmator": "aplicatie" } ``` ### POST /v1/auth/intrare-google Acces: `public` **Intrare cu Google** Verifică jetonul Google și deschide sesiunea contului; limită pe IP (429). Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `jeton` | `string` | da | între 10 și 8192 caractere | | `tine_minte` | `boolean` | nu | — | Răspuns 200: | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `trebuieSchimbataParola` | `boolean` | da | una din: `false` | | `urmator` | `string` | da | una din: `aplicatie` | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/auth/intrare-google \ -H "Content-Type: application/json" \ -d '{ "jeton": "text", "tine_minte": true }' ``` Răspuns 200 (exemplu din schemă): ```json { "trebuieSchimbataParola": false, "urmator": "aplicatie" } ``` ### POST /v1/auth/parola-noua Acces: `sesiune` **Setează parola nouă** Contul din sesiune își pune parola nouă (de ex. după parola temporară). Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `jeton` | `string` | da | — | | `parola` | `string` | da | între 10 și 128 caractere | | `deconecteazaTot` | `boolean` | nu | implicit `true` | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/auth/parola-noua \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ "jeton": "text", "parola": "text", "deconecteazaTot": true }' ``` ### POST /v1/auth/parola-uitata/confirma Acces: `public` **Confirmă parola uitată** Pune parola nouă după linkul de parolă uitată; limită pe IP. Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `jeton` | `string` | da | — | | `parola` | `string` | da | între 10 și 128 caractere | | `deconecteazaTot` | `boolean` | nu | implicit `true` | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/auth/parola-uitata/confirma \ -H "Content-Type: application/json" \ -d '{ "jeton": "text", "parola": "text", "deconecteazaTot": true }' ``` ### POST /v1/auth/parola/schimba Acces: `sesiune` **Schimbă parola** Schimbă parola cu parola veche; celelalte sesiuni se închid, dacă nu se cere altfel. Corpul cererii (JSON): | Câmp | Tip | Obligatoriu | Detalii | | --- | --- | --- | --- | | `veche` | `string` | da | între 1 și 256 caractere | | `noua` | `string` | da | între 10 și 128 caractere | | `deconecteazaTot` | `boolean` | nu | implicit `true` | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/auth/parola/schimba \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ "veche": "text", "noua": "text", "deconecteazaTot": true }' ``` --- # OAuth 9 rute de platformă, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ### GET /.well-known/oauth-authorization-server Acces: `public` **Metadatele serverului OAuth** Documentul RFC 8414 al serverului de autorizare: căi, granturi, metode. Public. ```bash curl https://app.ilustromania.com/nucleu/.well-known/oauth-authorization-server ``` ### GET /.well-known/oauth-protected-resource Acces: `public` **Metadatele resursei OAuth** Documentul RFC 9728 al resursei protejate (serverul MCP). Public. ```bash curl https://app.ilustromania.com/nucleu/.well-known/oauth-protected-resource ``` ### POST /v1/oauth/acord Acces: `sesiune` **Acordul OAuth** Omul permite (cod de o folosință) sau refuză (`access_denied`); cererea se consumă o singură dată, doar de pe originea aplicației. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/oauth/acord \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/oauth/autorizeaza Acces: `public` **Autorizarea OAuth** Pornește autorizarea și duce omul la ecranul de acord; la orice nepotrivire răspunde 400, fără redirect. Limitată pe IP. ```bash curl https://app.ilustromania.com/nucleu/v1/oauth/autorizeaza ``` ### GET /v1/oauth/cerere/{id} Acces: `sesiune` **Cererea de pe ecranul de acord** Ce vede omul logat pe ecranul de acord: numele clientului, gazda de întoarcere și firmele contului. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl https://app.ilustromania.com/nucleu/v1/oauth/cerere/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/oauth/inregistreaza Acces: `public` **Înregistrarea dinamică a clientului OAuth** Înregistrare RFC 7591 a unui client OAuth; doar JSON, limitată pe IP pe oră. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/oauth/inregistreaza \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### POST /v1/oauth/jeton Acces: `public` **Jetonul OAuth** Schimbă codul de autorizare sau jetonul de reîmprospătare pe jeton de acces; limitată. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/oauth/jeton \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/oauth/resursa Acces: `public` **Metadatele resursei OAuth (alias)** Același document al resursei protejate, sub prefixul OAuth. Public. ```bash curl https://app.ilustromania.com/nucleu/v1/oauth/resursa ``` ### POST /v1/oauth/revoca Acces: `public` **Revocă jetonul OAuth** Revocă un jeton al clientului (RFC 7009); răspunde 200 gol. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/oauth/revoca \ -H "Content-Type: application/json" \ -d '{ … }' ``` --- # Metadate OAuth 2 rute de platformă, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ### GET /.well-known/oauth-authorization-server/v1/oauth Acces: `public` **Metadatele serverului OAuth (cu emitent)** Același document al serverului de autorizare, pe calea emitentului. Public. ```bash curl https://app.ilustromania.com/nucleu/.well-known/oauth-authorization-server/v1/oauth ``` ### GET /.well-known/oauth-protected-resource/v1/mcp Acces: `public` **Metadatele resursei OAuth pentru MCP** Același document al resursei protejate, pe calea specifică MCP. Public. ```bash curl https://app.ilustromania.com/nucleu/.well-known/oauth-protected-resource/v1/mcp ``` --- # Passkey 4 rute de platformă, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ### POST /v1/passkey/inregistreaza Acces: `sesiune` **Înregistrează passkey-ul** Verifică răspunsul WebAuthn la provocarea de înregistrare și salvează passkey-ul pe contul logat; provocarea se folosește o singură dată. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/passkey/inregistreaza \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### POST /v1/passkey/intra Acces: `public` **Intrarea cu passkey** Verifică semnătura passkey-ului la provocare și deschide sesiunea; limitată pe IP, provocarea se consumă. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/passkey/intra \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### POST /v1/passkey/optiuni-inregistrare Acces: `sesiune` **Opțiunile de înregistrare passkey** Provocarea WebAuthn pentru adăugarea unui passkey pe contul logat; limitată pe cont. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/passkey/optiuni-inregistrare \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### POST /v1/passkey/optiuni-intrare Acces: `public` **Opțiunile de intrare cu passkey** Provocarea WebAuthn pentru intrarea fără parolă; limitată pe IP. Fără cont. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/passkey/optiuni-intrare \ -H "Content-Type: application/json" \ -d '{ … }' ``` --- # Resetarea parolei 2 rute de platformă, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ### POST /v1/resetare/cere Acces: `public` **Cere resetarea parolei** Trimite emailul de resetare a parolei, limitat pe IP și pe email; răspunsul nu arată dacă adresa există. Fără cont. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/resetare/cere \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### POST /v1/resetare/confirma Acces: `public` **Confirmă parola nouă** Pune parola nouă pe baza codului din email; refuză parolele comune. Limitată pe IP. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/resetare/confirma \ -H "Content-Type: application/json" \ -d '{ … }' ``` --- # Acces și firme 33 de rute de platformă, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ### GET /v1/acces/firmele-mele Acces: `sesiune` **Firmele mele** Firmele la care are acces contul din sesiune. ```bash curl https://app.ilustromania.com/nucleu/v1/acces/firmele-mele \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/acces/invitatii Acces: `rol:admin` **Creează o invitație** Administratorul firmei generează un cod de invitație pentru un om nou (drept oameni.creeaza și rol admin în bază). ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/acces/invitatii \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/acces/moduri Acces: `sesiune` **Modurile de acces ale echipei** Lista modurilor de acces ale oamenilor din firma activă (doar administrator). ```bash curl https://app.ilustromania.com/nucleu/v1/acces/moduri \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/acces/oameni Acces: `rol:admin` **Oamenii firmei** Lista conturilor din firma activă, cu rol și stare. ```bash curl https://app.ilustromania.com/nucleu/v1/acces/oameni \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/acces/oameni Acces: `rol:admin` **Adaugă un om** Creează un om nou în firmă (drept oameni.creeaza). ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/acces/oameni \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### PUT /v1/acces/oameni/{contId}/mod Acces: `sesiune` **Schimbă modul de acces al unui om** Administratorul setează modul de acces al unui cont din echipă; 404 cont negăsit. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `contId` | `string` | valoarea din cale | ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/acces/oameni/{contId}/mod \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### PUT /v1/acces/oameni/{contId}/pagini Acces: `rol:admin` **Paginile unui om** Înlocuiește lista de pagini a omului pe firma activă (administrator). Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `contId` | `string` | valoarea din cale | ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/acces/oameni/{contId}/pagini \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### PUT /v1/acces/oameni/{contId}/pagini-scoase Acces: `sesiune` **Paginile scoase unui om** Setează lista de pagini ascunse explicit pentru un om din firmă. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `contId` | `string` | valoarea din cale | ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/acces/oameni/{contId}/pagini-scoase \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### PUT /v1/acces/oameni/{contId}/rol-echipa Acces: `sesiune` **Pune un rol de echipă pe om** Administratorul atribuie unui cont din firmă un rol de echipă. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `contId` | `string` | valoarea din cale | ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/acces/oameni/{contId}/rol-echipa \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### PATCH /v1/acces/oameni/{id} Acces: `rol:admin` **Pornește sau oprește accesul unui om** Schimbă starea de acces a unui cont din firmă (drept oameni.modifica). Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X PATCH https://app.ilustromania.com/nucleu/v1/acces/oameni/{id} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/acces/oameni/{id}/drepturi Acces: `rol:admin` **Drepturile unui om** Drepturile efective ale unui cont din firmă (drept drepturi.citeste). Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl https://app.ilustromania.com/nucleu/v1/acces/oameni/{id}/drepturi \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PUT /v1/acces/oameni/{id}/drepturi Acces: `rol:admin` **Setează drepturile unui om** Înlocuiește drepturile individuale ale unui cont (drept drepturi.modifica). Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/acces/oameni/{id}/drepturi \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### POST /v1/acces/oameni/{id}/parola-temporara Acces: `rol:admin` **Resetează parola unui om** Administratorul generează o parolă temporară nouă pentru un cont existent; sesiunile lui se închid. Limită pe administrator (429). Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/acces/oameni/{id}/parola-temporara \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### PATCH /v1/acces/oameni/{id}/rol Acces: `rol:admin` **Schimbă rolul unui om** Schimbă rolul de bază al unui cont din firmă (drept oameni.modifica). Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X PATCH https://app.ilustromania.com/nucleu/v1/acces/oameni/{id}/rol \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### POST /v1/acces/oameni/cont Acces: `rol:admin` **Adaugă un om cu parolă temporară** Creează contul și întoarce o singură dată parola temporară (201). ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/acces/oameni/cont \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/acces/pagini-cu-oameni Acces: `sesiune` **Paginile cu oamenii care le văd** Pentru administratorul firmei: fiecare pagină cu oamenii care au acces la ea. ```bash curl https://app.ilustromania.com/nucleu/v1/acces/pagini-cu-oameni \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/acces/pagini/{pagina}/oameni Acces: `sesiune` **Cine vede o pagină** Oamenii firmei și accesul fiecăruia la pagina dată (doar administrator). Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `pagina` | `string` | valoarea din cale | ```bash curl https://app.ilustromania.com/nucleu/v1/acces/pagini/{pagina}/oameni \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PUT /v1/acces/pagini/{pagina}/oameni/{contId} Acces: `sesiune` **Dă sau ia o pagină unui om** Administratorul pornește sau oprește accesul unui om anume la o pagină. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `pagina` | `string` | valoarea din cale | | `contId` | `string` | valoarea din cale | ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/acces/pagini/{pagina}/oameni/{contId} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### PUT /v1/acces/pagini/{pagina}/oameni/{contId}/editare Acces: `sesiune` **Drept de editare pe pagină pentru om** Administratorul stabilește dacă omul poate edita pe pagina dată. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `pagina` | `string` | valoarea din cale | | `contId` | `string` | valoarea din cale | ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/acces/pagini/{pagina}/oameni/{contId}/editare \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/acces/pagini/{pagina}/roluri Acces: `sesiune` **Rolurile care văd o pagină** Rolurile firmei și accesul fiecăruia la pagina dată; pe firmele doar-admin apare doar rolul admin. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `pagina` | `string` | valoarea din cale | ```bash curl https://app.ilustromania.com/nucleu/v1/acces/pagini/{pagina}/roluri \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PUT /v1/acces/pagini/{pagina}/roluri/{cheie} Acces: `sesiune` **Dă sau ia o pagină unui rol** Administratorul pornește sau oprește accesul unui rol la o pagină. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `pagina` | `string` | valoarea din cale | | `cheie` | `string` | valoarea din cale | ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/acces/pagini/{pagina}/roluri/{cheie} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### PUT /v1/acces/pagini/{pagina}/roluri/{cheie}/editare Acces: `sesiune` **Drept de editare pe pagină pentru rol** Administratorul stabilește dacă rolul poate edita pe pagina dată. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `pagina` | `string` | valoarea din cale | | `cheie` | `string` | valoarea din cale | ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/acces/pagini/{pagina}/roluri/{cheie}/editare \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/acces/roluri Acces: `rol:admin` **Rolurile și drepturile lor** Rolurile de bază ale firmei cu drepturile fiecăruia. ```bash curl https://app.ilustromania.com/nucleu/v1/acces/roluri \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### GET /v1/acces/roluri-echipa Acces: `sesiune` **Rolurile echipei** Rolurile definite pe firma activă (doar administrator). ```bash curl https://app.ilustromania.com/nucleu/v1/acces/roluri-echipa \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/acces/roluri-echipa Acces: `sesiune` **Creează un rol de echipă** Administratorul adaugă un rol nou pe firmă; 409 dacă cheia există deja. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/acces/roluri-echipa \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### DELETE /v1/acces/roluri-echipa/{cheie} Acces: `sesiune` **Șterge un rol de echipă** Șterge un rol creat de firmă; rolurile de sistem nu se pot șterge (403). Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `cheie` | `string` | valoarea din cale | ```bash curl -X DELETE https://app.ilustromania.com/nucleu/v1/acces/roluri-echipa/{cheie} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PUT /v1/acces/roluri-echipa/{cheie} Acces: `sesiune` **Modifică un rol de echipă** Schimbă numele sau setările unui rol existent; 404 rol negăsit. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `cheie` | `string` | valoarea din cale | ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/acces/roluri-echipa/{cheie} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/acces/roluri-echipa/{cheie}/notificari Acces: `sesiune` **Notificările unui rol** Ce notificări primește rolul ales (doar administrator). Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `cheie` | `string` | valoarea din cale | ```bash curl https://app.ilustromania.com/nucleu/v1/acces/roluri-echipa/{cheie}/notificari \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PUT /v1/acces/roluri-echipa/{cheie}/notificari Acces: `sesiune` **Setează notificările unui rol** Administratorul alege ce notificări primește rolul. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `cheie` | `string` | valoarea din cale | ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/acces/roluri-echipa/{cheie}/notificari \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/acces/roluri-toate-firmele Acces: `sesiune` **Rolurile din toate firmele mele** Rolurile tuturor firmelor unde contul e administrator, pentru harta conexiunilor. ```bash curl https://app.ilustromania.com/nucleu/v1/acces/roluri-toate-firmele \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### PUT /v1/acces/roluri/{rol} Acces: `rol:admin` **Setează drepturile unui rol** Înlocuiește drepturile rolului dat (drept roluri.modifica). Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `rol` | `string` | valoarea din cale | ```bash curl -X PUT https://app.ilustromania.com/nucleu/v1/acces/roluri/{rol} \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/invitatii/{cod} Acces: `public` **Verifică o invitație** Spune dacă codul de invitație e valid și încă neconsumat. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `cod` | `string` | valoarea din cale | ```bash curl https://app.ilustromania.com/nucleu/v1/invitatii/{cod} ``` ### POST /v1/invitatii/{cod}/accepta Acces: `public` **Acceptă o invitație** Creează contul omului invitat din nume și email; 404 cod invalid, 409 email deja folosit. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `cod` | `string` | valoarea din cale | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/invitatii/{cod}/accepta \ -H "Content-Type: application/json" \ -d '{ … }' ``` --- # MCP 2 rute de platformă, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ### GET /v1/mcp Acces: `public` **Punctul MCP (GET)** Răspunde la GET pe calea MCP; transportul e doar POST JSON-RPC, fără flux SSE. ```bash curl https://app.ilustromania.com/nucleu/v1/mcp ``` ### POST /v1/mcp Acces: `public` **Serverul MCP (JSON-RPC 2.0)** Primește cereri JSON-RPC 2.0 pentru unelte MCP de citire, apelate intern pe rutele existente cu drepturile lor. Cererile fără jeton `mcp_` valid sunt limitate pe IP. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/mcp \ -H "Content-Type: application/json" \ -d '{ … }' ``` --- # OpenAPI O rută de platformă, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ### GET /v1/openapi.json Acces: `rol:admin` **Documentul OpenAPI al API-ului** Documentul OpenAPI generat din rutele înregistrate, cu descrieri și acces. Doar administratorul; fără cache. ```bash curl https://app.ilustromania.com/nucleu/v1/openapi.json \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` --- # Sănătate 2 rute de platformă, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ### GET /sanatate Acces: `public` **Viața procesului** Răspunde `{stare: "ok"}` cât timp procesul e pornit, fără să atingă baza. Public. ```bash curl https://app.ilustromania.com/nucleu/sanatate ``` ### GET /v1/sanatate Acces: `public` **Sănătatea serviciului** Starea bazei și dacă migrările aplicate sunt la zi față de lista așteptată. Public, fără jurnal. ```bash curl https://app.ilustromania.com/nucleu/v1/sanatate ``` --- # Erori web O rută de platformă, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ### POST /v1/erori-web Acces: `public` **Raportează o eroare din browser** Tunelul prin care paginile trimit erorile din browser; limită pe minut și pe IP. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/erori-web \ -H "Content-Type: application/json" \ -d '{ … }' ``` --- # Chei API 3 rute de platformă, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ### GET /v1/chei-api Acces: `rol:admin` **Cheile API ale firmei** Lista cheilor API ale firmei din sesiune (active întâi, cel mult 200), fără cheia întreagă și fără hash. Doar administratorul, doar din sesiune: o cerere făcută cu o cheie API primește 403. ```bash curl https://app.ilustromania.com/nucleu/v1/chei-api \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/chei-api Acces: `rol:admin` **Creează o cheie API** Corp `{nume, rol, doarCitire?, zile?}` (implicit doar citire, 90 de zile, cel mult 365). Întoarce 201 cu cheia întreagă `ilk_…`, arătată doar acum. 400 corp invalid, 409 `plafon_chei` la 20 de chei active. Doar administratorul, doar din sesiune. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/chei-api \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### POST /v1/chei-api/{id}/revoca Acces: `rol:admin` **Revocă o cheie API** Cheia nu mai trece de la acest moment. 404 dacă e a altei firme, lipsește sau e deja revocată. Doar administratorul, doar din sesiune. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/chei-api/{id}/revoca \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` --- # Webhook-uri 7 rute de platformă, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ### GET /v1/webhookuri Acces: `rol:admin` **Destinațiile webhook ale firmei** Lista destinațiilor webhook ale firmei din sesiune, fără secrete. Doar administratorul (403 altfel); 409 dacă livrarea nu e pornită pe firmă. ```bash curl https://app.ilustromania.com/nucleu/v1/webhookuri \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/webhookuri Acces: `rol:admin` **Adaugă o destinație webhook** Corp `{url, evenimente, descriere?}`; evenimente din factura.emisa, aviz.emis, oferta.creata, comanda_catering.primita. Întoarce 201 `{destinatie, secret}`: secretul `whsec_…` se arată doar acum. 400 corp invalid sau adresă refuzată, 503 fără cheia Seifului. Doar administratorul. ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/webhookuri \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### GET /v1/webhookuri/{id}/livrari Acces: `rol:admin` **Jurnalul livrărilor unei destinații** Cele mai noi 100 de livrări ale destinației (încercare, cod, durată, când). 404 destinație străină sau lipsă. Doar administratorul. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl https://app.ilustromania.com/nucleu/v1/webhookuri/{id}/livrari \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` ### POST /v1/webhookuri/{id}/opreste Acces: `rol:admin` **Oprește o destinație webhook** Destinația nu mai primește nimic, iar livrările în așteptare se anulează. 404 dacă destinația e a altei firme sau lipsește. Doar administratorul. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/webhookuri/{id}/opreste \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### POST /v1/webhookuri/{id}/roteste-secret Acces: `rol:admin` **Rotește secretul destinației** Scrie un secret nou în Seif și îl întoarce o singură dată `{destinatie, secret}`; cel vechi mai semnează 24 de ore. 409 pe destinație oprită, 503 fără cheia Seifului. Doar administratorul. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/webhookuri/{id}/roteste-secret \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### POST /v1/webhookuri/{id}/test Acces: `rol:admin` **Trimite un webhook de probă** Pune o livrare `test.ping` doar spre această destinație și răspunde 202. 404 destinație străină sau lipsă, 409 destinație oprită. Doar administratorul. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/webhookuri/{id}/test \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` ### POST /v1/webhookuri/livrari/{id}/retrimite Acces: `rol:admin` **Retrimite o livrare webhook** Pune din nou același eveniment spre aceeași destinație, de la încercarea 1, și răspunde 202 `{livrare}`. 404 livrare străină sau lipsă, 409 destinație oprită. Doar administratorul. Parametri de cale: | Nume | Tip | Descriere | | --- | --- | --- | | `id` | `string` | identificatorul resursei | ```bash curl -X POST https://app.ilustromania.com/nucleu/v1/webhookuri/livrari/{id}/retrimite \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" \ -H "Content-Type: application/json" \ -d '{ … }' ``` --- # Documentație O rută de platformă, cu exemplu de cerere pentru fiecare. Adresa de bază: `https://app.ilustromania.com/nucleu` ### GET /v1/documentatie Acces: `sesiune` **Documentația firmei** Pagina HTML de documentație a firmei din sesiune, doar a ei, pentru cadrul paginii /documentatie (fără cache, `frame-ancestors 'self'`). 401 fără sesiune, 404 dacă firma nu are documentație sau fișierul lipsește. ```bash curl https://app.ilustromania.com/nucleu/v1/documentatie \ -H "Cookie: __Host-sesiune=$JETON_SESIUNE" ``` --- # Prezentare Ce înseamnă un magazin online în platformă și în ce stare este. Un magazin online este un site în care clientul alege produse, plasează o comandă și, când se poate, plătește cu cardul. Magazinul online din platformă este **în pregătire**. > [!NOTE] > Pagina spune doar ce există la data de 12.11.2026. Ce nu există încă este marcat „în pregătire”, fără date și fără prețuri promise. Ce urmează, vezi [Ce urmează](#ce-urmeaza). --- # Ce urmează Ce este în pregătire pentru magazinele online, fără date promise. > [!WARNING] > Tot ce e pe această pagină este **în pregătire**. Nu există încă în platformă și nu are dată anunțată. ## În pregătire - Magazin online general: coș și comandă pentru orice tip de produs. - Site de magazin construit din Website Builder. - Plata cu cardul în magazin. Când o parte devine disponibilă, apare în [Noutăți](#note-versiune) și se mută din „în pregătire” în pagina corespunzătoare. --- # Comparația cu standardele Unde stă platforma față de documentațiile și standardele de referință: Claude, Webflow, Framer, Standard Webhooks, OWASP, RFC 9457 și OAuth 2.1. ## De ce comparăm Clienții care vin din alte platforme se așteaptă la aceleași reguli: limite anunțate în antete, erori într-un format standard, webhook-uri semnate, chei API separate de parola omului. Tabelele de mai jos arată ce avem deja și ce lipsește. Ce lipsește intră în [Foaia de parcurs](#foaie-de-parcurs). Sursele au fost citite pe 11.11.2026. Starea platformei este cea de pe 12.11.2026; ce s-a adăugat în noiembrie este marcat cu data. ## Platformele de referință | Subiect | Claude | Webflow | Framer | Ilustromania | | --- | --- | --- | --- | --- | | Autentificare pentru API | cheie API pe organizație | jeton de site sau OAuth | cheie API pe proiect (beta) | sesiune; [cheie API](#chei-api) pe firmă, cu rol și expirare (din 12.11.2026, deocamdată doar pentru citirea stocului); jeton `mcp_` pentru MCP | | Mai multe organizații | spații de lucru în organizație | site-uri în spațiu de lucru | proiecte | firme, cu izolare în baza de date (RLS) | | Limite anunțate în antete | da | da (`X-RateLimit-*`) | — | da, pe scrieri: `RateLimit` și `RateLimit-Policy` (din 11.11.2026) | | Webhook-uri semnate | da, pe organizație | da, cu semnătură și marcă de timp | — | da, pe firmă, după Standard Webhooks (din 12.11.2026, [Webhook-uri](#webhook-uri)); plus flux în timp real (SSE) | | Format de eroare | obiect `error` cu `type` | obiect cu cod și mesaj | — | câmpul `eroare`; la cerere RFC 9457, cu `cod` canonic (din 11.11.2026) | | Specificație OpenAPI publică | da | da | — | da, dar doar pentru `admin` și parțială | | Versiunea în adresă | antet de versiune | `/v2` | — | `/v1`, cu [regula de versiuni](#versiuni) scrisă | | Server MCP | — | da | — | da, cu OAuth 2.1 | Pentru casa de marcat DLS nu există documentație publică: integrarea se face prin agenți pe server, descrisă în [Sisteme externe](#sisteme-externe). ## Standardele | Standard | Ce cere | Starea la noi | | --- | --- | --- | | [OWASP API Security Top 10](https://owasp.org/API-Security/) | autorizare pe fiecare obiect, autentificare solidă, limite, inventarul rutelor | autorizare pe firmă prin RLS, acces declarat pe fiecare rută (refuz implicit), limite pe scrieri, inventar generat din cod | | [OAuth 2.1](https://oauth.net/2.1/) | PKCE obligatoriu, fără grant implicit, jetoane de reîmprospătare rotite | respectat: doar `S256`, rotație la fiecare folosire | | [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) | erori `application/problem+json` cu `type`, `title`, `status`, `detail` | există din 11.11.2026, la cerere (`Accept: application/problem+json`): `type`, `title`, `status`, plus `cod` și `id_cerere`; fără `detail`. Fără antet rămâne `{ "eroare": … }` ([Erori](#api-erori)) | | [Standard Webhooks](https://www.standardwebhooks.com) | antete `webhook-id`, `webhook-timestamp`, `webhook-signature`, HMAC-SHA256, reîncercări | există din 12.11.2026: antetele și semnătura `v1` exact după specificație, secret `whsec_`, 10 încercări cu pauze crescătoare, două semnături 24 de ore după rotire, `410` oprește destinația ([Webhook-uri](#webhook-uri)) | | [IETF RateLimit headers](https://datatracker.ietf.org/doc/draft-ietf-httpapi-ratelimit-headers/) | antetele `RateLimit` și `RateLimit-Policy` | există din 11.11.2026, după draft 11, pe scrieri; citirile au doar `429` și `Retry-After` ([Limite](#limite)) | | Chei de idempotență ([`Idempotency-Key`](https://datatracker.ietf.org/doc/draft-ietf-httpapi-idempotency-key-header/)) | o scriere repetată nu se aplică de două ori | există din 11.11.2026, pe 9 rute care creează documente; cheia se ține 24 de ore ([Cereri repetate fără dubluri](#idempotenta)) | | Identificator de cerere | fiecare răspuns are un id care se regăsește în jurnale | există din 11.11.2026: `x-request-id` pe fiecare răspuns, același în jurnalul serverului | ## Ce facem deja peste medie - **Refuz implicit:** o rută fără acces declarat răspunde `401 ruta_nedeclarata` înainte să ruleze. - **Izolarea firmelor în baza de date**, nu doar în cod: chiar o greșeală în aplicație nu poate citi altă firmă. - **Regula 503:** o sursă externă lipsă nu se ascunde niciodată sub o listă goală. - **Parolele:** scrypt cu sare, verificare față de parolele comune și față de ultimele 5 ale contului. - **Jurnale curate:** cookie-urile, antetele de autorizare și câmpurile secrete se înlocuiesc cu `[ascuns]`. --- # Foaia de parcurs Ce lipsește ca platforma să fie un SaaS complet pentru clienți externi, în ordinea priorității, și ce s-a făcut deja. > [!NOTE] > Prioritățile de mai jos descriu lucruri **care nu există încă**. Nu te baza pe ele într-o integrare până nu apar în [Noutăți](#note-versiune). Ce s-a făcut deja este la final, în „Gata”, cu data. Starea de pe 12.11.2026. ## Prioritatea 1: o documentație completă a API-ului | Lucru | De ce | | --- | --- | | Specificația OpenAPI pentru toate rutele, publică | azi `GET /v1/openapi.json` cere `admin` și acoperă doar rutele descrise cu scheme | | Scheme de intrare și de răspuns pe fiecare rută | azi 107 din 398 de rute au tabelele de parametri, corp și răspuns în referință | ## Prioritatea 2: chei API pe mai multe rute | Lucru | De ce | | --- | --- | | Mai multe rute deschise cheilor API | azi o cheie citește doar stocul ([Chei API](#chei-api)); lista crește rută cu rută, după ce fiecare e verificată să decidă accesul doar pe rolul și firma cererii | ## Gata | Lucru | De când | Unde citești | | --- | --- | --- | | Numărul cererii în fiecare răspuns (`x-request-id`) | 11.11.2026 | [Prezentarea API-ului](#api-prezentare--numarul-cererii) | | Antete de limită pe scrieri (`RateLimit`, `RateLimit-Policy`) și `Retry-After` calculat din fereastră | 11.11.2026 | [Limite](#limite--antetele-de-limita) | | Format de eroare RFC 9457, la cerere, cu un `cod` canonic care adună mesajele vechi | 11.11.2026 | [Erori](#api-erori--formatul-rfc-9457-la-cerere) | | `Idempotency-Key` pe 9 rute care creează documente | 11.11.2026 | [Cereri repetate fără dubluri](#idempotenta) | | Regula de versiuni scrisă | 11.11.2026 | [Versiuni API](#versiuni) | | Chei API pe firmă (`ilk_…`), cu rol, doar citire implicit, expirare de cel mult 365 de zile și revocare | 12.11.2026 | [Chei API](#chei-api) | | Webhook-uri semnate după Standard Webhooks, cu reîncercări, rotirea secretului și jurnalul livrărilor | 12.11.2026 | [Webhook-uri](#webhook-uri) | | Descriere pe fiecare rută (398 din 398) | 12.11.2026 | [Referința API](#api-prezentare) | | Tabele de parametri, corp și răspuns, cu exemple curl / JavaScript / Python, pe rutele care au schemă | 12.11.2026 | [Referința API](#api-prezentare) | --- # Glosar Cuvintele platformei, pe scurt. | Termen | Înseamnă | | --- | --- | | Firmă | un client al platformei; datele unei firme nu se văd din alta | | Firma activă | firma pe care lucrează sesiunea în acest moment | | Modul | o parte a platformei (meniu, facturi, creatori…), pornită sau oprită pe fiecare firmă | | Cont | un om care intră în platformă; poate avea acces la mai multe firme | | Rol | nivelul omului în firmă (de exemplu `admin`) | | Drept | o permisiune fină pe un modul, de exemplu `F_MENIU_EDIT` | | Rol de echipă | un grup de oameni cu aceleași pagini și notificări | | Sesiune | intrarea în cont, ținută în cookie-ul `__Host-sesiune` | | Executor | un agent care rulează pe server și face lucrul din coadă, cu jeton de serviciu | | Coadă | lista de sarcini care așteaptă un executor | | Canal | un loc de vânzare: sală, Wolt, Bolt, Glovo, Google | | DLS | casa de marcat a restaurantului | | FGO | programul de facturare | | SPV | Spațiul Privat Virtual ANAF, pentru e-Factura | | Eveniment | un rând din jurnalul schimbărilor, cu număr `seq` | | SSE | Server-Sent Events: fluxul prin care browserul primește evenimentele în timp real | | MCP | Model Context Protocol: felul în care un asistent AI folosește uneltele platformei | | PKCE | protecția fluxului OAuth: clientul dovedește că el a început cererea | | RLS | Row Level Security: regula din baza de date care ascunde rândurile altor firme | | Regula 503 | o sursă externă lipsă răspunde `503` cu motiv, niciodată cu o listă goală | --- # Noutăți Ce s-a schimbat în platformă și în documentație, în ordine inversă, cu data. ## 12 noiembrie 2026: chei API și webhook-uri Adăugiri în `v1`, fără nicio schimbare pentru clienții existenți ([Versiuni API](#versiuni)). - **Chei API pe firmă.** Un Administrator creează chei `ilk_…` cu rol, doar citire implicit și expirare de cel mult 365 de zile; cheia se trimite cu `Authorization: Bearer`. Deocamdată o cheie citește stocul ([Chei API](#chei-api)). - **Webhook-uri semnate.** Evenimentele `factura.emisa`, `aviz.emis`, `oferta.creata` și `comanda_catering.primita` pleacă spre adresele `https` ale firmei, semnate după Standard Webhooks, cu 10 încercări și jurnal ([Webhook-uri](#webhook-uri)). - **Referința API.** Fiecare rută are descriere; 107 de rute au tabelele de parametri, corp și răspuns, iar exemplele de cerere vin în curl, JavaScript și Python. ## 11 noiembrie 2026: API Toate schimbările de mai jos sunt adăugiri în `v1`: un client care nu le folosește primește exact ce primea înainte ([Versiuni API](#versiuni)). - **Numărul cererii.** Fiecare răspuns are antetul `x-request-id`; un `x-request-id` trimis de client se păstrează. - **Antete de limită.** Scrierile primesc `RateLimit` și `RateLimit-Policy`; `Retry-After` spune câte secunde mai sunt până se golește fereastra ([Limite](#limite)). - **Erori RFC 9457, la cerere.** Cu `Accept: application/problem+json`, erorile vin cu `type`, `title`, `status`, `cod` și `id_cerere`; `eroare` rămâne ([Erori](#api-erori)). - **Cereri repetate fără dubluri.** 9 rute care creează documente acceptă `Idempotency-Key` ([Cereri repetate fără dubluri](#idempotenta)). - **Regula de versiuni**, scrisă ([Versiuni API](#versiuni)). ## 11 noiembrie 2026: prima ediție **Prima ediție a documentației.** - Ghidul: introducere, concepte, primii pași și câte o pagină pentru fiecare modul. - Administrarea: firme și module, conturi și invitații, roluri și drepturi, securitate, limite. - Integrările: serverul MCP, OAuth 2.1, fluxul de evenimente, sistemele externe. - Referința API: 398 de rute în 65 de module, generate direct din codul platformei, cu metoda, accesul și un exemplu pentru fiecare. - Resursele: comparația cu standardele, foaia de parcurs, glosarul. > [!TIP] > Referința API se regenerează din cod, deci rămâne la zi cu platforma. Textele explicative se actualizează odată cu modulele.