API 4invoices για προγραμματιστές

REST πάνω από HTTPS, JSON, δημόσια προδιαγραφή OpenAPI. Τιμολόγηση, πελάτες, σειρές και η πλήρης διαδρομή της εθνικής ηλεκτρονικής τιμολόγησης (ANAF e-Factura στη Ρουμανία) — από το ERP, το κατάστημα ή το CRM σας.

Το δημόσιο API της 4invoices στέλνει τα δεδομένα τιμολόγησης από το σύστημά σας στη 4invoices και οδηγεί το τιμολόγιο ως το τέλος στο εθνικό σύστημα ηλεκτρονικής τιμολόγησης (στη Ρουμανία: SPV της ANAF). Ένας διακομιστής παραγωγής, μία ροή client_credentials, Ιδεμποτέντ φορολογικές εγγραφές και ένας ενιαίος φάκελος σφάλματος σε κάθε διαδρομή.

https://api.4invoices.net/v1 · Προδιαγραφή OpenAPI (χωρίς κλειδί)

Τι είναι το API 4invoices

  • Το δημόσιο API του 4invoices είναι REST μέσω HTTPS, με JSON στην είσοδο και JSON στην έξοδο.
  • Ένας διακομιστής παραγωγής: https://api.4invoices.net/v1. Δεν υπάρχει ξεχωριστός διακομιστής δοκιμών.
  • Η προδιαγραφή OpenAPI 3.1 είναι δημόσια και χωρίς κλειδί — εισαγάγετέ την απευθείας στο Postman ή σε γεννήτρια πελάτης.
  • Κάθε απόκριση περιέχει την κεφαλίδα X-Request-Id· αναφέρετέ την όταν ζητάτε υποστήριξη.
  • Κάθε φορολογική εγγραφή είναι idempotent: μια επανάληψη μετά από Τάιμαουτ δεν εκδίδει ποτέ δεύτερο παραστατικό.
Μόνο μεταξύ διακομιστών. Μην καλείτε ποτέ το API από JavaScript του browser: το client_secret θα έφτανε στον επισκέπτη και το CORS μπλοκάρει κάθε πηγή.
  • Ο πλήρης ρουμανικός κύκλος ANAF e-Factura μέσω API: προέλεγχος, υποβολή, παρακολούθηση, υπογεγραμμένη απόκριση και εισερχόμενα. Κανένας ρουμανικός ανταγωνιστής δεν το προσφέρει.
  • Το SmartBill δεν έχει κανένα endpoint για υποβολή στο εθνικό σύστημα ηλεκτρονικής τιμολόγησης (ANAF, στη Ρουμανία) — η αποστολή στο SPV είναι ρύθμιση λογαριασμού μόνο στο περιβάλλον χρήστη — και κανένα endpoint λίστας τιμολογίων.
  • Το Oblio μπορεί να δημιουργεί πελάτες και προϊόντα μόνο ως παρενέργεια της έκδοσης παραστατικού, όχι ως αυτόνομους πόρους.
  • Παραγωγή XML UBL (στη Ρουμανία: CIUS-RO) χωρίς υποβολή: παίρνετε το νόμιμο παραστατικό ως αρχείο χωρίς να αγγίξετε το εθνικό σύστημα ηλεκτρονικής τιμολόγησης.
  • Idempotency στις φορολογικές εγγραφές: μια επανάληψη μετά από Τάιμαουτ δεν δημιουργεί δεύτερο τιμολόγιο ούτε καίει δεύτερο αριθμό.
  • Εισερχόμενα τιμολόγια προμηθευτών αναγνώσιμα προγραμματιστικά — η βάση για αυτοματοποιημένη συμφωνία.

Το πρώτο τιμολόγιο σε 6 βήματα

