Finans
DocumentațieAPI public

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/v1

Toate 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_xxxxxxxxx

Pentru aplicațiile conectate poți folosi și token-uri OAuth:

Authorization: Bearer foat_xxxxxxxxx

Identificatorii publici folosesc prefixele:

  • fk_live_... pentru cheile de producție
  • fk_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/authorize
  • POST /api/public/v1/oauth/authorize/decision
  • POST /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ă.

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.

POST /invoices
201
Creează factura sau proforma.
POST /invoices/{invoiceId}/issue
200
Emite o ciornă și îi alocă numărul fiscal.
POST /invoices/{invoiceId}/storno
201
Creează factura de stornare.
POST /invoices/{invoiceId}/convert-to-invoice
201
Convertește o proformă în factură.
POST /invoices/{invoiceId}/email
200
Trimite documentul pe email.
POST /payments
201
Înregistrează o plată.

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

Citiri (GET)
600 pe minut, per cheie API
Scrieri (POST, PATCH, DELETE)
180 pe minut, per cheie API
Per adresă IP
1200 pe minut, numărate separat pentru citiri și pentru scrieri, verificate înainte de autentificare
POST /oauth/token
60 pe minut, per combinația adresă IP și client_id
Dimensiunea corpului JSON
maximum 256 KB; peste limită primești 413 payload_too_large
Linii pe factură
maximum 200

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 și POST /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 este 403 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

GET
/api/public/v1/health/verify
fără scope suplimentar

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

GET
/api/public/v1/company/profile
company.read

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

GET
/api/public/v1/document-series
document_series.read

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

GET
/api/public/v1/bank-accounts
bank_accounts.read

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

GET
/api/public/v1/clients
clients.read
POST
/api/public/v1/clients
clients.write
GET
/api/public/v1/clients/{clientId}
clients.read
PATCH
/api/public/v1/clients/{clientId}
clients.write

Resursa este utilă pentru sincronizarea clienților din magazin online, ERP sau CRM.

Parametri pentru lista de clienți
page
Numărul paginii, începând de la 1. Implicit 1.
page_size
Rezultate pe pagină. Implicit 50, maximum 100 (valorile mai mari sunt reduse la 100).
search
Caută în nume, CUI și email.

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_xxxxxxxxx

Produse

GET
/api/public/v1/products
products.read
POST
/api/public/v1/products
products.write
GET
/api/public/v1/products/{productId}
products.read
PATCH
/api/public/v1/products/{productId}
products.write

Se poate folosi pentru maparea SKU, sincronizarea catalogului și crearea de servicii standardizate.

Parametri pentru lista de produse
q
Caută în numele produsului.
currency
Doar produsele în moneda indicată (RON, EUR etc.). O monedă necunoscută este ignorată.
limit
Numărul maxim de produse returnate. Implicit 50, maximum 200. Lista produselor nu este paginată.
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&currency=RON&limit=20
Authorization: Bearer fk_live_xxxxxxxxx

Facturi

GET
/api/public/v1/invoices
invoices.read
POST
/api/public/v1/invoices
invoices.write
GET
/api/public/v1/invoices/{invoiceId}
invoices.read
PATCH
/api/public/v1/invoices/{invoiceId}
invoices.write
DELETE
/api/public/v1/invoices/{invoiceId}
invoices.write
POST
/api/public/v1/invoices/{invoiceId}/issue
invoices.write
POST
/api/public/v1/invoices/{invoiceId}/cancel
invoices.write
POST
/api/public/v1/invoices/{invoiceId}/storno
invoices.write
POST
/api/public/v1/invoices/{invoiceId}/convert-to-invoice
invoices.write
POST
/api/public/v1/invoices/{invoiceId}/email
invoices.write
GET
/api/public/v1/invoices/{invoiceId}/pdf
documents.read
GET
/api/public/v1/invoices/{invoiceId}/xml
documents.read
GET
/api/public/v1/invoices/{invoiceId}/spv-status
spv.read

