Public REST API v1
API-Dokumentation
Read-only mandantengebundene REST-API für e-Rechnung-Daten. Stabile URL, kursorbasierte Paginierung, signierte Webhooks.
Quickstart
Erzeugen Sie einen API-Schlüssel in den Workspace-Einstellungen und rufen Sie danach jeden Endpunkt mit Authorization-Header auf.
# 1. Issue an API key in workspace settings → API keys.
# 2. Export it locally so curl can read it:
export ERI_API_KEY='eri_live_…'
# 3. List the 10 most recent invoices:
curl -s 'https://e-rechnung-inbox.de/api/public/v1/invoices?limit=10' \
-H "Authorization: Bearer $ERI_API_KEY"
// Node 18+ (built-in fetch). No SDK required.
const res = await fetch(
'https://e-rechnung-inbox.de/api/public/v1/invoices?limit=10',
{ headers: { Authorization: `Bearer ${process.env.ERI_API_KEY}` } },
);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const { data, next_cursor } = await res.json();
console.log(data.length, 'invoices,', next_cursor ? 'more available' : 'end of list');
Alle Endpunkte sind read-only und mandantengebunden. Das Beispiel nutzt /invoices; dasselbe Muster funktioniert für /invoices/{id} und /suppliers.
Authentifizierung
Jede Anfrage an /api/public/v1/* benötigt einen API-Schlüssel — entweder im Authorization-Bearer-Header oder im X-API-Key-Header. Beide Varianten sind gleichwertig.
Authorization: Bearer eri_live_…X-API-Key: eri_live_…
Schlüssel werden in den Workspace-Einstellungen unter "API-Schlüssel" erzeugt. Sie sind mandantengebunden — ein Steuerberater mit Zugriff auf mehrere Mandanten erzeugt pro Mandant einen Schlüssel.
Klartext-Schlüssel werden ausschließlich einmalig bei der Erstellung angezeigt. Bewahren Sie sie in einem Secrets-Store auf.
Endpunkte
Vollständige OpenAPI-3.1-Spezifikation: /api/public/v1/openapi.json.
/api/public/v1/invoicesinvoicesList invoices
Returns mandant-scoped invoices in receipt order (newest first). Cursor-paginated.
| Param | In | Type | Notes |
|---|---|---|---|
| cursor | query | string | Opaque cursor from a previous response's `next_cursor`. |
| limit | query | integer | |
| status | query | valid | invalid | pdf_only | |
| workflow_status | query | new | reviewed | approved | paused | rejected | in_progress | exported | paid | |
| supplier_id | query | string<uuid> | |
| from | query | string<date-time> | |
| to | query | string<date-time> | |
| has_iban_change | query | boolean | Filter to invoices with a flagged IBAN change. |
200 — OK
/api/public/v1/invoicesinvoicesUpload an invoice
Accepts a PDF/XML invoice file for asynchronous processing. Requires an API key with write scope, an `Idempotency-Key` header, and that public API write endpoints are enabled for the workspace/environment. CSV+ZIP export remains the supported fallback for downstream accounting.
| Param | In | Type | Notes |
|---|---|---|---|
| Idempotency-Key* | header | string |
200 — OK
/api/public/v1/invoices/{id}invoicesGet a single invoice
| Param | In | Type | Notes |
|---|---|---|---|
| id* | path | string<uuid> | |
| include | query | string | Comma-separated extras. `pdf_url` adds a 5-minute signed PDF URL. |
200 — OK
/api/public/v1/invoices/{id}invoicesUpdate invoice workflow status
Narrow write endpoint for workflow_status changes only. Requires an API key with `write` scope and an `Idempotency-Key` header; all writes are mandant-scoped and audit-stamped.
| Param | In | Type | Notes |
|---|---|---|---|
| id* | path | string<uuid> | |
| Idempotency-Key* | header | string |
200 — Updated invoice.
/api/public/v1/supplierssuppliersList suppliers
Returns mandant-scoped suppliers in name-ascending order. Cursor-paginated.
| Param | In | Type | Notes |
|---|---|---|---|
| cursor | query | string | |
| limit | query | integer | |
| has_open_invoices | query | boolean | |
| vat_id | query | string |
200 — OK
Code-Beispiele
Minimale Aufrufe zum Listen von Rechnungen:
import os, requests
res = requests.get(
"https://e-rechnung-inbox.de/api/public/v1/invoices",
params={"limit": 10},
headers={"Authorization": f"Bearer {os.environ['ERI_API_KEY']}"},
timeout=10,
)
res.raise_for_status()
print(res.json()["data"])
Hosted MCP
Use the Public API key with Claude Desktop, Cursor, or Smithery via the hosted MCP endpoint. How to connect.
Webhooks
Erstellen Sie eine Subscription über die Workspace-Einstellungen. Wir senden eine signierte JSON-Payload per HTTPS POST. Unterstützte Event-Typen:
invoice.createdinvoice.validatedinvoice.flaggedinvoice.workflow_status_changedinvoice.exportedsupplier.createdsupplier.iban_changed
Payload-Beispiele
Jedes Event nutzt denselben Envelope: id, type, created_at und data. Die Felder in data hängen vom Event ab; sensible Bankdaten werden nur minimiert weitergegeben.
{
"id": "evt_01HV0CK7M5W9XGZN2Y9EGZ7T9R",
"type": "invoice.created",
"created_at": "2026-05-08T11:42:18.317Z",
"data": {
"invoice_id": "f3a3c5e0-3d3a-4f6e-9e9c-2d2dabec3d31",
"invoice_number": "RE-2026-0142",
"supplier": { "id": "8a2…", "name": "ACME GmbH" },
"gross_total": 1428.0,
"currency": "EUR"
}
}{
"id": "evt_01HV0CK7M5W9XGZN2Y9EGZ7T9S",
"type": "supplier.iban_changed",
"created_at": "2026-05-08T11:42:18.317Z",
"data": {
"supplier_id": "8a2…",
"supplier_name": "ACME GmbH",
"previous_iban_last4": "0145",
"new_iban_last4": "9921",
"first_seen_in_invoice_id": "f3a3c5e0-3d3a-4f6e-9e9c-2d2dabec3d31"
}
}Signatur prüfen
Verifizierung der Signatur (Header X-ERI-Signature im Format t=UNIX_SECONDS,v1=HMAC_HEX):
import crypto from 'node:crypto';
export function verifyEriWebhook(payload, signatureHeader, signingSecret) {
// signatureHeader format: "t=<unix>,v1=<hex>"
const parts = Object.fromEntries(
signatureHeader.split(',').map((p) => p.split('=')),
);
const t = Number(parts.t);
if (!Number.isFinite(t) || Math.abs(Date.now() / 1000 - t) > 300) return false;
const expected = crypto.createHmac('sha256', signingSecret)
.update(`${t}.${payload}`)
.digest('hex');
const actual = parts.v1 ?? '';
return actual.length === expected.length && crypto.timingSafeEqual(
Buffer.from(expected, 'utf8'),
Buffer.from(actual, 'utf8'),
);
}
import hashlib, hmac, time
def verify_eri_webhook(payload: bytes, header: str, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
t = int(parts.get("t", "0"))
if abs(int(time.time()) - t) > 300:
return False
signed = f"{t}.{payload.decode('utf-8')}"
expected = hmac.new(secret.encode("utf-8"), signed.encode("utf-8"),
hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts.get("v1", ""))
Retries und Auto-Disable
Bei Fehlschlägen wird der Backoff 1 min, 5 min, 30 min, 2 h, 12 h verwendet — maximal 5 Versuche. Nach 5 Fehlern in Folge wird die Subscription automatisch deaktiviert.
- Fehlgeschlagene Zustellungen werden mit exponentiellem Backoff erneut versucht: 1 Minute, 5 Minuten, 30 Minuten, 2 Stunden, 12 Stunden.
- Nach 5 aufeinanderfolgenden fehlgeschlagenen Zustellungen wird die Subscription automatisch deaktiviert.
- Nach Auto-Disable: Endpoint reparieren, Subscription neu anlegen und das neue Signing-Secret sicher speichern.
Rate-Limits (tier-bezogen)
Pro API-Schlüssel werden zwei Buckets geprüft (Minute + Stunde). Limits hängen vom Tarif ab:
| Plan | Anfragen / Minute | Anfragen / Stunde | Webhook-Subscription-Limit |
|---|---|---|---|
| Starter | 30 | 500 | 1 |
| Standard | 60 | 1000 | 3 |
| Premium | 120 | 3000 | 10 |
| Platinum | 300 | 10000 | 25 |
| Kanzlei | 300 | 10000 | 25 |
Bei Ausfall der Rate-Limit-Backends antwortet die API mit HTTP 503 + Retry-After (Fail-Closed). Tier-Lookup-Fehler fällt auf Starter-Limit zurück.
Erfolgreiche Antworten enthalten X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset (Unix-Timestamp) und X-RateLimit-Tier (aktiver Plan).
Fehler
Fehlerantworten sind JSON und enthalten einen stabilen Code plus eine menschenlesbare Nachricht.
| HTTP | Code | Beschreibung |
|---|---|---|
| 400 | INVALID_INPUT | Parameter oder JSON-Body sind ungültig. |
| 401 | AUTH_UNAUTHORIZED | API-Key fehlt, ist ungültig oder wurde widerrufen. |
| 403 | AUTH_FORBIDDEN | Der API-Key hat nicht die nötigen Rechte für diese Ressource. |
| 404 | NOT_FOUND | Die Ressource existiert nicht im aktuellen Mandanten oder ist nicht sichtbar. |
| 429 | rate_limit_exceeded | Planabhängiges Rate-Limit überschritten. Nach kurzer Wartezeit erneut versuchen. |
| 503 | rate_limit_backend_unavailable | Rate-Limit-Backend nicht verfügbar; API schlägt fail-closed fehl. |
Typisches Format: { "error": "AUTH_UNAUTHORIZED", "message": "...", "request_id": "..." }.
Changelog
Öffentliche API-Änderungen werden hier versioniert dokumentiert.
2026-05-08
v1: Read-only Endpunkte für Rechnungen und Lieferanten, mandantenbezogene API-Keys und signierte Webhooks.
Nicht in v1 enthalten
Folgende Funktionen sind absichtlich noch nicht Teil der v1:
- Schreib-Endpunkte (POST/PATCH/DELETE)
- Steuerberater-Cross-Mandant-Zugriff (kommt in v2 nach echter Partner-Nachfrage)
- Metered Billing (kommt mit dem hosted MCP Commercial Tier)
- Separates Sandbox-Environment (eri_test_-Präfix ist reserviert)