Το δημόσιο 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: μια επανάληψη μετά από Τάιμαουτ δεν εκδίδει ποτέ δεύτερο παραστατικό.
- Ο πλήρης ρουμανικός κύκλος ANAF e-Factura μέσω API: προέλεγχος, υποβολή, παρακολούθηση, υπογεγραμμένη απόκριση και εισερχόμενα. Κανένας ρουμανικός ανταγωνιστής δεν το προσφέρει.
- Το SmartBill δεν έχει κανένα endpoint για υποβολή στο εθνικό σύστημα ηλεκτρονικής τιμολόγησης (ANAF, στη Ρουμανία) — η αποστολή στο SPV είναι ρύθμιση λογαριασμού μόνο στο περιβάλλον χρήστη — και κανένα endpoint λίστας τιμολογίων.
- Το Oblio μπορεί να δημιουργεί πελάτες και προϊόντα μόνο ως παρενέργεια της έκδοσης παραστατικού, όχι ως αυτόνομους πόρους.
- Παραγωγή XML UBL (στη Ρουμανία: CIUS-RO) χωρίς υποβολή: παίρνετε το νόμιμο παραστατικό ως αρχείο χωρίς να αγγίξετε το εθνικό σύστημα ηλεκτρονικής τιμολόγησης.
- Idempotency στις φορολογικές εγγραφές: μια επανάληψη μετά από Τάιμαουτ δεν δημιουργεί δεύτερο τιμολόγιο ούτε καίει δεύτερο αριθμό.
- Εισερχόμενα τιμολόγια προμηθευτών αναγνώσιμα προγραμματιστικά — η βάση για αυτοματοποιημένη συμφωνία.
Το πρώτο τιμολόγιο σε 6 βήματα
- Δημιουργήστε διαπιστευτήρια στο πάνελ, στο Τιμολόγηση → Ρυθμίσεις → Πρόσβαση API, και αντιγράψτε το client_secret — εμφανίζεται μόνο μία φορά.
- Συμπληρώστε το προφίλ της εταιρείας και ενεργοποιήστε τουλάχιστον μία σειρά τύπου fiscale, αλλιώς η έκδοση επιστρέφει issuer_profile_incomplete ή no_series_configured.
- Δημιουργήστε διαπιστευτήρια στο πάνελ, στο Τιμολόγηση → Ρυθμίσεις → Πρόσβαση API, και αντιγράψτε το client_secret — εμφανίζεται μόνο μία φορά.
- Συμπληρώστε το προφίλ της εταιρείας και ενεργοποιήστε τουλάχιστον μία σειρά τύπου fiscale, αλλιώς η έκδοση επιστρέφει issuer_profile_incomplete ή no_series_configured.
- Ανταλλάξτε τα διαπιστευτήρια με token: POST /v1/auth/token με grant_type client_credentials. Το τόκεν ισχύει μία ώρα — αποθηκεύστε το, μη ζητάτε νέο σε κάθε κλήση.
- Εκδώστε το τιμολόγιο: POST /v1/invoices με κεφαλίδα Idempotency-Key και vat_rate ως ποσοστό (21, όχι 0.21).
- Τρέξτε τον προέλεγχο (στη Ρουμανία: CIUS-RO) με POST /v1/invoices/{id}/efactura/validate και στείλτε το τιμολόγιο στο εθνικό σύστημα ηλεκτρονικής τιμολόγησης (στη Ρουμανία: ANAF) με POST /v1/invoices/{id}/efactura/submit.
- Παρακολουθήστε την κατάσταση με 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" }
}'Όλα τα Ένδποιντς
| Μέθοδος | Endpoint | Scope | Idempotency-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
Η διαδρομή προς το εθνικό σύστημα ηλεκτρονικής τιμολόγησης
- Η πραγματική διαφορά: ολόκληρος ο κύκλος με το εθνικό σύστημα ηλεκτρονικής τιμολόγησης (στη Ρουμανία: 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" }
]
}
}| Κατάσταση | Τύπος | Κωδικός |
|---|---|---|
| 401 | authentication_error | missing_bearer_token |
| 401 | authentication_error | invalid_client |
| 401 | authentication_error | token_expired |
| 401 | authentication_error | invalid_token |
| 401 | authentication_error | unauthorized |
| 403 | permission_error | insufficient_scope |
| 403 | permission_error | forbidden |
| 404 | not_found_error | not_found |
| 409 | conflict_error | idempotency_key_reuse |
| 409 | conflict_error | idempotency_in_progress |
| 409 | conflict_error | no_series_configured |
| 422 | invalid_request_error | validation_failed |
| 422 | invalid_request_error | issuer_profile_incomplete |
| 422 | invalid_request_error | cius_ro_incomplete |
| 422 | invalid_request_error | cannot_storno_draft |
| 422 | invalid_request_error | nothing_to_storno |
| 422 | invalid_request_error | invalid_json_number |
| 429 | rate_limit_error | rate_limit_exceeded |
| 502 | upstream_error | upstream_error |
| 503 | service_unavailable_error | service_unavailable |
Όρια αιτημάτων
| Bucket | Αιτήματα / λεπτό | Τι σημαίνει |
|---|---|---|
| read | 120 | GET *, POST /invoices/preview-totals, POST /invoices/{id}/efactura/validate |
| write | 30 | POST /invoices, POST /invoices/{id}/storno, POST|DELETE /invoices/{id}/payments, POST /clients |
| submit | 10 | POST /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, αλλιώς εκδίδετε
// ένα δεύτερο φορολογικό παραστατικό.Οδηγοί κατά θέμα
Κάθε θέμα έχει τη σελίδα του, με τα σχετικά Ένδποιντς, παραδείγματα και σφάλματα.
- Ταυτοποίηση στο API 4invoicesΑνταλλάσσετε client_id και client_secret με ένα Bearer τόκεν μιας ώρας, μόνο μεταξύ διακομιστών.
- Έκδοση τιμολογίων μέσω του APIPOST /invoices με Idempotency-Key, ΦΠΑ σε ποσοστά, μαζί με λίστα, πιστωτικό και σήμανση εξόφλησης.
- Πελάτες και αναζήτηση στο μητρώο επιχειρήσεωνΕισαγωγή ή ενημέρωση με απαλοιφή διπλοτύπων βάσει tax_id και αναζήτηση στο μητρώο της ANAF (Ρουμανία), ώστε να μην απορριφθεί το ηλεκτρονικό τιμολόγιο.
- Εθνικό σύστημα ηλεκτρονικής τιμολόγησης μέσω του APIΠροέλεγχος (στη Ρουμανία: CIUS-RO), υποβολή στο εθνικό σύστημα (στη Ρουμανία: SPV), κανονικοποιημένη κατάσταση, υπογεγραμμένη απάντηση και εισερχόμενα τιμολόγια.
- Σειρές αρίθμησης στο APIΧωρίς ενεργή σειρά τύπου fiscale, η έκδοση επιστρέφει 409 no_series_configured.
- Κωδικοί σφαλμάτων και ο φάκελος σφάλματοςΜία μορφή σε κάθε διαδρομή: type, code, message, param, doc_url, request_id και σφάλματα.
- Όρια αιτημάτων και επαναλήψεις120 αναγνώσεις, 30 εγγραφές και 10 λειτουργίες με το εθνικό σύστημα ηλεκτρονικής τιμολόγησης το λεπτό ανά διαπιστευτήριο, με κεφαλίδες X-RateLimit-*.
Συχνά λάθη
- Αποστολή του ΦΠΑ ως κλάσμα (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 αρνείται να παραγάγει άκυρο φορολογικό έγγραφο.