Πριν από την πρώτη κλήση
  1. Δημιουργήστε διαπιστευτήρια στο πάνελ, στο Τιμολόγηση → Ρυθμίσεις → Πρόσβαση API, και αντιγράψτε το client_secret — εμφανίζεται μόνο μία φορά.
  2. Συμπληρώστε το προφίλ της εταιρείας και ενεργοποιήστε τουλάχιστον μία σειρά τύπου fiscale, αλλιώς η έκδοση επιστρέφει issuer_profile_incomplete ή no_series_configured.
  1. Δημιουργήστε διαπιστευτήρια στο πάνελ, στο Τιμολόγηση → Ρυθμίσεις → Πρόσβαση API, και αντιγράψτε το client_secret — εμφανίζεται μόνο μία φορά.
  2. Συμπληρώστε το προφίλ της εταιρείας και ενεργοποιήστε τουλάχιστον μία σειρά τύπου fiscale, αλλιώς η έκδοση επιστρέφει issuer_profile_incomplete ή no_series_configured.
  3. Ανταλλάξτε τα διαπιστευτήρια με token: POST /v1/auth/token με grant_type client_credentials. Το τόκεν ισχύει μία ώρα — αποθηκεύστε το, μη ζητάτε νέο σε κάθε κλήση.
  4. Εκδώστε το τιμολόγιο: POST /v1/invoices με κεφαλίδα Idempotency-Key και vat_rate ως ποσοστό (21, όχι 0.21).
  5. Τρέξτε τον προέλεγχο (στη Ρουμανία: CIUS-RO) με POST /v1/invoices/{id}/efactura/validate και στείλτε το τιμολόγιο στο εθνικό σύστημα ηλεκτρονικής τιμολόγησης (στη Ρουμανία: ANAF) με POST /v1/invoices/{id}/efactura/submit.
  6. Παρακολουθήστε την κατάσταση με GET /v1/efactura/submissions/{id} μέχρι να φτάσει σε accepted ή rejected.
bash
# 1 — τόκεν (1 ώρα, αποθηκεύστε το στην κρυφή μνήμη)
ACCESS_TOKEN=$(curl -sS -X POST https://api.4invoices.net/v1/auth/token \
  -H 'Content-Type: application/json' \
  -d '{"grant_type":"client_credentials","client_id":"cli_XXX","client_secret":"sec_XXX"}' \
  | python3 -c 'import sys,json; print(json.load(sys.stdin)["access_token"])')

# 2 — έκδοση του τιμολογίου (το Idempotency-Key είναι ΥΠΟΧΡΕΩΤΙΚΟ)
curl -sS -X POST https://api.4invoices.net/v1/invoices \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: order-10457-invoice' \
  -d '{"buyer":{"name":"Exemplu Comert SRL","tax_id":"RO12345678","county":"Cluj","city":"Cluj-Napoca","country":"RO"},
       "lines":[{"description":"Abonament SaaS","quantity":1,"unit_price":400,"vat_rate":21}]}'

# 3 — preflight CIUS-RO, then send to ANAF
curl -sS -X POST https://api.4invoices.net/v1/invoices/inv_01J7ABC/efactura/validate \
  -H "Authorization: Bearer $ACCESS_TOKEN"

curl -sS -X POST https://api.4invoices.net/v1/invoices/inv_01J7ABC/efactura/submit \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Idempotency-Key: efactura-inv_01J7ABC-1'
javascript
const BASE = "https://api.4invoices.net/v1";

const token = await fetch(`${BASE}/auth/token`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    grant_type: "client_credentials",
    client_id: process.env.FOURINVOICES_CLIENT_ID,
    client_secret: process.env.FOURINVOICES_CLIENT_SECRET,
  }),
}).then((r) => r.json()).then((d) => d.access_token);

