Manual API

Ultima actualizare: 11 august 2026

API-ul HTTP îți dă din propriul sistem exact ce vezi în panou: entitățile din spatele certificatului tău SPV, documentele primite de la ANAF și XML-ul lor original.

Este inclus în planurile Platinum și Contabil. Pe celelalte planuri tokenul se poate crea, dar orice cerere primește 403 — nu 401, ca să nu cauți o problemă de credențiale pe care nu o ai.

De ce ai nevoie

  • Un cont cu planul Platinum sau Contabil.
  • Un token API, creat din Setări → Integrări. Se afișează o singură dată, la creare.
  • Cel puțin o entitate cu monitorizarea pornită — API-ul răspunde numai despre CIF-urile pe care le monitorizezi.

Nu ai nevoie de nimic din partea ANAF. Certificatul, OAuth-ul și interogarea SPV rămân la noi; tu vorbești doar cu API-ul acesta.

Adresa de bază și versiunea

https://alertespv.ro/api/v1

Versiunea este în cale de la prima zi. Câmpuri noi pot apărea oricând într-un răspuns, așa că citește-le pe nume și ignoră-le pe cele necunoscute. Câmpurile existente nu își schimbă înțelesul și nu dispar fără o versiune nouă în cale.

Toate răspunsurile sunt JSON, cu o singură excepție: descărcarea XML-ului, care întoarce fișierul așa cum l-a emis ANAF.

Autentificare

Tokenul se trimite ca antet „Authorization: Bearer …". Nu există sesiuni, cookie-uri sau chei în query string.

curl -s https://alertespv.ro/api/v1/entities \
  -H "Authorization: Bearer spva_cheia_ta" \
  -H "Accept: application/json"
  • Tokenul începe întotdeauna cu „spva_", ca să fie recunoscut într-un log sau într-un paste greșit.
  • Maximum 5 tokenuri active per cont. Revocarea este imediată.
  • Nu îl păstrăm în clar — dacă l-ai pierdut, creezi altul și îl revoci pe cel vechi.
  • Coloana „ultima folosire" din Setări → Integrări îți spune dacă un token mai e folosit înainte să îl revoci.

Tokenul deschide toate documentele fiscale ale contului. Ține-l pe server, într-o variabilă de mediu — niciodată în cod versionat, într-o aplicație mobilă sau în JavaScript rulat în browser.

SituațieRăspunsCe înseamnă
Antet lipsă sau fără prefixul „spva_"401Missing or malformed API token.
Token necunoscut sau revocat401Unknown API token.
Token valid, plan fără API403The API is not part of your current plan.

Limite și paginare

120 de cereri pe minut per cont. Peste limită răspunsul este 429, cu antetul „Retry-After" care spune câte secunde să aștepți. Limita este mult peste ce cere o sincronizare zilnică și mult sub ce costă o buclă cu un bug.

  • Listele paginate acceptă „per_page" între 1 și 100; implicit 100.
  • Pagina se cere cu „page". Numărul total de pagini este în „meta.last_page".
  • Ordinea documentelor este descrescătoare după data creării la ANAF, apoi după id.

GET /entities — entitățile și sloturile

Întoarce tot ce vede certificatul tău — firme și persoane fizice — împreună cu starea sloturilor din plan. O entitate există în listă chiar dacă nu este monitorizată; monitorizarea este ce consumă un slot.

{
  "data": [
    {
      "id": 12,
      "cif": "44674942",
      "kind": "company",
      "name": "EXEMPLU SRL",
      "monitored": true,
      "enabled": true,
      "last_polled_at": "2026-08-11T07:00:12+00:00",
      "slot_releasable_from": "2026-09-09"
    }
  ],
  "slots": { "used": 1, "limit": 25, "free": 24 }
}
CâmpTipCe este
idnumărIdentificatorul entității la noi. El se folosește în „/entities/{id}/start".
ciftextCIF-ul sau CNP-ul, fără prefixul „RO".
kindtext„company" sau „person".
nametext sau nullDenumirea, așa cum o dă ANAF.
monitoredbooleanDacă entitatea ocupă acum un slot din plan.
enabledbooleanDacă interogarea este pornită. Oprită, entitatea încă poate ocupa slotul.
last_polled_atISO 8601 sau nullUltima interogare reușită la ANAF.
slot_releasable_fromdată sau nullPrima zi în care slotul se poate elibera, dacă monitorizarea rămâne oprită.

POST /entities/{id}/start și /stop

