Finans Public API
Documentația publică pentru dezvoltatorii care construiesc integrări cu Finans. API-ul este gândit pentru magazine online, ERP, CRM, aplicații interne și automatizări: autentificare cu chei API sau token-uri OAuth, idempotență, mediu de test, webhook-uri și endpoint-uri pentru documente fiscale.
Testează integrarea în contul firmei tale
API-ul este inclus în abonamentele Finans. La prima înregistrare a firmei ai 30 de zile de testare, fără card. Administratorul generează cheia de test din Setări → Cont → Integrări.
Introducere
Prefixul de bază pentru toate endpoint-urile publice este /api/public/v1. API-ul ajunge pentru integrări generale care creează clienți, emit facturi, înregistrează plăți și descarcă PDF sau XML.
Resursele disponibile sunt:
- Verificare conexiune
- Firma emitentă
- Serii de documente
- Conturi bancare
- Clienți
- Produse
- Facturi și proforme
- Plăți
- Webhook-uri și evenimentele lor
Toate cererile de scriere cu efect fiscal (emitere, stornare, conversie, email, plată) cer header-ul Idempotency-Key. Pentru încercări fără documente reale, folosește o cheie de test (vezi secțiunea „Mediul de test”).
URL de bază
URL-ul de bază al API-ului:
https://app.finans.ro/api/public/v1Toate exemplele din această pagină folosesc acest prefix. Pentru medii interne sau de test, prefixul poate fi același API public expus sub alt domeniu, dar structura de endpoint-uri rămâne identică.
Autentificare
API-ul folosește chei generate din aplicație (Setări → Integrări) și trimise prin header-ul standard:
Authorization: Bearer fk_live_xxxxxxxxxPentru aplicațiile conectate poți folosi și token-uri OAuth:
Authorization: Bearer foat_xxxxxxxxxIdentificatorii publici folosesc prefixele:
fk_live_...pentru cheile de producțiefk_test_...pentru cheile de test (vezi „Mediul de test”)foat_...pentru token-urile OAuth
Fluxul OAuth disponibil în produs folosește endpoint-urile:
GET /api/public/v1/oauth/authorizePOST /api/public/v1/oauth/authorize/decisionPOST /api/public/v1/oauth/token
Pentru o verificare rapidă folosește GET /health/verify. Acesta întoarce date despre firmă, conexiunea de integrare, modul cheii (TEST sau LIVE) și scope-urile disponibile pe cheia curentă.
Scope-uri recomandate
Pentru o integrare completă recomandăm o cheie API dedicată, cu atât scope-uri de citire, cât și de scriere.
company.readdocument_series.readbank_accounts.readclients.readclients.writeproducts.readproducts.writeinvoices.readinvoices.writepayments.readpayments.writedocuments.readspv.readwebhooks.readwebhooks.writeDacă primești un răspuns cu missing_scopes, cheia este validă, dar nu are permisiunea cerută de endpoint-ul apelat.
Idempotență
Pentru a evita duplicatele la reîncercări HTTP, API-ul memorează răspunsul fiecărei cereri de mai jos. Header-ul Idempotency-Key este obligatoriu pe toate șase; fără el primești 400 missing_idempotency_key.
Celelalte rute de scriere (PATCH, DELETE, /cancel, clienți, produse, webhook-uri) nu folosesc Idempotency-Key.
Trimite o cheie stabilă pe acțiunea de business, nu pe fiecare încercare HTTP:
Idempotency-Key: external-system:order_100045:create_invoice- Cheia este valabilă 72 de ore și se aplică per firmă, metodă și adresă. Aceeași cheie pe alt endpoint, sau pe altă factură, este o cerere nouă.
- Aceeași cheie cu același conținut întoarce răspunsul memorat al primei cereri, fără să creeze nimic a doua oară.
- Aceeași cheie cu alt conținut dă
409 idempotency_conflict. - Dacă primești
409 idempotency_in_progress, nu genera o cheie nouă și nu schimba conținutul. Retrimite aceeași cerere, cu aceeași cheie, după o pauză scurtă, până primești răspunsul final.
Nu există un endpoint separat pentru starea unei chei; recuperarea se face prin retrimiterea aceleiași cereri.
Limite
Ferestrele sunt de 60 de secunde. Când o limită este depășită, API-ul răspunde 429 cu codul rate_limit_exceeded și nu trimite header-ul Retry-After:
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
{
"error": {
"code": "rate_limit_exceeded",
"message": "Ai depasit limita de request-uri pentru aceasta cheie API."
}
}Oprește cererile pentru restul ferestrei și reia-le cu pauze tot mai lungi. O cerere respinsă cu 429 nu a creat nimic, deci o poți retrimite fără risc, cu aceeași Idempotency-Key unde este cazul. La POST /oauth/token, răspunsul 429 are forma OAuth: { "error": "rate_limit_exceeded", "error_description": "..." }.
Dacă imporți volume mari, grupează scrierile și nu depăși 3 pe secundă pe cheie.
Mediul de test (sandbox)
O cheie fk_test_... îți permite să verifici integrarea fără documente fiscale reale. Cheile de test se creează din aplicație, la Setări → Integrări, și au același format de autentificare ca cele de producție.
Ce poate face o cheie de test:
- Poate citi doar
/health/verify,/company/profile,/document-seriesși/products(inclusiv un produs după id). - Poate apela
POST /invoicesșiPOST /payments, care doar validează conținutul și simulează rezultatul.
Ce nu poate face:
- Nu citește clienți, facturi, plăți, conturi bancare sau webhook-uri. Răspunsul este
403 sandbox_read_not_supported. - Nu face nicio altă scriere: clienți, produse,
PATCH,DELETE, emitere, anulare, stornare, conversie, email, webhook-uri. Răspunsul este403 sandbox_write_not_supported. - Simulările nu creează documente sau clienți, nu consumă numere din serii, nu ajung în SPV și nu declanșează webhook-uri.
Simularea unei facturi întoarce un obiect cu test_mode: true și persisted: false, cu id-ul de forma test_inv_...(la plăți, test_pay_...). Idempotency-Key rămâne obligatoriu și în modul de test.
{
"data": {
"id": "test_inv_5b1c0e52-0d1e-4c57-9b39-1f5a7a8a9c11",
"number": "TEST-1790000000000",
"type": "INVOICE",
"status": "DRAFT",
"issue_date": "2026-04-19",
"currency": "RON",
"subtotal": 100,
"vat_amount": 21,
"total": 121,
"amount_paid": 0,
"pdf_url": null,
"xml_url": null,
"test_mode": true,
"persisted": false
}
}Fiecare simulare rămâne în registrul sandbox, vizibil administratorului firmei în aplicație, la Setări → Integrări, secțiunea „Registru sandbox”. Registrul este citit de aplicație prin GET /api/account/integrations/sandbox-runs?limit=100 (implicit 100, maximum 250); acel endpoint cere sesiunea de administrator din aplicație și nu se apelează cu o cheie API.
Trecerea la producție înseamnă schimbarea cheii în fk_live_..., asociată unei conexiuni de integrare active. Nimic altceva nu se schimbă în cereri.
Verificare conexiune
Folosește acest endpoint ca prim pas la integrare. Dacă răspunde corect, cheia este validă și vezi exact ce scope-uri ai la dispoziție.
Firma
Returnează datele firmei emitente folosite în documente: identificare fiscală, adresă, contact, TVA, moneda implicită și setările generale ale firmei.
În câmpul vat_rates găsești cotele TVA active ale firmei, cu cota implicită prima. Din 1 august 2025 cotele sunt 21% (standard) și 11% (redusă); folosește în linii cota din acest câmp, nu o valoare scrisă în cod.
Serii de documente
Poți descoperi seriile active pentru documente și le poți filtra cu ?type=INVOICE, ?type=PROFORMA sau ?type=RECEIPT.
La emitere poți trimite series sau series_id. Dacă există o singură serie activă sau una marcată implicit, API-ul o poate selecta automat.
Conturi bancare
Returnează conturile bancare active ale firmei. Poți filtra cu ?currency=EUR sau cu altă monedă suportată, pentru a alege contul potrivit la documentele în valută.
Clienți
Resursa este utilă pentru sincronizarea clienților din magazin online, ERP sau CRM.
Răspunsul listei are forma data plus meta cu page, page_size și total.
POST /api/public/v1/clients
Authorization: Bearer fk_live_xxxxxxxxx
Content-Type: application/json{
"type": "COMPANY",
"external_id": "customer_123",
"name": "Client Test SRL",
"cui": "RO12345678",
"reg_com": "J40/123/2024",
"address": "Str. Exemplu 1",
"city": "București",
"county": "București",
"country": "RO",
"postal_code": "010101",
"email": "client@example.com",
"phone": "+40722111222",
"iban": "RO49AAAA1B31007593840000",
"bank_name": "Banca Demo",
"vat_payer": true,
"vat_number": "RO12345678"
}GET /api/public/v1/clients?search=Client%20Test&page=1&page_size=20
Authorization: Bearer fk_live_xxxxxxxxxProduse
Se poate folosi pentru maparea SKU, sincronizarea catalogului și crearea de servicii standardizate.
POST /api/public/v1/products
Authorization: Bearer fk_live_xxxxxxxxx
Content-Type: application/json{
"name": "Produs demo",
"unit": "buc",
"currency": "RON",
"unit_price": 100,
"vat_rate": 21,
"price_includes_vat": false,
"product_code": "SKU-ABC"
}GET /api/public/v1/products?q=Produs%20demo¤cy=RON&limit=20
Authorization: Bearer fk_live_xxxxxxxxxFacturi
O factură poate fi emisă direct dintr-un sistem extern, cu un conținut orientat pe integrări: client, source și lines.
GET /api/public/v1/invoices?status=issued&client_id=cli_123&page=1&page_size=25
Authorization: Bearer fk_live_xxxxxxxxx{
"data": [ ... ],
"meta": { "page": 1, "page_size": 25, "total": 137 }
}Dacă trimiți series_id, API-ul folosește exact seria indicată. Dacă omiți seria, API-ul încearcă să aleagă seria activă implicită pentru tipul documentului.
În fiecare linie, external_product_id este opțional și este tratat ca alias pentru product_code. Valoarea se mapează la codul de produs folosit în liniile facturii și în catalogul intern, nu la un external_id separat.
Produsul nu trebuie să existe deja în /products. Dacă există un produs activ cu același cod, este reutilizat; dacă nu există, factura poate fi emisă oricum, iar catalogul este sincronizat automat din liniile documentului.
Ce face fiecare acțiune:
DELETE /invoices/{invoiceId}șterge doar ciorne. O factură emisă se anulează sau se stornează./issueemite o ciornă și îi alocă numărul fiscal./cancelanulează o factură care nu a fost validată în SPV./stornocreează factura de stornare; poți trimite opțional unreason./convert-to-invoicetransformă o proformă în factură fiscală./emailtrimite documentul cu PDF atașat. Corpul cereto_emailși acceptăccșibcc, maximum 10 adrese fiecare.
Emiterea, stornarea, conversia și emailul cer Idempotency-Key; vezi secțiunea „Idempotență”.
Plăți
După emiterea facturii poți înregistra plata și, pentru plățile în numerar, poți descărca PDF-ul chitanței.
Pentru plățile în numerar poți trimite receipt_series sau receipt_series_id. Dacă nu sunt trimise și există o singură serie activă sau una implicită, API-ul o poate selecta automat.
GET /api/public/v1/payments?from=2026-04-01&to=2026-04-30&search=100045&page=1&page_size=50
Authorization: Bearer fk_live_xxxxxxxxxPOST /payments cere Idempotency-Key.
Webhook-uri
Endpoint-urile acoperă configurarea, modificarea și testarea webhook-urilor. La creare alegi lista de evenimente (vezi „Evenimente webhook”). Evenimentele se pun în coadă, iar livrarea și reîncercările le face workerul dedicat; doar evenimentul de test de la /test este trimis imediat, ca să vezi rezultatul în răspuns.
O livrare reușită înseamnă un răspuns 2xx în cel mult 10 secunde. Dacă endpoint-ul tău răspunde altfel sau nu răspunde, livrarea se reia, până la 5 încercări în total, la intervale tot mai mari (aproximativ 1 minut, 5 minute, 30 de minute, 2 ore). Un eveniment poate ajunge de mai multe ori: deduplică după id-ul din corp și nu te baza pe ordinea livrării.
URL-urile de webhook trebuie să fie publice, pe https://, fără host-uri locale, private sau rezervate.
Dacă serverul este configurat cu PUBLIC_API_WEBHOOK_ALLOWED_HOSTS, host-ul endpoint-ului trebuie să intre și în lista explicit permisă.
La creare primești o singură dată plain_secret. Păstrează-l în siguranță și verifică fiecare livrare folosind x-finans-signature: semnătura este HMAC-SHA256, în format hex, calculată peste șirul <x-finans-timestamp>.<corpul_brut>. Compară semnăturile în timp constant și respinge timestamp-urile mai vechi de 5 minute, ca protecție împotriva reluării.
Header-ele trimise sunt x-finans-signature, x-finans-timestamp, x-finans-event, x-finans-event-id și x-finans-delivery-id. Verificarea se face pe corpul brut, înainte de parsarea JSON.
Evenimente webhook
Sunt 14 evenimente. Fiecare livrare are același plic, iar conținutul lui data depinde de eveniment:
{
"id": "0d0c4a5e-7f4e-4b39-9d2f-2a6c3f1c9a10",
"type": "invoice.issued",
"created_at": "2026-04-19T09:30:12.418Z",
"data": {
"invoice": {
"id": "inv_123",
"number": "FCT0042",
"type": "INVOICE",
"status": "ISSUED",
"issue_date": "2026-04-19T00:00:00.000Z",
"due_date": "2026-05-04T00:00:00.000Z",
"currency": "RON",
"subtotal": 100,
"vat_amount": 21,
"total": 121,
"amount_paid": 0,
"payment_method": "BANK_TRANSFER",
"client": {
"id": "cli_123",
"type": "COMPANY",
"name": "Client Test SRL",
"cui": "RO12345678"
},
"created_at": "2026-04-19T09:30:11.902Z",
"updated_at": "2026-04-19T09:30:12.310Z"
},
"source": null
}
}id: identificatorul evenimentului, același la toate reîncercările.type: numele evenimentului, repetat în header-ulx-finans-event.created_at: momentul emiterii, în format ISO 8601.data.invoice:id,number,type,status,issue_date,due_date,currency,subtotal,vat_amount,total,amount_paid,payment_method,client(id,type,name,cui),created_at,updated_at.data.source: sursa documentului trimisă la creare (sistem, conexiune, comandă), saunull.data.payment:id,invoice_id,amount,currency,amount_ron,payment_date,payment_method,reference,created_at.
Se creează o factură direct emisă (cu număr fiscal). Nu se emite pentru ciorne, proforme sau storno.
Conținut data: invoice, source
Se creează o ciornă de factură.
Conținut data: invoice, source
Se șterge o ciornă, inclusiv prin DELETE /invoices/{invoiceId}.
Conținut data: invoice
Se șterge o factură emisă din aplicație, în condițiile în care ștergerea este permisă (de exemplu fără trimitere în SPV). API-ul public șterge doar ciorne.
Conținut data: invoice
O ciornă primește număr fiscal, din aplicație sau prin POST /invoices/{invoiceId}/issue.
Conținut data: invoice, source
Se modifică o factură sau o ciornă, din aplicație sau prin PATCH /invoices/{invoiceId}.
Conținut data: invoice
Factura este anulată prin POST /invoices/{invoiceId}/cancel.
Conținut data: invoice
Se creează o factură de stornare pentru o factură emisă.
Conținut data: invoice, source
O încasare acoperă integral factura. Se emite împreună cu payment.created, după el.
Conținut data: payment, invoice_id, invoice_status, amount_paid
ANAF a validat factura trimisă în SPV. Se emite o singură dată, la prima stare observată.
Conținut data: invoice_id, invoice_number, upload_id, download_id, status, spv_status, anaf_state
ANAF a respins factura trimisă în SPV. Aceleași câmpuri ca la invoice.spv.accepted.
Conținut data: invoice_id, invoice_number, upload_id, download_id, status, spv_status, anaf_state
Se creează o proformă.
Conținut data: invoice, source
O proformă este convertită în factură fiscală (POST /invoices/{invoiceId}/convert-to-invoice sau conversie din aplicație).
Conținut data: proforma_id, invoice
Se înregistrează o încasare, prin API, din aplicație sau din reconcilierea bancară.
Conținut data: payment, invoice_id, invoice_status, amount_paid
Avizele de însoțire nu generează evenimente, iar facturile create de integrarea eMAG nu emit invoice.created. Endpoint-ul /test trimite către webhook-ul ales un eveniment separat, webhook.test, cu un mesaj și id-ul webhook-ului, indiferent de evenimentele la care este abonat.
Exemplu: creare factură
POST /api/public/v1/invoices
Authorization: Bearer fk_live_xxxxxxxxx
Idempotency-Key: external-system:order_100045:create_invoice
Content-Type: application/json{
"series": "FCT",
"type": "INVOICE",
"issue_date": "2026-04-19",
"currency": "RON",
"client": {
"external_id": "customer_123",
"email": "client@example.com",
"name": "Client Test SRL",
"cui": "RO12345678",
"address": "Str. Exemplu 1",
"city": "București",
"country": "RO"
},
"source": {
"system": "external_app",
"connection_id": "ic_123",
"external_order_id": "100045",
"external_order_number": "#100045"
},
"lines": [
{
"external_product_id": "sku_abc",
"description": "Produs demo",
"quantity": 1,
"unit_price": 100,
"vat_rate": 21
}
]
}Cota de 21% este cea standard din 1 august 2025. Citește cotele active ale firmei din GET /company/profile și folosește-le în vat_rate.
Exemplu: creare plată
{
"invoice_id": "inv_123",
"amount": 121,
"currency": "RON",
"payment_date": "2026-04-19",
"payment_method": "CARD",
"reference": "order-100045",
"source": {
"connection_id": "ic_123",
"external_order_id": "100045",
"external_payment_id": "pay_100045"
}
}Pentru plățile în numerar poți trimite și receipt_series, apoi folosești endpoint-ul PDF pentru chitanță.