const invoice = await fetch(`${BASE}/invoices`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${token}`,
    "Content-Type": "application/json",
    "Idempotency-Key": `order-${orderId}-invoice`,
  },
  body: JSON.stringify({
    buyer: { name: "Exemplu Comert SRL", tax_id: "RO12345678", county: "Cluj", city: "Cluj-Napoca", country: "RO" },
    lines: [{ description: "Abonament SaaS", quantity: 1, unit_price: 400, vat_rate: 21 }],
    currency: "RON",
    metadata: { order_id: String(orderId) },
  }),
}).then((r) => r.json());

console.log(invoice.number, invoice.totals); // "FCT-000123" { net, vat, gross }
python
import os, uuid, requests

BASE = "https://api.4invoices.net/v1"

token = requests.post(f"{BASE}/auth/token", json={
    "grant_type": "client_credentials",
    "client_id": os.environ["FOURINVOICES_CLIENT_ID"],
    "client_secret": os.environ["FOURINVOICES_CLIENT_SECRET"],
}, timeout=30).json()["access_token"]

headers = {
    "Authorization": f"Bearer {token}",
    # Ένα κλειδί ανά ΛΟΓΙΚΗ πράξη — επαναχρησιμοποιήστε το στην επανάληψη, ποτέ μην το δημιουργείτε ξανά.
    "Idempotency-Key": f"order-{order_id}-invoice",
}

invoice = requests.post(f"{BASE}/invoices", headers=headers, json={
    "buyer": {"name": "Exemplu Comert SRL", "tax_id": "RO12345678",
              "county": "Cluj", "city": "Cluj-Napoca", "country": "RO"},
    "lines": [{"description": "Abonament SaaS", "quantity": 1,
               "unit_price": 400, "vat_rate": 21}],   # ποσοστό, όχι 0.21
    "currency": "RON",
}, timeout=60).json()

print(invoice["number"], invoice["totals"])

Ταυτοποίηση

  • Η ροή είναι client_credentials: ανταλλάσσετε client_id + client_secret με ένα Bearer access_token.
  • Το τόκεν λήγει σε 3600 δευτερόλεπτα. Ανανεώνετέ το πριν τη λήξη, όχι σε κάθε κλήση — το POST /auth/token έχει δικό του όριο ανά IP.
  • Το client_secret αποθηκεύεται μόνο ως Χας και δεν ανακτάται. Αν το χάσετε, δημιουργείτε νέα διαπιστευτήρια και ανακαλείτε τα παλιά.
  • Μόνο μεταξύ διακομιστών. Κανένα πρόγραμμα περιήγησης πηγή δεν επιτρέπεται μέσω CORS: από JavaScript της σελίδας το μυστικό θα διέρρεε στον επισκέπτη.
  • Τα πεδία παραχωρούνται ανά σύνολο διαπιστευτηρίων. Εκδώστε ξεχωριστά διαπιστευτήρια για κάθε σύστημα και δώστε στο καθένα τα ελάχιστα πεδία που χρειάζεται.

Τιμολόγηση → Ρυθμίσεις → Πρόσβαση API

bash
curl -sS -X POST https://api.4invoices.net/v1/auth/token \
  -H 'Content-Type: application/json' \
  -d '{
    "grant_type": "client_credentials",
    "client_id": "cli_XXXXXXXXXXXX",
    "client_secret": "sec_XXXXXXXXXXXXXXXXXXXX"
  }'

# → {"access_token":"eyJ...","token_type":"Bearer","expires_in":3600,
#    "scope":"invoices:read invoices:write efactura:submit"}
javascript
// Αποθηκεύστε το τόκεν στην κρυφή μνήμη· μην δημιουργείτε ένα ανά αίτημα.
let cached = { token: "", expiresAt: 0 };

async function getToken() {
  if (cached.token && Date.now() < cached.expiresAt - 60_000) return cached.token;

  const res = await fetch("https://api.4invoices.net/v1/auth/token", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      grant_type: "client_credentials",
      client_id: process.env.FOURINVOICES_CLIENT_ID,
      client_secret: process.env.FOURINVOICES_CLIENT_SECRET,
    }),
  });
  if (!res.ok) throw new Error(`token ${res.status}`);

  const data = await res.json();
  cached = { token: data.access_token, expiresAt: Date.now() + data.expires_in * 1000 };
  return cached.token;
}

Οι συντελεστές ΦΠΑ είναι ποσοστά

vat_rate: 21 ✓  ·  vat_rate: 0.19 ✗  ·  vat_rate: 1 ✗
  • Το vat_rate είναι ΠΟΣΟΣΤΟ: 21 σημαίνει 21%.
  • Τιμές στο (0, 1] απορρίπτονται — το 0.19 είναι κλάσμα και το 1 είναι αμφίσημο μεταξύ 1% και 100%.
  • Το 0 είναι έγκυρο και σημαίνει απαλλαγή ή αντίστροφη επιβάρυνση· γράψτε τη νομική αναφορά στο σημειώσεις.
  • Το unit_price είναι η ΚΑΘΑΡΗ τιμή μονάδας όταν ο εκδότης είναι υπόχρεος ΦΠΑ.
  • Χρειάζεστε τα σύνολα πριν την οριστικοποίηση; Το POST /v1/invoices/preview-totals εκτελεί τους ίδιους υπολογισμούς χωρίς να καταναλώνει φορολογικό αριθμό.

Idempotency στις φορολογικές εγγραφές

  • Η κεφαλίδα Idempotency-Key είναι ΥΠΟΧΡΕΩΤΙΚΗ στα POST /invoices, POST /invoices/{id}/storno και POST /invoices/{id}/efactura/submit.
  • Τα κλειδιά διατηρούνται για 24 ώρες. Η επανάληψη του ίδιου κλειδιού με το ίδιο σώμα επιστρέφει το αρχικό αποτέλεσμα και ορίζει Idempotency-Replayed: true.
  • Το ίδιο κλειδί με διαφορετικό σώμα επιστρέφει 409 idempotency_key_reuse — ένα ήδη εκδοθέν φορολογικό παραστατικό δεν αντικαθίσταται ποτέ.
  • Ένα κλειδί ανά ΛΟΓΙΚΗ λειτουργία, όχι ανά απόπειρα HTTP: order-10457-invoice, όχι νέο UUID σε κάθε επανάληψη.
  • Ο λόγος είναι νομικός, όχι τεχνικός: ένας φορολογικός αριθμός που καταναλώθηκε και ένα τιμολόγιο που κατατέθηκε στο εθνικό σύστημα ηλεκτρονικής τιμολόγησης (στη Ρουμανία: SPV) δεν μπορούν να αποσυρθούν.
bash
curl -sS -X POST https://api.4invoices.net/v1/invoices \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: order-10457-invoice' \
  -d '{
    "buyer": {
      "name": "Exemplu Comert SRL",
      "tax_id": "RO12345678",
      "address": "Str. Fabricii 12",
      "city": "Cluj-Napoca",
      "county": "Cluj",
      "country": "RO",
      "email": "[email protected]"
    },
    "lines": [
      { "description": "Abonament SaaS, iulie", "quantity": 1, "unit_price": 400, "vat_rate": 21, "unit": "buc" },
      { "description": "Implementare", "quantity": 6, "unit_price": 250, "vat_rate": 21, "unit": "ora" }
    ],
    "currency": "RON",
    "document_kind": "factura",
    "due_date": "2026-08-15",
    "metadata": { "order_id": "10457" }
  }'

Όλα τα Ένδποιντς

ΜέθοδοςEndpointScopeIdempotency-KeyΑιτήματα / λεπτό
POST/auth/token
Ανταλλαγή διαπιστευτηρίων με διακριτικό πρόσβασης
——120
GET/me
Τενάντ, πεδία και τρέχοντα όρια
invoices:read—120
POST/invoices
Έκδοση τιμολογίου ή προτιμολόγιο
invoices:writeυποχρεωτικό30
GET/invoices
Λίστα τιμολογίων (σελιδοποίηση με δρομέας)
invoices:read—120
POST/invoices/preview-totals
Υπολογισμός συνόλων χωρίς δημιουργία τίποτα
invoices:read—120
GET/invoices/{invoice_id}
Ανάκτηση ενός τιμολογίου
invoices:read—120
GET/invoices/{invoice_id}/pdf
Λήψη του PDF του τιμολογίου
invoices:read—120
GET/invoices/{invoice_id}/xml
Λήψη του XML UBL (στη Ρουμανία: CIUS-RO)
invoices:read—120
POST/invoices/{invoice_id}/storno
Έκδοση πιστωτικού σημειώματος που ακυρώνει αυτό το τιμολόγιο
invoices:writeυποχρεωτικό30
POST/invoices/{invoice_id}/payments
Σήμανση τιμολογίου ως εξοφλημένου
invoices:write—30
DELETE/invoices/{invoice_id}/payments
Σήμανση τιμολογίου ως μη εξοφλημένου
invoices:write—30
GET/clients
Λίστα πελατών
clients:read—120
POST/clients
Δημιουργία ή ενημέρωση πελάτη
clients:write—30
GET/lookup/company/{tax_id}
Επικύρωση CUI στο μητρώο της ANAF (Ρουμανία)
clients:read—120
GET/series
Λίστα σειρών αρίθμησης
invoices:read—120
POST/invoices/{invoice_id}/efactura/validate
Προέλεγχος (στη Ρουμανία: CIUS-RO) — επικύρωση χωρίς αποστολή
efactura:read—120
POST/invoices/{invoice_id}/efactura/submit
Υποβολή του τιμολογίου στο εθνικό σύστημα ηλεκτρονικής τιμολόγησης (στη Ρουμανία: ANAF/SPV)
efactura:submitυποχρεωτικό10
GET/efactura/submissions
Λίστα υποβολών προς το εθνικό σύστημα ηλεκτρονικής τιμολόγησης
efactura:read—120
GET/efactura/submissions/{submission_id}
Ανάκτηση μίας υποβολής
efactura:read—120
POST/efactura/submissions/{submission_id}/refresh
Αίτημα προς το εθνικό σύστημα ηλεκτρονικής τιμολόγησης για την τρέχουσα κατάσταση
efactura:read—10
GET/efactura/submissions/{submission_id}/download
Λήψη του αρχείου της υπογεγραμμένης απάντησης
efactura:read—120
GET/efactura/inbox
Τιμολόγια προμηθευτών που ελήφθησαν μέσω του εθνικού συστήματος ηλεκτρονικής τιμολόγησης
efactura:read—120
GET/efactura/inbox/{inbound_id}
Ανάκτηση ενός εισερχόμενου τιμολογίου
efactura:read—120
GET/efactura/inbox/{inbound_id}/download
Λήψη του αρχείου εισερχόμενου τιμολογίου
efactura:read—120
POST/efactura/inbox/sync
Λήψη νέων μηνυμάτων του εθνικού συστήματος ηλεκτρονικής τιμολόγησης στα εισερχόμενα
efactura:read—10

25 endpoints · https://api.4invoices.net/v1/openapi.json

Scopes

  • invoices:read
  • invoices:write
  • clients:read
  • clients:write
  • efactura:read
  • efactura:submit

Η διαδρομή προς το εθνικό σύστημα ηλεκτρονικής τιμολόγησης

not_sentvalidatingqueuedsentprocessingacceptedrejectederror
  • Η πραγματική διαφορά: ολόκληρος ο κύκλος με το εθνικό σύστημα ηλεκτρονικής τιμολόγησης (στη Ρουμανία: ANAF e-Factura) εκτελείται μέσω του API, όχι μόνο η παραγωγή του παραστατικού.
  • Το POST /invoices/{id}/efactura/validate εκτελεί τον προέλεγχο (στη Ρουμανία: CIUS-RO) και επιστρέφει ακριβώς τα πεδία που θα απέρριπτε το εθνικό σύστημα ηλεκτρονικής τιμολόγησης, χωρίς να στείλει τίποτα.
  • Το GET /invoices/{id}/xml παράγει το XML σε UBL (στη Ρουμανία: CIUS-RO) χωρίς υποβολή — χρήσιμο για το δικό σας αρχείο ή για εσωτερικό έλεγχο.
  • Το POST /invoices/{id}/efactura/submit το καταθέτει στο εθνικό σύστημα ηλεκτρονικής τιμολόγησης (στη Ρουμανία: SPV). Αυτό είναι μη αναστρέψιμο: ένα δεύτερο αντίγραφο του ίδιου τιμολογίου δεν μπορεί να αποσυρθεί από το σύστημα.
  • Ο κύκλος ζωής είναι κανονικοποιημένος: not_sent → επικύρωση → σε ουρά → στάλθηκε → επεξεργασία → accepted | rejected | σφάλμα. Το accepted σημαίνει νόμιμα διαβιβασμένο· το σφάλμα είναι αποτυχία μεταφοράς από τη δική μας πλευρά και η επανάληψη είναι ασφαλής.
  • Το GET /efactura/submissions/{id}/download ανακτά το αρχείο με την υπογεγραμμένη απόκριση του εθνικού συστήματος (στη Ρουμανία: ANAF).
  • Υπάρχει και η εισερχόμενη πλευρά: το POST /efactura/inbox/sync φέρνει τα νέα μηνύματα από το εθνικό σύστημα ηλεκτρονικής τιμολόγησης (στη Ρουμανία: SPV) και το GET /efactura/inbox απαριθμεί τα τιμολόγια που σας έστειλαν οι προμηθευτές σας.
bash
# CIUS-RO preflight. Sends NOTHING to ANAF.
curl -sS -X POST "https://api.4invoices.net/v1/invoices/inv_01J7ABC/efactura/validate?include_xml=true" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

# → {"invoice_id":"inv_01J7ABC","valid":false,
#    "errors":["buyer.county is required for RO buyers"],
#    "xml_base64":"PD94bWwg..."}
bash
curl -sS -X POST https://api.4invoices.net/v1/invoices/inv_01J7ABC/efactura/submit \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Idempotency-Key: efactura-inv_01J7ABC-1'

# → {"submission_id":"sub_01J7XYZ","status":"queued"}
python
import time, requests

BASE = "https://api.4invoices.net/v1"
TERMINAL = {"accepted", "rejected"}
H = {"Authorization": f"Bearer {token}"}

def wait_for_anaf(submission_id, timeout=900):
    deadline = time.time() + timeout
    delay = 15
    while time.time() < deadline:
        r = requests.get(f"{BASE}/efactura/submissions/{submission_id}", headers=H, timeout=30)
        r.raise_for_status()
        status = r.json()["status"]
        if status in TERMINAL:
            return r.json()
        # /refresh asks ANAF directly and shares the 10/min submit bucket.
        requests.post(f"{BASE}/efactura/submissions/{submission_id}/refresh", headers=H, timeout=30)
        time.sleep(delay)
        delay = min(delay * 2, 120)
    raise TimeoutError(submission_id)

Σφάλματα

  • Κάθε 4xx και 5xx χρησιμοποιεί τον ίδιο φάκελο: { "error": { type, code, message, param?, doc_url, request_id, σφάλματα? } }.
  • Το type είναι η γενική κατηγορία στην οποία βασίζετε τη διακλάδωση («μπορώ να ξαναδοκιμάσω;»), και το code είναι η σταθερή αιτία που αντιστοιχίζετε στο δικό σας σύστημα.
  • Το param είναι η διαδρομή με τελείες προς το προβληματικό πεδίο, για παράδειγμα lines.0.vat_rate.
  • Το σφάλματα[] εμφανίζεται όταν ένα μόνο αίτημα έχει προβλήματα σε περισσότερα από ένα πεδία.
  • Το request_id καταγράφεται από τη δική μας πλευρά. Αναφέρετέ το σε κάθε αίτημα υποστήριξης — εντοπίζει ακριβώς το αίτημα.
  • Ένα 404 επιστρέφεται επίσης για id που ανήκει σε άλλον tenant: δεν επιβεβαιώνουμε ποτέ ότι υπάρχουν δεδομένα κάποιου άλλου.
json
{
  "error": {
    "type": "invalid_request_error",
    "code": "validation_failed",
    "message": "Datele trimise nu sunt valide.",
    "param": "lines.0.vat_rate",
    "doc_url": "https://4invoices.net/ro/api-dezvoltatori#errors",
    "request_id": "req_8a893f2f33d4472da8d6e144",
    "errors": [
      { "param": "lines.0.vat_rate", "message": "VAT rate must be a percent (21), not a fraction", "type": "value_error" }
    ]
  }
}
ΚατάστασηΤύποςΚωδικός
401authentication_errormissing_bearer_token
401authentication_errorinvalid_client
401authentication_errortoken_expired
401authentication_errorinvalid_token
401authentication_errorunauthorized
403permission_errorinsufficient_scope
403permission_errorforbidden
404not_found_errornot_found
409conflict_erroridempotency_key_reuse
409conflict_erroridempotency_in_progress
409conflict_errorno_series_configured
422invalid_request_errorvalidation_failed
422invalid_request_errorissuer_profile_incomplete
422invalid_request_errorcius_ro_incomplete
422invalid_request_errorcannot_storno_draft
422invalid_request_errornothing_to_storno
422invalid_request_errorinvalid_json_number
429rate_limit_errorrate_limit_exceeded
502upstream_errorupstream_error
503service_unavailable_errorservice_unavailable

Όρια αιτημάτων

BucketΑιτήματα / λεπτόΤι σημαίνει
read120GET *, POST /invoices/preview-totals, POST /invoices/{id}/efactura/validate
write30POST /invoices, POST /invoices/{id}/storno, POST|DELETE /invoices/{id}/payments, POST /clients
submit10POST /invoices/{id}/efactura/submit, POST /efactura/submissions/{id}/refresh, POST /efactura/inbox/sync
  • Τα όρια ισχύουν ανά σύνολο διαπιστευτηρίων, ανά λεπτό: 120 για αναγνώσεις, 30 για εγγραφές, 10 για τις λειτουργίες με το εθνικό σύστημα ηλεκτρονικής τιμολόγησης (submit, refresh, inbox/sync).
  • Τα X-RateLimit-Limit, X-RateLimit-Remaining και X-RateLimit-Reset εμφανίζονται σε ΚΑΘΕ απόκριση, συμπεριλαμβανομένου του 429.
  • Ένα 429 περιέχει επίσης Retry-After σε δευτερόλεπτα. Τηρήστε το· μην επαναλαμβάνετε σε σφιχτό βρόχο.
  • Ο κουβάς των 10/min υπάρχει επειδή στη Ρουμανία η ANAF επιτρέπει περίπου μία κλήση λίστας μηνυμάτων ανά λεπτό ανά CIF.
  • Βάλτε το /efactura/inbox/sync σε cron, όχι σε επιθετικό Πόλινγκ.
  • Χρειάζεστε υψηλότερα όρια; Ζητήστε τα μέσω της Επικοινωνίας, αναφέροντας τον αναμενόμενο ημερήσιο όγκο.

X-RateLimit-Limit · X-RateLimit-Remaining · X-RateLimit-Reset · Retry-After

http
HTTP/2 429
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1785312000
Retry-After: 17
Content-Type: application/json

{"error":{"type":"rate_limit_error","code":"rate_limit_exceeded",
 "message":"...","doc_url":"https://4invoices.net/ro/api-dezvoltatori#errors",
 "request_id":"req_..."}}
javascript
// Τηρείτε το Retry-After· μην επαναλαμβάνετε ποτέ 4xx εκτός του 429.
async function call(url, init, attempt = 0) {
  const res = await fetch(url, init);
  if (res.status === 429 || res.status >= 500) {
    if (attempt >= 5) return res;
    const retryAfter = Number(res.headers.get("Retry-After")) || 2 ** attempt;
    await new Promise((r) => setTimeout(r, retryAfter * 1000));
    return call(url, init, attempt + 1);
  }
  return res;
}

// Μια επανάληψη εγγραφής ΠΡΕΠΕΙ να επαναχρησιμοποιήσει το ΙΔΙΟ Idempotency-Key, αλλιώς εκδίδετε
// ένα δεύτερο φορολογικό παραστατικό.

Οδηγοί κατά θέμα

Κάθε θέμα έχει τη σελίδα του, με τα σχετικά Ένδποιντς, παραδείγματα και σφάλματα.

Συχνά λάθη

  • Αποστολή του ΦΠΑ ως κλάσμα (0.19) ή ως 1. Και τα δύο απορρίπτονται· στείλτε 19 ή 21.
  • Έκδοση τόκεν σε κάθε αίτημα. Το τόκεν ισχύει μία ώρα· αποθηκεύστε το στην κρυφή μνήμη, αλλιώς θα χτυπήσετε το όριο ανά IP του τόκεν Ένδποιντ.
  • Δημιουργία νέου Ιδεμποτεντότητα κλειδί σε κάθε επανάληψη — ακριβώς το σενάριο που παράγει διπλά τιμολόγια.
  • Κλήσεις από JavaScript του πρόγραμμα περιήγησης. Το CORS μπλοκάρει κάθε πηγή, και το μυστικό θα εκτίθετο ούτως ή άλλως.
  • Κατάθεση στο εθνικό σύστημα ηλεκτρονικής τιμολόγησης χωρίς τον προέλεγχο (στη Ρουμανία: CIUS-RO). Μια λανθασμένη υποβολή δεν αποσύρεται.
  • Πόλινγκ στο /efactura/submissions/{id}/refresh περισσότερες από 10 φορές το λεπτό — αυτός ο κουβάς είναι κοινός με το υποβάλλω.
  • Έκδοση πριν ολοκληρωθεί το προφίλ της εταιρείας και πριν υπάρξει ενεργή φορολογική σειρά.

Ερωτήσεις και απαντήσεις για προγραμματιστές

Πώς αποκτώ διαπιστευτήρια για το API 4invoices;

Από το πάνελ, στο Τιμολόγηση → Ρυθμίσεις → Πρόσβαση API. Λαμβάνετε client_id και client_secret· το μυστικό εμφανίζεται μία φορά και αποθηκεύεται μόνο ως Χας.

Πόσο διαρκεί το access_token;

3600 δευτερόλεπτα, δηλαδή μία ώρα. Αποθηκεύστε το σε κρυφή μνήμη και ανανεώστε πριν λήξει — το Ένδποιντ του τόκεν έχει δικό του όριο ανά IP.

Πώς στέλνω τον συντελεστή ΦΠΑ;

Ως ποσοστό: 21 για 21%. Τιμές στο (0, 1] απορρίπτονται, άρα 0.19 και 1 αποτυγχάνουν στον έλεγχο.

Ποιες κλήσεις απαιτούν Idempotency-Key;

POST /invoices, POST /invoices/{id}/storno και POST /invoices/{id}/efactura/submit. Τα κλειδιά κρατούνται 24 ώρες και μια επανάληψη επιστρέφει το αρχικό έγγραφο με Idempotency-Replayed: true.

Μπορώ να υποβάλω τιμολόγιο στο εθνικό σύστημα ηλεκτρονικής τιμολόγησης απευθείας από το API;

Ναι. Προέλεγχος με /efactura/validate (στη Ρουμανία: CIUS-RO), υποβολή με /efactura/submit, παρακολούθηση της κανονικοποιημένης κατάστασης στο /efactura/submissions/{id} και λήψη της υπογεγραμμένης απάντησης του συστήματος (στη Ρουμανία: ANAF).

Ποια είναι τα όρια αιτημάτων;

Ανά διαπιστευτήριο, ανά λεπτό: 120 αναγνώσεις, 30 εγγραφές και 10 λειτουργίες με το εθνικό σύστημα ηλεκτρονικής τιμολόγησης. Κάθε απάντηση φέρει κεφαλίδες X-RateLimit-*, και το 429 προσθέτει Retry-After.

Μπορώ να καλέσω το API από τη διεπαφή χρήστη;

Όχι. Είναι αποκλειστικά server-to-server: κανένα πηγή πρόγραμμα περιήγησης δεν περνά το CORS και το client_secret δεν έχει θέση σε κώδικα που φτάνει στον επισκέπτη.

Γιατί λαμβάνω issuer_profile_incomplete στο πρώτο τιμολόγιο;

Το προφίλ της εταιρείας είναι ελλιπές ή δεν υπάρχει ενεργή σειρά τύπου fiscale. Συμπληρώστε και τα δύο στο πάνελ· το API αρνείται να παραγάγει άκυρο φορολογικό έγγραφο.