Sari la conținut

Lista principalelor endpoint-uri API publice

9 min citire·Actualizat 8 oct. 2026

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

MetodaEndpointDescriereScope necesar
GET/invoicesLista facturi (paginată)read
GET/invoices/:idDetalii facturaread
POST/invoicesCreează factura nouăwrite
PATCH/invoices/:idActualizează facturawrite
GET/invoices/:id/pdfDescarcă PDF facturaread
POST/invoices/:id/send-emailTrimite factura pe email către clientwrite
GET/invoices/:id/efactura/statusStatus e-Factura ANAF pentru facturaread
POST/invoices/:id/shipmentGenerează 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; vezi GET /products și /products/lookup). Produsul trebuie să aparțină firmei și să fie activ, altfel cererea e respinsă (invalid_product_id pentru 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, default false) — 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-ul stock:read (verificările expun niveluri de stoc — 403 insufficient_scope altfel). 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ă cu descarca_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 cu below_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

MetodaEndpointDescriereScope necesar
GET/clientsLista clientiread
GET/clients/:idDetalii clientread
POST/clientsCreează client nouwrite
PATCH/clients/:idActualizează clientwrite
DELETE/clients/:idȘterge clientwrite
GET/clients/lookup/anaf/:cuiPreia datele firmei de la ANAF, după CUIread
GET/clients/lookup/vies/:vatVerificare 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

MetodaEndpointDescriereScope necesar
GET/productsLista produselor ACTIVE (paginată: ?page= / ?limit=, default 20, max 100; ?search= pe nume/cod/barcode/cod furnizor)read
GET/products/lookup?code=SKU123Găsire exactă după cod intern / barcode / cod furnizor (doar produse active; found: false fără match)read
GET/products/costsCostul ultimei achiziții pe produs (din recepții), în lei — vezi mai joscosts:read (separat)
GET/products/:idDetalii produs — întoarce și produsele dezactivate, cu is_active: false (utile la reconciliere); produsele șterse răspund 404 product_not_foundread

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) sau supplier.name identifică 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ște https://). 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

MetodaEndpointDescriereScope necesar
GET/stock?code=SKU123Stocul curent pentru un produs, după codread
GET/stock/allStocul curent pentru toate produseleread

Informații cont

MetodaEndpointDescriereScope necesar
GET/accountInformații firma și abonamentread

Calcul TVA

MetodaEndpointDescriereScope necesar
POST/tax/calculateCalcul automat cota TVA pe baza tipului de client, țară și produsread

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

CodSemnificație
200Succes
201Resursa creată cu succes
400Date invalide (verificare parametri)
401Cheie API invalida sau lipsa
403Permisiuni insuficiente (scope lipsa)
404Resursa nu exista
429Rate limit depășit
500Eroare 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