Pornirea consumă un slot care nu se întoarce imediat. Răspunsul spune explicit dacă s-a luat unul („slot_taken"), pentru că un script este exact apelantul care pornește treizeci de entități fără să fi citit nimic înainte.

curl -s -X POST https://alertespv.ro/api/v1/entities/12/start \
  -H "Authorization: Bearer spva_cheia_ta" \
  -H "Accept: application/json"
  • „slot_taken": false înseamnă că entitatea ocupa deja un slot — apelul a fost idempotent, nu ai plătit de două ori.
  • Oprirea răspunde întotdeauna cu „slot_released": false. Slotul se eliberează abia la o graniță de lună de abonament, la cel puțin 30 de zile de la pornire, și numai dacă monitorizarea era oprită atunci.
  • O entitate care aparține altui cont răspunde 403, nu 404.

GET /documents — documentele

Documentele CIF-urilor pe care le monitorizezi, cu sumarul extras din XML. Un „cif" care nu e printre ele este ignorat tăcut ca filtru — lista rămâne limitată la ce ai voie să vezi.

ParametruValoareEfect
ciftextRestrânge la un singur CIF monitorizat.
fromdatăDe la începutul acelei zile, după data creării la ANAF.
todatăPână la sfârșitul acelei zile, inclusiv.
per_page1–100Câte rânduri pe pagină. Implicit 100.
pagenumărPagina cerută. Implicit 1.
curl -s -G https://alertespv.ro/api/v1/documents \
  -H "Authorization: Bearer spva_cheia_ta" \
  --data-urlencode "cif=44674942" \
  --data-urlencode "from=2026-08-01" \
  --data-urlencode "to=2026-08-11" \
  --data-urlencode "per_page=50"
  • „type" și „details" vin de la ANAF exact cum le dă el: „FACTURA PRIMITA", „FACTURA TRIMISA", „ERORI FACTURA". Tratează-le ca text, nu ca enumerare închisă.
  • „summary" este null când documentul nu a fost arhivat încă sau când XML-ul nu este o factură pe care o putem citi (raport de erori fără conținut, format nerecunoscut).
  • „summary.kind" este „invoice", „credit_note", „errors" sau „unsupported".
  • „counterpartyRole" spune cine este partenerul: „supplier" la o factură primită, „customer" la una trimisă. Aceeași factură are roluri diferite pentru cele două firme, dacă ambele sunt monitorizate.
  • Sumele sunt text, nu numere: „990.08" rămâne exact atât. Nu le trece prin float dacă le pui într-o contabilitate.
  • „anaf_id" plus „cif" identifică unic documentul la ANAF; „id" este identificatorul lui la noi.

GET /documents/{id}/xml — fișierul original

Întoarce XML-ul așa cum l-a emis ANAF, ca fișier de descărcat, cu numele „{cif}-{anaf_id}.xml". Nu este JSON și nu este modificat în niciun fel — este exact copia pe care o arhivăm.

curl -s -OJ https://alertespv.ro/api/v1/documents/8814/xml \
  -H "Authorization: Bearer spva_cheia_ta"
RăspunsCândCe faci
403Documentul aparține unui CIF pe care nu îl monitorizezi.Verifică lista din /entities.
404Documentul nu a fost arhivat niciodată.Nimic de descărcat; „archived": false îți spunea asta dinainte.
410A fost arhivat, dar a ieșit din retenția planului.Rândul rămâne, fișierul nu. Descarcă mai devreme sau treci pe un plan cu retenție mai mare.

Coduri de răspuns

CodÎnseamnă
200Cererea a reușit.
401Token lipsă, malformat, necunoscut sau revocat.
403Planul nu include API-ul, sau resursa aparține altui cont.
404Resursa nu există.
409Toate sloturile din plan sunt ocupate în luna de abonament curentă.
410Fișierul a existat și a fost șters la expirarea retenției.
422Parametri invalizi. Corpul conține câmpul și motivul.
429Peste 120 de cereri pe minut. Așteaptă cât spune „Retry-After".

Rețetă: documentele de ieri, cu XML-ul lor

Tiparul obișnuit al unei sincronizări zilnice: ceri documentele dintr-un interval, ții minte ce ai luat deja după „id", și descarci XML-ul doar pentru cele arhivate.

# Ieri, toate CIF-urile monitorizate, prima pagina.
YESTERDAY=$(date -d yesterday +%F)

curl -s -G https://alertespv.ro/api/v1/documents \
  -H "Authorization: Bearer $ALERTESPV_TOKEN" \
  --data-urlencode "from=$YESTERDAY" \
  --data-urlencode "to=$YESTERDAY" \
  --data-urlencode "per_page=100" | jq '.data[] | {id, cif, type, archived}'

Dacă vrei să afli despre documente în momentul în care apar, nu la o oră fixă, folosește webhook-urile. API-ul este pentru sincronizări și pentru interogări la cerere; webhook-ul este pentru „spune-mi acum".

Citește șiManual Webhook