Emiterea unei facturi
POST /v2/facturi creează și emite o factură: client, articole, număr alocat din registru, totul într-o singură tranzacție. Dacă ceva nu trece, nu rămâne nimic în urmă — nici client, nici factură pe jumătate.
Înainte de primul apel
Trei lucruri de pregătit în FGO, o singură dată:
- Un registru de facturi care conține seria pe care o trimiți. Registrul poate fi comun sau al utilizatorului API.
- O cheie API cu dreptul
facturi:emitere. Vezi Autentificare. - Adresa site-ului din care emiți, înregistrată în Setări eCommerce → API. O trimiți în headerul
Fgo-Url-Platformala fiecare emitere.
Cererea
{
"serie": "FGO",
"valuta": "RON",
"tip_factura": "F",
"data_emitere": "2026-09-03",
"data_scadenta": "2026-09-18",
"id_extern": "comanda-10425",
"verificare_duplicat": true,
"client": {
"tip": "PJ",
"denumire": "Client Exemplu SRL",
"cod_unic": "12345674",
"email": "facturi@client-exemplu.ro",
"tara": "Romania",
"cod_judet": "B",
"localitate": "Bucuresti",
"adresa": "Str. Exemplu nr. 10"
},
"articole": [
{
"cod_articol": "SERV-CONSULT",
"denumire": "Servicii consultanta",
"um": "ora",
"nr_produse": 1,
"pret_unitar": 100,
"cota_tva": 0.21
}
]
}
Obligatorii sunt serie, valuta, tip_factura și articole cu cel puțin un articol. data_emitere lipsă înseamnă azi; data_scadenta lipsă se calculează din setările firmei.
Clientul
client.tip e PJ sau PF. Un client PJ are nevoie de denumire și cod_unic; unul PF de denumire și, dacă îl ai, de CNP în același cod_unic.
FGO caută clientul după cod_unic. Dacă îl găsește, îl folosește; dacă nu, îl creează din ce ai trimis. Pentru persoanele juridice românești, datele oficiale — denumire, număr la Registrul Comerțului, plătitor de TVA, județ, localitate, adresă — se completează din ANAF și pot înlocui ce ai trimis tu. Nu e o eroare: e clientul așa cum e înregistrat oficial.
cod_judet e codul canonic din GET /v2/nomenclatoare/judete (B, IS, CJ), nu numele județului. tara e denumirea din GET /v2/nomenclatoare/tari.
Articolele
Fiecare articol are obligatoriu denumire și nr_produse. Prețul îl dai fie ca pret_unitar, fie ca pret_total.
cota_tva e coeficientul din GET /v2/nomenclatoare/cote-tva: 0.21 pentru 21%, 0 pentru scutit. E obligatorie, cu o excepție: la tip_factura = T (taxare inversă) o poți omite, FGO pune zero.
Articolul se leagă de nomenclatorul firmei printr-un singur câmp, cel ales de firmă în FGO, la Configurare API, setarea „Căutare articole după”:
| Setarea firmei | Câmpul după care se caută | Cum se potrivește |
|---|---|---|
| cod articol (implicit) | cod_articol |
exact, pe codul comercial |
| cod de bare | cod_bare |
exact, pe codul de bare |
| denumire | denumire |
exact, pe denumire |
Poți trimite pe linie toate câmpurile, cod_articol, cod_bare și denumire. FGO caută articolul doar după câmpul modului; dacă nu-l găsește, creează un articol nou cu denumirea și cu toate codurile trimise, așa că următoarea emitere îl găsește. Dacă îl găsește după cod, dar cu altă denumire, decide setarea „Generare articol nou” din aceeași pagină: articol nou cu codul trimis, sau primul articol cu acel cod.
O linie fără codul modului caută articolul după denumire și îl creează dacă lipsește, cu codurile trimise pe el. Așa merg taxele, transportul, discounturile, orice linie pe care integratorul nu o ține cu codul firmei. La emitere setarea e o prioritate, nu o restricție: nicio linie nu e respinsă din cauza ei. Aceeași setare decide și filtrele acceptate la GET /v2/articole și pe operațiile de stoc, unde regula e strictă: doar câmpul modului, altfel 400 validation. cod_gestiune descarcă stocul din gestiunea aceea.
Discounturi
Un discount e o linie ca oricare alta, cu tip = Discount. Se leagă de articolul pe care îl reduce prin nr_crt_articol, care indică nr_crt-ul acelei linii. Valoarea negativă o dai în unul din trei feluri, toate cu același rezultat, cantitate negativă și preț pozitiv:
| Forma | Câmpuri | Observații |
|---|---|---|
| total negativ | pret_total: -12.10, nr_produse: 1 |
totalul negativ trece cantitatea pe minus |
| cantitate negativă, total pozitiv | pret_total: 12.10, nr_produse: -1 |
forma trimisă de modulele FGO pentru WooCommerce |
| cantitate negativă, preț unitar pozitiv | pret_unitar: 10, nr_produse: -1 |
calcul clasic, fără TVA în preț |
pret_unitar negativ e respins: valoarea negativă se dă prin cantitate sau prin total. pret_total include TVA.
"articole": [
{
"tip": "Articol",
"nr_crt": 1,
"cod_articol": "SERV-CONSULT",
"denumire": "Servicii consultanta",
"um": "ora",
"nr_produse": 1,
"pret_unitar": 100,
"cota_tva": 0.21
},
{
"tip": "Discount",
"nr_crt": 2,
"nr_crt_articol": 1,
"denumire": "Discount 10% Servicii consultanta",
"um": "ora",
"nr_produse": 1,
"pret_total": -12.10,
"cota_tva": 0.21
}
]
Un discount fără nr_crt_articol rămâne pe factură, nelegat de o linie. efactura nu e permis pe discount. Discountul nu trimite de regulă niciun cod: FGO îl caută după denumire în nomenclator și, dacă nu-l găsește, îl creează ca articol al firmei, la fel ca orice linie fără cod. Dacă îi pui un cod, se aplică aceleași reguli ca la articole.
e-Factura
Când cumpărătorul cere referințe în XML-ul e-Factura — număr de comandă, contract, coduri CPV pe produs — le pui în efactura, la nivel de factură și pe fiecare articol. Sunt opționale; maparea completă către câmpurile BT e în referință, la operație.
Verificarea duplicatelor
Trimite "verificare_duplicat": true pentru a verifica dacă factura există deja. Opțiunea este implicit dezactivată.
- Dacă
id_externeste nenul, verificarea folosește acest identificator, firma cheii API și seria transmisă. - Dacă
id_externlipsește sau estenull, verificarea foloseșteserieșinumar, dacă ambele sunt completate. - Fără ID extern și fără serie și număr, nu există un criteriu de verificare.
Dacă factura este găsită, API-ul răspunde 400 invalid-state, cu motiv.cod=factura_duplicata, cu seria și numărul facturii existente în detail, și nu emite o factură nouă. Dacă verificarea întâmpină o eroare, emiterea se oprește.
Verificarea privește facturile deja salvate; nu garantează unicitatea între cereri simultane. Dacă pierzi răspunsul la emitere, verifică mai întâi rezultatul prin GET /v2/facturi. Nu retrimite automat cât timp prima cerere ar putea fi încă în curs.
Răspunsul
{
"data": {
"serie": "FGO",
"numar": "1201",
"stocuri": [
{ "cod_articol": "PROD-001", "cod_bare": "5941234567005", "nume": "Produs demonstrativ", "cod_gestiune": "DEPOZIT", "stoc": 41.000 }
]
},
"meta": { "timestamp": "2026-09-03T12:00:00+03:00" }
}
Seria și numărul: factura există și e emisă. stocuri are câte un element pentru fiecare linie care a descărcat stoc, cu stocul rămas după această factură și cu codul, codul de bare și numele articolului din nomenclator. O integrare de magazin îl scrie direct pe produsul vândut, fără o cerere separată. Lista e goală, nu absentă, când firma nu ține stoc pe articol, când tipul de document nu descarcă stoc sau când drepturile firmei nu permit expunerea stocului.
Informația despre stoc din răspunsul emiterii este disponibilă în Premium și Enterprise, în condițiile de mai sus. Citirea separată prin endpointurile /v2/stocuri/... necesită Enterprise. Vezi disponibilitatea pe abonamente.
Din ce gestiune e stocul: dacă linia a avut cod_gestiune și firma lucrează cu gestiuni pe linie, elementul îl repetă și stoc este stocul rămas în acea gestiune, cea din care s-a descărcat. Fără gestiune pe linie, cod_gestiune este null și stoc este stocul total al articolului, din toate gestiunile. Un magazin care vinde dintr-o singură gestiune trimite același cod_gestiune pe fiecare linie și citește restul stocului cu GET /v2/stocuri/stoc-gestiune pe aceeași gestiune, ca cele două cifre să însemne același lucru. Același articol pe mai multe linii scade cumulat: al doilea element pleacă de la stocul rămas după primul.
Restul — PDF, link de plată, încasări — se cere separat, pe /v2/facturi/{serie}/{numar}/...:
| Vrei | Apelezi |
|---|---|
| PDF-ul | POST /v2/facturi/{serie}/{numar}/genereaza-link-de-descarcare |
| link de plată | POST /v2/facturi/{serie}/{numar}/genereaza-link-de-plata |
| să o trimiți clientului | POST /v2/facturi/{serie}/{numar}/trimite-pe-email |
| să o trimiți la ANAF | POST /v2/facturi/{serie}/{numar}/trimite-in-spv, apoi GET .../efactura pentru status |
| să înregistrezi o încasare | POST /v2/facturi/{serie}/{numar}/incasari |
| starea ei | GET /v2/facturi/{serie}/{numar} |
| să o anulezi | POST /v2/facturi/{serie}/{numar}/anuleaza — rămâne în evidență, marcată anulată |
| să o stornezi | POST /v2/facturi/{serie}/{numar}/storneaza — emite factura de stornare |
| să retragi o stornare | POST /v2/facturi/{serie}/{numar}/repune-in-vigoare-factura-stornata — folosește seria și numărul originalului; acesta revine la starea emisă, iar documentul storno este șters logic. Necesită facturi:anulare și Fgo-Url-Platforma. |
| să o ștergi | DELETE /v2/facturi/{serie}/{numar} — numai dacă nu e încasată și nu e în SPV |
Linkul PDF e un short link FGO și rămâne același cât timp factura nu se schimbă, deci îl poți păstra.
Limita de apeluri
O emitere pe secundă, pe firmă. A doua în aceeași secundă primește 429 cu Retry-After. Dacă emiți în lot, serializează apelurile și respectă headerul — vezi Erori, limite și reluări.