O factură poate fi emisă direct dintr-un sistem extern, cu un conținut orientat pe integrări: client, source și lines.

Parametri pentru lista de facturi
page
Numărul paginii, începând de la 1. Implicit 1.
page_size
Rezultate pe pagină. Implicit 50, maximum 100.
client_id
Doar facturile clientului cu acest id.
status
DRAFT, ISSUED, PAID, OVERDUE, CANCELLED sau PARTIALLY_PAID, fără deosebire între litere mari și mici. O valoare necunoscută este ignorată, nu produce eroare.
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ă.
  • /issue emite o ciornă și îi alocă numărul fiscal.
  • /cancel anulează o factură care nu a fost validată în SPV.
  • /storno creează factura de stornare; poți trimite opțional un reason.
  • /convert-to-invoice transformă o proformă în factură fiscală.
  • /email trimite documentul cu PDF atașat. Corpul cere to_email și acceptă cc și bcc, maximum 10 adrese fiecare.

Emiterea, stornarea, conversia și emailul cer Idempotency-Key; vezi secțiunea „Idempotență”.

Plăți

GET
/api/public/v1/payments
payments.read
POST
/api/public/v1/payments
payments.write
GET
/api/public/v1/payments/{paymentId}
payments.read
PATCH
/api/public/v1/payments/{paymentId}
payments.write
GET
/api/public/v1/payments/{paymentId}/pdf
documents.read

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.

Parametri pentru lista de plăți
page
Numărul paginii, începând de la 1. Implicit 1.
page_size
Rezultate pe pagină. Implicit 50, maximum 100.
search
Caută în numărul facturii, numele clientului și referința plății.
from
Data de început, în format AAAA-LL-ZZ. Se include ziua indicată.
to
Data de sfârșit, în format AAAA-LL-ZZ. Se include toată ziua indicată.
GET /api/public/v1/payments?from=2026-04-01&to=2026-04-30&search=100045&page=1&page_size=50
Authorization: Bearer fk_live_xxxxxxxxx

POST /payments cere Idempotency-Key.

Webhook-uri

GET
/api/public/v1/webhooks
webhooks.read
POST
/api/public/v1/webhooks
webhooks.write
PATCH
/api/public/v1/webhooks/{webhookId}
webhooks.write
POST
/api/public/v1/webhooks/{webhookId}/test
webhooks.write

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-ul x-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ă), sau null.
  • data.payment: id, invoice_id, amount, currency, amount_ron, payment_date, payment_method, reference, created_at.
invoice.created

Se creează o factură direct emisă (cu număr fiscal). Nu se emite pentru ciorne, proforme sau storno.

Conținut data: invoice, source

invoice.draft.created

Se creează o ciornă de factură.

Conținut data: invoice, source

invoice.draft.deleted

Se șterge o ciornă, inclusiv prin DELETE /invoices/{invoiceId}.

Conținut data: invoice

invoice.deleted

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

invoice.issued

O ciornă primește număr fiscal, din aplicație sau prin POST /invoices/{invoiceId}/issue.

Conținut data: invoice, source

invoice.updated

Se modifică o factură sau o ciornă, din aplicație sau prin PATCH /invoices/{invoiceId}.

Conținut data: invoice

invoice.cancelled

Factura este anulată prin POST /invoices/{invoiceId}/cancel.

Conținut data: invoice

invoice.storno.created

Se creează o factură de stornare pentru o factură emisă.

Conținut data: invoice, source

invoice.paid

O încasare acoperă integral factura. Se emite împreună cu payment.created, după el.

Conținut data: payment, invoice_id, invoice_status, amount_paid

invoice.spv.accepted

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

invoice.spv.rejected

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

proforma.created

Se creează o proformă.

Conținut data: invoice, source

proforma.converted

O proformă este convertită în factură fiscală (POST /invoices/{invoiceId}/convert-to-invoice sau conversie din aplicație).

Conținut data: proforma_id, invoice

payment.created

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ță.