Lista principalelor endpoint-uri API publice
API-ul public SoftFactura urmeaza convențiile REST și returnează răspunsuri JSON. Toate endpoint-urile necesită autentificare cu cheia API, transmisă fie în header-ul X-Api-Key, fie ca Authorization: Bearer sf_live_....
URL de baza: https://api.softfactura.ro/public/v1
Facturi
| Metoda | Endpoint | Descriere | Scope necesar |
|---|---|---|---|
| GET | /invoices | Lista facturi (paginată) | read |
| GET | /invoices/:id | Detalii factura | read |
| POST | /invoices | Creează factura nouă | write |
| PATCH | /invoices/:id | Actualizează factura | write |
| GET | /invoices/:id/pdf | Descarcă PDF factura | read |
| POST | /invoices/:id/send-email | Trimite factura pe email către client | write |
| GET | /invoices/:id/efactura/status | Status e-Factura ANAF pentru factura | read |
| POST | /invoices/:id/shipment | Generează AWB pentru factura (necesită modulul Curieri) | write |
Endpoint-urile de facturi lucrează doar cu documentele emise de firma ta: facturi, proforme, oferte, avize și facturi cu minus. Facturile primite de la furnizori (din SPV sau introduse la cheltuieli) nu apar în listă, iar pe rutele /invoices/:id… răspund 404, ca un document inexistent.
Modificarea unui document (PATCH /invoices/:id)
- Documentele transmise deja în e-Factura (transmiterea la ANAF e „trimisă" sau „validată", inclusiv facturile importate din SPV) nu mai pot fi modificate deloc — nici liniile, nici clientul, datele sau notele: răspuns
409 invoice_locked_efactura. Corecția se face prin storno + factură nouă. O transmitere respinsă de ANAF nu blochează modificarea. - Documentele cu plăți înregistrate nu pot fi modificate (400).
- Proformele și ofertele nu ajung în e-Factura, deci pot fi modificate cât timp nu au plăți înregistrate.
Legarea liniilor de produse + descărcarea stocului
Câmpuri opționale la POST /invoices (DOAR la creare — la PATCH sunt respinse cu field_not_supported_on_update, pentru că update-ul înlocuiește liniile fără legare de produse):
lines[].product_id— leagă linia de un produs EXISTENT din catalog (UUID — string; veziGET /productsși/products/lookup). Produsul trebuie să aparțină firmei și să fie activ, altfel cererea e respinsă (invalid_product_idpentru UUID malformat sau tip greșit,product_not_found,product_inactive). Descrierea, prețul și TVA rămân cele trimise pe linie.lines[].code— cod produs (SKU): dacă firma are activată salvarea automată a produselor noi, linia e potrivită după cod cu catalogul (sau produsul e creat automat).descarca_stoc(pe document, defaultfalse) — dacătrue, factura emisă descarcă stocul produselor gestionate, exact ca emiterea din aplicație, inclusiv verificările pre-emitere: stoc insuficient sau vânzare sub cost răspund 400 (below_cost). Cere modulul Stoc activ (stock_module_required) și, pe cheia API, scope-ulstock:read(verificările expun niveluri de stoc — 403insufficient_scopealtfel). Descărcarea propriu-zisă rulează asincron, imediat după emitere — confirmarea se vede în mișcările de stoc, nu în răspunsul HTTP. Ignorat pentru proforme.allow_below_cost(pe document, doar împreună cudescarca_stoc: true) — confirmă explicit vânzarea sub costul de achiziție (echivalentul confirmării din aplicație); fără el, liniile cu preț ≤ cost sunt respinse cubelow_cost.
Creare factura — exemplu
POST /public/v1/invoices
{
"client_id": "cl_abc123",
"type": "factura",
"currency": "RON",
"issue_date": "2026-07-02",
"due_date": "2026-08-01",
"auto_submit_efactura": true,
"lines": [
{
"description": "Servicii consultanta IT",
"quantity": 10,
"unit_price": 500,
"vat_rate": 21
}
]
}
Parametri de filtrare
?status=issued— filtrează după status (draft, issued, paid, cancelled).?from=2026-01-01&to=2026-03-31— interval de date.?page=2&limit=50— paginare (default: page 1, limit 20).
Clienti
| Metoda | Endpoint | Descriere | Scope necesar |
|---|---|---|---|
| GET | /clients | Lista clienti | read |
| GET | /clients/:id | Detalii client | read |
| POST | /clients | Creează client nou | write |
| PATCH | /clients/:id | Actualizează client | write |
| DELETE | /clients/:id | Șterge client | write |
| GET | /clients/lookup/anaf/:cui | Preia datele firmei de la ANAF, după CUI | read |
| GET | /clients/lookup/vies/:vat | Verificare TVA intracomunitar (VIES) | read |
Parametri de filtrare
?search=firma— caută după nume, CUI sau email.?type=pj— filtrează după tip (pj = persoana juridica, pf = persoana fizica).
Produse
| Metoda | Endpoint | Descriere | Scope necesar |
|---|---|---|---|
| GET | /products | Lista produselor ACTIVE (paginată: ?page= / ?limit=, default 20, max 100; ?search= pe nume/cod/barcode/cod furnizor) | read |
| GET | /products/lookup?code=SKU123 | Găsire exactă după cod intern / barcode / cod furnizor (doar produse active; found: false fără match) | read |
| GET | /products/costs | Costul ultimei achiziții pe produs (din recepții), în lei — vezi mai jos | costs:read (separat) |
| GET | /products/:id | Detalii produs — întoarce și produsele dezactivate, cu is_active: false (utile la reconciliere); produsele șterse răspund 404 product_not_found | read |
Crearea și actualizarea produselor prin API nu sunt încă disponibile — catalogul se administrează din aplicație. Liniile de factură se pot lega de produse existente cu lines[].product_id, iar liniile fără produs se trimit cu descriere liberă. Stocul curent NU e inclus în răspunsurile de produse — folosește endpoint-urile de stoc de mai jos (scope separat stock:read). Excepții: codurile de furnizor și adresele paginilor de produs ale produselor existente (PUT /products/supplier-codes, PUT /products/urls, mai jos).
PUT /public/v1/products/supplier-codes
Codurile sub care un furnizor cunoaște produsele tale (de exemplu codul SPIN „DL66870” pentru produsul tău „ML30W220VIP67CW”). Odată trimise, la recepție (NIR) poți tasta codul de pe factura furnizorului și SoftFactura găsește produsul tău, iar facturile primite prin SPV se potrivesc automat. Idempotent: ce există deja se sare. Scope: write (sau products:write). Răspunsul e împachetat ca la restul API-ului (data, vezi „Format răspuns").
{
"supplier": { "cui": "5969560", "name": "SPIN COM-EXIM SRL" },
"items": [
{ "code": "ML30W220VIP67CW", "supplier_codes": ["DL66870"] },
{ "code": "PL20W30X30WXX", "supplier_codes": ["DL74532", "SPN7441"] }
]
}
supplier.cui(recomandat) sausupplier.nameidentifică furnizorul din lista ta de clienți/furnizori; dacă nu e găsit, codurile se memorează fără furnizor (rămân găsibile la căutare).items[].code= codul produsului din SoftFactura;supplier_codes= codurile sub care îl are furnizorul (max 50). Max 2.000 de produse per apel.- Răspuns:
supplier_id,supplier_matched_by(cui|name|null),products_matched,products_unknown(coduri de produs negăsite, max 50),inserted,skipped.
PUT /public/v1/products/urls
Adresele paginilor de produs din magazinul tău online. Odată trimise, denumirea produsului devine link către pagina lui pe toate documentele PDF (factură, proformă, ofertă, aviz) — la fel ca pentru facturile create din comenzile magazinului cu lines[].url. Magazinul e sursa de adevăr: setează unde lipsește, corectează unde diferă, nu atinge ce e identic — poți rula apelul zilnic, e idempotent. Scope: write (sau products:write). Răspunsul e împachetat ca la restul API-ului (data).
{
"items": [
{ "code": "PL76005", "url": "https://magazin.ro/panouri-led-aplicate/mini-panou-led-rotund-12w" },
{ "code": "PL8056", "url": "https://magazin.ro/plafoniere-si-aplice-led/aplica-led-perete-exterior-8w" }
]
}
items[].code= codul produsului din SoftFactura (potrivire fără majuscule);url= adresa paginii (http/https, max 2048; un domeniu fără schemă primeștehttps://). Max 2.000 de produse per apel. O adresă completată manual în SoftFactura este înlocuită cu cea trimisă (magazinul e sursa de adevăr).- Răspuns:
products_matched,updated,unchanged,invalid_urls(adrese care nu sunt pagini web — nu se scriu),products_unknown(coduri negăsite, max 50),errors,blocked. - Supapă de siguranță: dacă, dintre produsele din apel care au deja adresă (minimum 20), peste 30 % ar primi altă adresă, schimbările nu se scriu și răspunsul are
blocked: { reason: "mass-change", existing_with_url, would_change }— protecție împotriva unei erori din magazin care ar strica linkurile de pe toate documentele. Adresele lipsă se completează oricum. După ce verifici, retrimite cu"force": true.
GET /public/v1/products/costs
Costul ultimei achiziții pentru fiecare produs: costul unitar cu care produsul a intrat în stoc la cea mai recentă recepție (NIR), în lei — prețul fără TVA, plus partea lui din cheltuielile trecute pe recepție (de exemplu transportul). La recepțiile în valută, costul e cel convertit în lei la confirmare. Recepțiile stornate sau returnate integral furnizorului nu se iau în calcul. Dacă același produs apare pe mai multe linii ale aceleiași recepții, se ia costul cel mai mare. Util pentru un magazin online care își calculează adaosul.
Scope costs:read, acordat separat: costurile de achiziție sunt date comerciale sensibile, așa că permisiunea nu e inclusă în read sau write. O bifezi explicit la crearea cheii („Costuri de achiziție”). Fără ea, răspunsul e 403 insufficient_scope.
?code=— opțional: unul sau mai multe coduri de produs, separate prin virgulă (sau parametrul repetat), max 500, potrivire fără majuscule.?since=YYYY-MM-DD— opțional: doar produsele cu o recepție din această zi sau mai nouă.?page=/?limit=— implicit 100, max 1.000 pe pagină.- Apar doar produsele care au avut cel puțin o recepție. Produsele dezactivate apar cu
is_active: false; cele șterse nu apar. - Costul e cel din gestiune (lotul de stoc al recepției). Dacă ai modificat recepția după ce o parte din marfă s-a vândut, costul poate rămâne cel dinainte de modificare.
{
"data": [
{
"product_id": "8f0c…",
"code": "BEC-E27-9W",
"name": "Bec LED E27 9W",
"unit": "buc",
"is_active": true,
"unit_cost": 4.85,
"currency": "RON",
"date": "2026-09-16",
"document": { "type": "nir", "series": "NIR", "number": 128 },
"supplier": { "name": "Furnizor SRL", "cui": "RO12345678" }
}
],
"meta": { "total": 1, "page": 1, "limit": 100, "totalPages": 1 }
}
Stoc
| Metoda | Endpoint | Descriere | Scope necesar |
|---|---|---|---|
| GET | /stock?code=SKU123 | Stocul curent pentru un produs, după cod | read |
| GET | /stock/all | Stocul curent pentru toate produsele | read |
Informații cont
| Metoda | Endpoint | Descriere | Scope necesar |
|---|---|---|---|
| GET | /account | Informații firma și abonament | read |
Calcul TVA
| Metoda | Endpoint | Descriere | Scope necesar |
|---|---|---|---|
| POST | /tax/calculate | Calcul automat cota TVA pe baza tipului de client, țară și produs | read |
Format răspuns
Toate răspunsurile urmeaza un format consistent:
{
"data": { ... },
"meta": {
"total": 142,
"page": 1,
"limit": 20,
"totalPages": 8
}
}
Erorile returnează:
{
"error": {
"type": "validation_error",
"code": "missing_client_id",
"message": "client_id is required",
"field": "client_id"
},
"meta": {
"request_id": "req_24e4cae10b0846ffb261b40d",
"api_version": "v1"
}
}
Coduri HTTP
| Cod | Semnificație |
|---|---|
| 200 | Succes |
| 201 | Resursa creată cu succes |
| 400 | Date invalide (verificare parametri) |
| 401 | Cheie API invalida sau lipsa |
| 403 | Permisiuni insuficiente (scope lipsa) |
| 404 | Resursa nu exista |
| 429 | Rate limit depășit |
| 500 | Eroare server |
Testează integrarea fără riscuri în mediul de test (sandbox), iar pentru întrebări scrie-ne la contact@softfactura.ro.
Ți-a fost util acest articol?
Ai în continuare nevoie de ajutor?
Contactează-ne pe email cu detalii — răspundem în maxim 24h în zilele lucrătoare.
Trimite email cu context pre-completat