ERP Invoice API — Rechnungen per REST-API erstellen
Externe Portale und Shops können über diese API automatisch Rechnungen im ERP erstellen. Jedes System erhält einen eigenen API-Schlüssel.
Inhalt
1. API-Schlüssel erstellen
ERP öffnen
Navigieren Sie zu ERP → Seitenleiste → System → API-Schlüssel.
Neuen Schlüssel anlegen
Klicken Sie auf „Neuer API-Schlüssel" und vergeben Sie einen Namen (z. B. „Webshop Hauptseite").
Schlüssel kopieren
Der Schlüssel wird nur einmal angezeigt. Kopieren Sie ihn sofort und speichern Sie ihn sicher ab.
Wichtig: Der API-Schlüssel kann nach dem Erstellen nicht mehr angezeigt werden. Bei Verlust muss ein neuer Schlüssel generiert werden.
Optional können Sie beim Erstellen konfigurieren:
- Rate-Limit — Maximale Requests pro Minute (Standard: 60)
- IP-Whitelist — Nur bestimmte IP-Adressen zulassen
- Callback-URL — Webhook für Statusänderungen (geplant)
2. Endpoint & Authentifizierung
HTTP-Header
| Header | Wert |
|---|---|
Authorization |
Bearer eak_IHR_API_KEY |
Content-Type |
application/json |
Accept |
application/json |
3. Request-Body — Alle Felder
Kontakt (Pflicht — eine der beiden Varianten)
Variante A: Bestehenden Kontakt per ID referenzieren:
{
"erp_contact_id": 15
}
Variante B: Kontaktdaten inline senden:
{
"contact": {
"email": "kunde@example.de",
"company_name": "Musterfirma GmbH",
"first_name": "Max",
"last_name": "Mustermann",
"street": "Musterstraße 1",
"zip": "12345",
"city": "Berlin",
"country": "DE",
"ust_id_nr": "DE123456789",
"phone": "+49 123 456789",
"payment_terms_days": 14
}
}
Kontakt-Auflösung: Bei Variante B wird zuerst nach einem bestehenden Kontakt mit gleicher E-Mail gesucht. Wird einer gefunden, werden die Adressdaten aktualisiert. Wird keiner gefunden, wird ein neuer Kontakt angelegt.
Nur contact.email ist ein Pflichtfeld bei Variante B. Alle anderen Felder sind optional.
Rechnungsdaten (alle optional)
| Feld | Typ | Standard | Beschreibung |
|---|---|---|---|
title |
string | – | Rechnungstitel, z. B. „Bestellung #12345" |
type |
string | invoice |
invoice oder credit_note |
invoice_date |
string | heute | Rechnungsdatum (YYYY-MM-DD) |
due_date |
string | +14 Tage | Fälligkeitsdatum (YYYY-MM-DD) |
service_period_start |
string | – | Leistungszeitraum Beginn (YYYY-MM-DD) |
service_period_end |
string | – | Leistungszeitraum Ende (YYYY-MM-DD) |
discount_percent |
number | 0 | Gesamtrabatt in Prozent (0–100) |
external_reference |
string | – | Externe Bestell-ID — empfohlen für Deduplication |
introduction_text |
string | ERP-Setting | Einleitungstext auf der Rechnung |
conclusion_text |
string | ERP-Setting | Schlusstext auf der Rechnung |
notes |
string | – | Interne Notizen (nicht auf der Rechnung sichtbar) |
Positionen (Pflicht — mindestens 1)
{
"line_items": [
{
"name": "Webhosting Premium",
"unit_price": 29.90,
"quantity": 1,
"unit": "Monat",
"tax_rate": 19.00,
"description": "Inkl. 50 GB SSD, SSL",
"discount_percent": 0,
"position": 1,
"product_sku": "WH-PREM-001"
}
]
}
| Feld | Pflicht | Standard | Beschreibung |
|---|---|---|---|
name |
Ja | – | Positionsname |
unit_price |
Ja | – | Einzelpreis netto in Euro |
quantity |
Nein | 1 | Menge |
unit |
Nein | Stück | Einheit (Stück, Monat, Stunde, Pauschal, ...) |
tax_rate |
Nein | 19.00 | Steuersatz in Prozent |
description |
Nein | – | Zusatztext zur Position |
discount_percent |
Nein | 0 | Positionsrabatt in Prozent |
position |
Nein | auto | Reihenfolge (wird automatisch nummeriert) |
product_sku |
Nein | – | Produkt per SKU aus dem Produktkatalog verknüpfen |
Flags (optional)
| Flag | Standard | Beschreibung |
|---|---|---|
auto_mark_sent |
false |
Rechnung sofort als „Versendet" markieren + in der Buchhaltung buchen |
auto_send_email |
false |
Rechnung per E-Mail an Kontakt versenden (PDF + ZUGFeRD als Anhang) |
Hinweis: auto_send_email benötigt auto_mark_sent: true — eine Rechnung im Entwurf-Status wird nicht per E-Mail versendet.
4. Antwort (Erfolg — HTTP 201)
{
"success": true,
"invoice": {
"id": 42,
"invoice_number": "RE-2026-00042",
"status": "sent",
"type": "invoice",
"title": "Bestellung #12345",
"external_reference": "SHOP-ORDER-12345",
"contact": {
"id": 15,
"customer_number": "KD-2026-015",
"display_name": "Musterfirma GmbH",
"email": "kunde@example.de"
},
"invoice_date": "2026-03-03",
"due_date": "2026-03-17",
"subtotal_net": 29.90,
"tax_amount": 5.68,
"total_gross": 35.58,
"discount_percent": 0,
"line_items": [
{
"position": 1,
"name": "Webhosting Premium",
"quantity": 1,
"unit": "Monat",
"unit_price": 29.90,
"tax_rate": 19.00,
"net_total": 29.90,
"gross_total": 35.58
}
],
"pdf_url": "https://it-dashboard.de/erp/invoices/42/pdf",
"created_at": "2026-03-03T10:30:00+00:00"
},
"warnings": []
}
Das warnings-Array enthält Hinweise, z. B. wenn eine product_sku nicht gefunden wurde. Die Rechnung wird trotzdem erstellt.
5. Fehlercodes
| HTTP | Code | Bedeutung |
|---|---|---|
| 401 | missing_token |
Kein Authorization: Bearer Header gesendet |
| 401 | invalid_format |
Token beginnt nicht mit eak_ |
| 401 | invalid_key |
API-Schlüssel nicht gefunden oder gelöscht |
| 403 | key_disabled |
API-Schlüssel ist deaktiviert |
| 403 | ip_blocked |
IP-Adresse nicht in der Whitelist |
| 409 | duplicate_reference |
external_reference existiert bereits — bestehende Rechnung wird zurückgegeben |
| 422 | validation_error |
Validierungsfehler — Details im details-Objekt |
| 429 | rate_limit |
Rate-Limit überschritten — Retry-After Header beachten |
| 500 | creation_failed |
Interner Fehler bei der Rechnungserstellung |
Beispiel: Validierungsfehler (422)
{
"error": "Validierungsfehler.",
"code": "validation_error",
"details": {
"contact": ["Entweder erp_contact_id oder contact-Objekt ist erforderlich."],
"line_items.0.unit_price": ["Einzelpreis ist ein Pflichtfeld."]
}
}
Beispiel: Duplikat (409)
{
"error": "Rechnung mit dieser external_reference existiert bereits.",
"code": "duplicate_reference",
"invoice": {
"id": 42,
"invoice_number": "RE-2026-00042",
"status": "sent",
"..."
}
}
6. Praxisbeispiele
Minimaler Request — nur Pflichtfelder
curl -X POST https://www.it-dashboard.de/api/erp/invoices \
-H "Authorization: Bearer eak_IHR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contact": {
"email": "info@kunde.de"
},
"line_items": [
{
"name": "Dienstleistung",
"unit_price": 100.00
}
]
}'
Erstellt eine Rechnung im Status Entwurf mit 1 Position, 19 % MwSt. und 14 Tagen Zahlungsziel.
Shop-Bestellung mit automatischem E-Mail-Versand
curl -X POST https://www.it-dashboard.de/api/erp/invoices \
-H "Authorization: Bearer eak_IHR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contact": {
"email": "max@musterfirma.de",
"company_name": "Musterfirma GmbH",
"first_name": "Max",
"last_name": "Mustermann",
"street": "Industriestr. 10",
"zip": "80333",
"city": "München"
},
"title": "Bestellung #WS-2026-0815",
"external_reference": "WS-2026-0815",
"line_items": [
{
"name": "Webhosting Business",
"description": "Inkl. 100 GB SSD, 10 Domains, SSL",
"quantity": 12,
"unit": "Monate",
"unit_price": 14.90
},
{
"name": "Domain-Registrierung example.de",
"quantity": 1,
"unit_price": 9.90
},
{
"name": "Einrichtungspauschale",
"quantity": 1,
"unit": "Pauschal",
"unit_price": 49.00
}
],
"auto_mark_sent": true,
"auto_send_email": true
}'
Rechnung wird erstellt, sofort als Versendet markiert, in der Buchhaltung gebucht und als PDF per E-Mail an den Kontakt geschickt.
PHP-Integration (Laravel / Guzzle)
$response = Http::withToken('eak_IHR_API_KEY')
->post('https://www.it-dashboard.de/api/erp/invoices', [
'contact' => [
'email' => $order->customer_email,
'company_name' => $order->company_name,
'street' => $order->street,
'zip' => $order->zip,
'city' => $order->city,
],
'title' => "Bestellung #{$order->id}",
'external_reference' => "SHOP-{$order->id}",
'line_items' => collect($order->items)->map(fn ($item) => [
'name' => $item->name,
'quantity' => $item->quantity,
'unit_price' => $item->price,
'tax_rate' => 19.00,
])->toArray(),
'auto_mark_sent' => true,
'auto_send_email' => true,
]);
if ($response->status() === 201) {
$invoiceNumber = $response->json('invoice.invoice_number');
// → "RE-2026-00042"
} elseif ($response->status() === 409) {
// Duplikat — Rechnung existiert bereits
$invoiceNumber = $response->json('invoice.invoice_number');
}
Produkte per SKU referenzieren
curl -X POST https://www.it-dashboard.de/api/erp/invoices \
-H "Authorization: Bearer eak_IHR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"erp_contact_id": 15,
"title": "Monatliche Abrechnung März 2026",
"service_period_start": "2026-03-01",
"service_period_end": "2026-03-31",
"line_items": [
{
"product_sku": "WH-PREM-001",
"name": "Webhosting Premium",
"unit_price": 29.90,
"quantity": 1
}
],
"auto_mark_sent": true
}'
Falls die SKU im Produktkatalog existiert, wird die Position mit dem Produkt verknüpft. Falls nicht, kommt eine Warnung im warnings-Array — die Rechnung wird trotzdem erstellt.
7. Deduplication (Doppel-Schutz)
Wenn external_reference gesetzt ist, prüft die API ob bereits eine Rechnung mit der gleichen Referenz und dem gleichen API-Key existiert:
- Neue Referenz → Rechnung wird erstellt (HTTP 201)
- Bestehende Referenz → Keine neue Rechnung, bestehende wird zurückgegeben (HTTP 409)
- Stornierte Rechnung mit gleicher Referenz → Wird ignoriert, neue Rechnung wird erstellt
Empfehlung: Setzen Sie immer external_reference mit der Shop-Bestell-ID, um bei Netzwerkfehlern oder Retries keine Duplikate zu erzeugen.
8. Rate-Limiting
Standard: 60 Requests pro Minute pro API-Key (konfigurierbar im Admin-Bereich).
Bei Überschreitung antwortet die API mit:
HTTP/1.1 429 Too Many Requests
Retry-After: 42
{
"error": "Rate-Limit überschritten.",
"code": "rate_limit",
"retry_after": 42
}
Warten Sie die im Retry-After Header angegebenen Sekunden ab, bevor Sie einen neuen Request senden.