Die öffentliche 4invoices-API überträgt Rechnungsdaten aus Ihrem System nach 4invoices und bringt die Rechnung in Rumänien vollständig durch das ANAF-SPV. Ein Produktionshost, ein client_credentials-Flow, idempotente Fiskalschreibvorgänge und ein einheitlicher Fehlerumschlag auf jeder Route.
https://api.4invoices.net/v1 · OpenAPI-Spezifikation (ohne Schlüssel)
Was die 4invoices-API ist
- Die öffentliche API von 4invoices ist REST über HTTPS, mit JSON als Ein- und Ausgabeformat.
- Es gibt einen einzigen Produktions-Host: https://api.4invoices.net/v1. Einen separaten Test-Host gibt es nicht.
- Die OpenAPI-3.1-Spezifikation ist öffentlich und ohne Schlüssel abrufbar – importieren Sie sie direkt in Postman oder einen Client-Generator.
- Jede Antwort enthält einen X-Request-Id-Header; geben Sie ihn bei Supportanfragen an.
- Jeder fiskalische Schreibvorgang ist idempotent: Ein erneuter Versuch nach einem Timeout erzeugt nie ein zweites Dokument.
- Für Rumänien der vollständige ANAF-e-Factura-Kreislauf über die API: Vorabprüfung, Einreichung, Statusverfolgung, signierte Antwort und der Eingangsposteingang. Kein rumänischer Wettbewerber bietet das an.
- SmartBill hat keinen Endpunkt für die Einreichung im nationalen E-Rechnungssystem (ANAF, in Rumänien) – der SPV-Versand ist eine reine UI-Kontoeinstellung – und überhaupt keinen Endpunkt zum Auflisten von Rechnungen.
- Oblio kann Kunden und Produkte nur als Nebeneffekt der Dokumentausstellung anlegen, nicht als eigenständige Ressourcen.
- UBL-/CIUS-RO-XML-Erzeugung (Rumänien) ohne Einreichung: Sie erhalten das rechtsgültige Dokument als Datei, ohne den SPV zu berühren.
- Idempotenz bei fiskalischen Schreibvorgängen: Ein Retry nach einem Timeout erzeugt keine zweite Rechnung und verbraucht keine zweite Nummer.
- Ein programmatisch lesbarer Posteingang für Lieferantenrechnungen – die Grundlage für einen automatisierten Abgleich.
Die erste Rechnung in 6 Schritten
- Erzeugen Sie im Panel unter Rechnungsstellung → Einstellungen → API-Zugang eine Zugangsdaten-Kombination und kopieren Sie das client_secret — es wird nur einmal angezeigt.
- Vervollständigen Sie das Firmenprofil und aktivieren Sie mindestens eine Serie vom Typ fiscale, sonst antwortet die Ausstellung mit issuer_profile_incomplete oder no_series_configured.
- Erzeugen Sie im Panel unter Rechnungsstellung → Einstellungen → API-Zugang eine Zugangsdaten-Kombination und kopieren Sie das client_secret — es wird nur einmal angezeigt.
- Vervollständigen Sie das Firmenprofil und aktivieren Sie mindestens eine Serie vom Typ fiscale, sonst antwortet die Ausstellung mit issuer_profile_incomplete oder no_series_configured.
- Tauschen Sie die Zugangsdaten gegen ein Token: POST /v1/auth/token mit grant_type client_credentials. Das Token gilt eine Stunde — zwischenspeichern, nicht pro Request neu holen.
- Stellen Sie die Rechnung aus: POST /v1/invoices mit Idempotency-Key-Header und vat_rate als Prozentwert (21, nicht 0.21).
- Für den rumänischen Markt führen Sie die CIUS-RO-Vorprüfung mit POST /v1/invoices/{id}/efactura/validate aus und senden die Rechnung dann mit POST /v1/invoices/{id}/efactura/submit an die ANAF.
- Verfolgen Sie den Status mit GET /v1/efactura/submissions/{id}, bis er accepted oder rejected erreicht.
bash
# 1 — Token (1 Stunde, zwischenspeichern)
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 — Rechnung ausstellen (Idempotency-Key ist PFLICHT)
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}",
# Ein Schlüssel pro LOGISCHEM Vorgang — beim Retry wiederverwenden, nie neu erzeugen.
"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}], # Prozentwert, nicht 0.21
"currency": "RON",
}, timeout=60).json()
print(invoice["number"], invoice["totals"])Authentifizierung
- Der Flow ist client_credentials: Sie tauschen client_id + client_secret gegen ein Bearer-access_token.
- Das Token läuft nach 3600 Sekunden ab. Erneuern Sie es vor Ablauf, nicht bei jedem Aufruf – POST /auth/token hat ein eigenes Rate-Limit pro IP.
- Das client_secret wird nur als Hash gespeichert und lässt sich nicht wiederherstellen. Geht es verloren, erstellen Sie eine neue Zugangskennung und widerrufen die alte.
- Nur Server-zu-Server. CORS lässt keinen Browser-Origin zu: Aus JavaScript auf einer Webseite würde das Secret an den Besucher gelangen.
- Scopes werden pro Zugangskennung vergeben. Legen Sie für jedes System eine eigene Zugangskennung an und geben Sie ihr nur die minimal nötigen Scopes.
Rechnungsstellung → Einstellungen → API-Zugang
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
// Token zwischenspeichern; nicht pro Anfrage einen neuen erzeugen.
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;
}MwSt.-Sätze sind Prozentwerte
vat_rate: 21 ✓ · vat_rate: 0.19 ✗ · vat_rate: 1 ✗- vat_rate ist ein PROZENTWERT: 21 bedeutet 21 %.
- Werte im Intervall (0, 1] werden abgelehnt – 0.19 ist ein Bruch, und 1 ist mehrdeutig zwischen 1 % und 100 %.
- 0 ist gültig und bedeutet steuerbefreit oder Reverse Charge; tragen Sie den gesetzlichen Hinweis in notes ein.
- unit_price ist der NETTO-Stückpreis, wenn der Aussteller umsatzsteuerpflichtig ist.
- Sie brauchen die Summen vor dem Festschreiben? POST /v1/invoices/preview-totals führt dieselbe Berechnung aus, ohne eine fiskalische Nummer zu verbrauchen.
Idempotenz bei Fiskalschreibvorgängen
- Der Idempotency-Key-Header ist PFLICHT bei POST /invoices, POST /invoices/{id}/storno und POST /invoices/{id}/efactura/submit.
- Schlüssel werden 24 Stunden lang gespeichert. Wird derselbe Schlüssel mit demselben Body erneut gesendet, kommt das ursprüngliche Ergebnis zurück, zusammen mit Idempotency-Replayed: true.
- Derselbe Schlüssel mit einem anderen Body liefert 409 idempotency_key_reuse – ein bereits ausgestelltes fiskalisches Dokument wird nie überschrieben.
- Ein Schlüssel pro LOGISCHER Operation, nicht pro HTTP-Versuch: order-10457-invoice, nicht bei jedem Retry eine neue UUID.
- Der Grund ist rechtlich, nicht technisch: Eine verbrauchte fiskalische Nummer und eine im nationalen E-Rechnungssystem eingereichte Rechnung lassen sich nicht zurückziehen.
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" }
}'Alle Endpunkte
| Methode | Endpunkt | Scope | Idempotency-Key | Anfragen / Minute |
|---|---|---|---|---|
| POST | /auth/token Zugangsdaten gegen ein Zugriffstoken tauschen | — | — | 120 |
| GET | /me Mandant, Scopes und aktuelle Limits | invoices:read | — | 120 |
| POST | /invoices Rechnung oder Proforma ausstellen | invoices:write | erforderlich | 30 |
| GET | /invoices Rechnungen auflisten (Cursor-Paginierung) | invoices:read | — | 120 |
| POST | /invoices/preview-totals Summen berechnen, ohne etwas anzulegen | invoices:read | — | 120 |
| GET | /invoices/{invoice_id} Eine Rechnung abrufen | invoices:read | — | 120 |
| GET | /invoices/{invoice_id}/pdf Rechnungs-PDF herunterladen | invoices:read | — | 120 |
| GET | /invoices/{invoice_id}/xml UBL-/CIUS-RO-XML (Rumänien) herunterladen | invoices:read | — | 120 |
| POST | /invoices/{invoice_id}/storno Gutschrift ausstellen, die diese Rechnung storniert | invoices:write | erforderlich | 30 |
| POST | /invoices/{invoice_id}/payments Rechnung als bezahlt markieren | invoices:write | — | 30 |
| DELETE | /invoices/{invoice_id}/payments Rechnung als unbezahlt markieren | invoices:write | — | 30 |
| GET | /clients Kunden auflisten | clients:read | — | 120 |
| POST | /clients Kunden anlegen oder aktualisieren | clients:write | — | 30 |
| GET | /lookup/company/{tax_id} Eine rumänische CUI im ANAF-Register prüfen | clients:read | — | 120 |
| GET | /series Nummernkreise auflisten | invoices:read | — | 120 |
| POST | /invoices/{invoice_id}/efactura/validate CIUS-RO-Vorprüfung (Rumänien) — validieren ohne Versand | efactura:read | — | 120 |
| POST | /invoices/{invoice_id}/efactura/submit Rechnung an die ANAF (SPV, Rumänien) senden | efactura:submit | erforderlich | 10 |
| GET | /efactura/submissions ANAF-Übermittlungen (Rumänien) auflisten | efactura:read | — | 120 |
| GET | /efactura/submissions/{submission_id} Eine Übermittlung abrufen | efactura:read | — | 120 |
| POST | /efactura/submissions/{submission_id}/refresh Aktuellen Status bei der ANAF (Rumänien) abfragen | efactura:read | — | 10 |
| GET | /efactura/submissions/{submission_id}/download Archiv der signierten Antwort herunterladen | efactura:read | — | 120 |
| GET | /efactura/inbox Über den SPV (Rumänien) eingegangene Lieferantenrechnungen | efactura:read | — | 120 |
| GET | /efactura/inbox/{inbound_id} Eine eingegangene Rechnung abrufen | efactura:read | — | 120 |
| GET | /efactura/inbox/{inbound_id}/download Archiv der eingegangenen Rechnung herunterladen | efactura:read | — | 120 |
| POST | /efactura/inbox/sync Neue SPV-Nachrichten (ANAF, Rumänien) in den Posteingang abrufen | 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
Der ANAF-e-Factura-Weg (Rumänien)
- Das eigentliche Alleinstellungsmerkmal: Für Rumänien läuft der gesamte ANAF-e-Factura-Kreislauf über die API, nicht nur die Dokumenterzeugung.
- POST /invoices/{id}/efactura/validate führt den CIUS-RO-Preflight (Rumänien) aus und liefert genau die Felder, die ANAF ablehnen würde, ohne etwas zu senden.
- GET /invoices/{id}/xml erzeugt das UBL-/CIUS-RO-XML (das rumänische Profil) ohne Einreichung – nützlich für Ihr eigenes Archiv oder eine interne Prüfung.
- POST /invoices/{id}/efactura/submit reicht die Rechnung im SPV (ANAF, Rumänien) ein. Das ist unumkehrbar: Eine zweite Kopie derselben Rechnung lässt sich nicht aus dem rumänischen SPV zurückziehen.
- Der Lebenszyklus ist normalisiert: not_sent → validating → queued → sent → processing → accepted | rejected | error. accepted bedeutet rechtswirksam übermittelt; error ist ein Übertragungsfehler auf unserer Seite und kann gefahrlos erneut versucht werden.
- GET /efactura/submissions/{id}/download ruft das Archiv mit der signierten Antwort der rumänischen ANAF ab.
- Auch die Eingangsseite gibt es: POST /efactura/inbox/sync holt neue SPV-Nachrichten (ANAF, Rumänien) ab, und GET /efactura/inbox listet die Rechnungen, die Ihnen Ihre Lieferanten geschickt haben.
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)Fehler
- Jede 4xx- und 5xx-Antwort verwendet dieselbe Hülle: { "error": { type, code, message, param?, doc_url, request_id, errors? } }.
- type ist die grobe Klasse, nach der Sie verzweigen („Kann ich es erneut versuchen?“), und code ist der stabile Grund, den Sie in Ihr eigenes System abbilden.
- param ist der Punktpfad des fehlerhaften Felds, zum Beispiel lines.0.vat_rate.
- errors[] erscheint, wenn eine einzelne Anfrage Probleme in mehr als einem Feld hat.
- request_id wird bei uns protokolliert. Geben Sie sie in jeder Anfrage an den Kundendienst an – sie identifiziert die exakte Anfrage.
- Ein 404 kommt auch für eine id zurück, die zu einem anderen Mandanten gehört: Wir bestätigen nie, dass Daten eines anderen existieren.
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" }
]
}
}| Status | Typ | Code |
|---|---|---|
| 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 |
Rate-Limits
| Bucket | Anfragen / Minute | Bedeutung |
|---|---|---|
| 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 |
- Die Limits gelten pro Zugangskennung und Minute: 120 für Lesezugriffe, 30 für Schreibzugriffe, 10 für die E-Rechnungs-Operationen (submit, refresh, inbox/sync).
- X-RateLimit-Limit, X-RateLimit-Remaining und X-RateLimit-Reset stehen in JEDER Antwort, auch im 429.
- Ein 429 enthält zusätzlich Retry-After in Sekunden. Halten Sie sich daran; wiederholen Sie nicht in einer engen Schleife.
- Das Kontingent von 10/min gibt es, weil ANAF in Rumänien selbst ungefähr einen Aufruf der Nachrichtenliste pro Minute und CIF zulässt.
- Legen Sie /efactura/inbox/sync auf einen Cronjob, nicht auf aggressives Polling.
- Sie brauchen höhere Obergrenzen? Fragen Sie über Kontakt an und nennen Sie Ihr erwartetes Tagesvolumen.
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 beachten; ein 4xx außer 429 nie wiederholen.
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;
}
// Ein Schreib-Retry MUSS denselben Idempotency-Key wiederverwenden, sonst stellen Sie
// ein zweites Fiskaldokument aus.Leitfäden nach Thema
Jedes Thema hat eine eigene Seite mit den passenden Endpunkten, Beispielen und Fehlern.
- Authentifizierung bei der 4invoices-APIclient_id und client_secret gegen ein einstündiges Bearer-Token tauschen — nur Server zu Server.
- Rechnungen über die API ausstellenPOST /invoices mit Idempotency-Key, MwSt. in Prozent, dazu Liste, Storno und Zahlungsvermerk.
- Kunden und ANAF-Registerabfrage (Rumänien)Ein auf tax_id dedupliziertes Anlegen oder Aktualisieren plus ANAF-Registerabfrage für rumänische Unternehmen, damit die e-Factura nicht abgewiesen wird.
- ANAF e-Factura (Rumänien) über die APIFür Rumänien: CIUS-RO-Vorprüfung, SPV-Einreichung, normalisierter Status, signierte Antwort und Eingangs-Posteingang.
- Nummernkreise in der APIOhne aktive Serie vom Typ fiscale liefert die Ausstellung 409 no_series_configured.
- Fehlercodes und der FehlerumschlagEine Form auf jeder Route: type, code, message, param, doc_url, request_id und errors.
- Rate-Limits und Wiederholungen120 Lese-, 30 Schreib- und 10 E-Rechnungs-Operationen pro Minute und Zugangskennung, mit X-RateLimit-*-Kopfzeilen.
Häufige Fehler
- Die USt als Bruch (0.19) oder als 1 senden. Beides wird abgelehnt; senden Sie 19 oder 21.
- Pro Request ein Token erzeugen. Das Token gilt eine Stunde; cachen Sie es, sonst stoßen Sie an das Limit pro IP des Token-Endpunkts.
- Bei jedem Retry einen neuen Idempotenzschlüssel erzeugen – genau das Szenario, das doppelte Rechnungen hervorbringt.
- Aufrufe aus Browser-JavaScript. CORS blockiert jeden Origin, und das Secret wäre ohnehin offengelegt.
- Im nationalen E-Rechnungssystem einreichen ohne Preflight (in Rumänien: CIUS-RO). Eine fehlerhafte Einreichung lässt sich nicht zurückziehen.
- /efactura/submissions/{id}/refresh mehr als 10-mal pro Minute abfragen – dieses Kontingent wird mit submit geteilt.
- Ausstellen, bevor das Firmenprofil vollständig ist und eine aktive fiskalische Serie existiert.
Fragen und Antworten für Entwickler
Wie erhalte ich Zugangsdaten für die 4invoices-API?
Im Panel unter Rechnungsstellung → Einstellungen → API-Zugang. Sie erhalten client_id und client_secret; das Secret wird einmalig angezeigt und nur als Hash gespeichert.
Wie lange ist das access_token gültig?
3600 Sekunden, also eine Stunde. Zwischenspeichern und vor Ablauf erneuern — der Token-Endpunkt hat ein eigenes IP-Limit.
Wie übergebe ich den MwSt.-Satz?
Als Prozentwert: 21 für 21 %. Werte in (0, 1] werden abgewiesen, 0.19 und 1 schlagen also fehl.
Welche Aufrufe brauchen einen Idempotency-Key?
POST /invoices, POST /invoices/{id}/storno und POST /invoices/{id}/efactura/submit. Schlüssel gelten 24 Stunden; eine Wiederholung liefert das Originaldokument mit Idempotency-Replayed: true.
Kann ich eine Rechnung direkt über die API an ANAF e-Factura (Rumänien) senden?
Ja, für den rumänischen Markt: CIUS-RO-Vorprüfung mit /efactura/validate, Einreichung mit /efactura/submit, normalisierter Status über /efactura/submissions/{id} und Download der signierten ANAF-Antwort.
Wie hoch sind die Rate-Limits?
Pro Zugangskennung und Minute: 120 Lesevorgänge, 30 Schreibvorgänge und 10 E-Rechnungs-Operationen. Jede Antwort trägt X-RateLimit-*, ein 429 zusätzlich Retry-After.
Darf ich die API aus dem Frontend aufrufen?
Nein. Sie ist ausschließlich Server-zu-Server: kein Browser-Origin passiert CORS, und ein client_secret gehört nicht in Code, der den Besucher erreicht.
Warum erhalte ich issuer_profile_incomplete bei der ersten Rechnung?
Das Firmenprofil ist unvollständig oder es fehlt eine aktive Serie vom Typ fiscale. Beides im Panel ergänzen; die API erzeugt kein ungültiges Fiskaldokument.