Entwickler-Dokumentation · API v1
Registrierkasse an TrustNode anbinden
Diese Anleitung zeigt, wie eine Kassensoftware Belege über TrustNode RKSV-konform signieren lässt. Ablionis ist bereits angebunden. Für jede andere Kasse gelten dieselben Schritte.
Überblick
TrustNode ist eine REST-API. Alle Anfragen und Antworten verwenden JSON und laufen über HTTPS.
Alle Endpunkte beginnen mit dem Präfix /api/v1.
Diese Adresse gilt für alle Mandanten. Ihre Zugangsdaten erhalten Sie von uns. In den Beispielen stehen für Adresse und Schlüssel die
Umgebungsvariablen TRUSTNODE_URL und TRUSTNODE_API_KEY.
Der Ablauf einer Signatur
Die Signatur läuft asynchron. Ihre Kasse übergibt den Beleg und bekommt sofort eine Job-ID zurück. Mit dieser Job-ID fragt sie ab, bis der signierte Code vorliegt. Auch bei vielen gleichzeitigen Belegen bleibt die Verkettung dadurch in der richtigen Reihenfolge.
Zugang & Authentifizierung
Jede Anfrage wird mit einem API-Schlüssel im HTTP-Header X-API-Key authentifiziert. Es gibt zwei Arten von Schlüsseln:
| Schlüssel | Für wen | Berechtigt zu |
|---|---|---|
| Mandanten-Schlüssel | ein Unternehmen, also die Betreiberin oder der Betreiber der Kasse | Belege signieren, Status abfragen, Sonderbelege, DEP-Export – nur für die eigenen Kassen |
| Partner-Schlüssel | Kassenhersteller und Softwarepartner wie Ablionis | Kundschaft und Kassen anlegen, eigene Kundschaft auflisten – siehe Partner-API |
X-API-Key: <Ihr API-Schlüssel>
Content-Type: application/json
Schlüssel werden nur ein einziges Mal angezeigt. TrustNode speichert sie nur als Hash und kann sie später nicht mehr auslesen. Bewahren Sie Schlüssel in einem Secret-Store oder in verschlüsselter Konfiguration auf, niemals im Quellcode, im Browser, in einer App oder in Logdateien.
Die Zugangsdaten erhalten Sie nach Vertragsabschluss von uns. Wenn ein Schlüssel verloren geht oder kompromittiert sein könnte, melden Sie sich sofort. Wir sperren ihn und stellen einen neuen aus.
Inbetriebnahme einer Kasse
Bevor eine Kasse reguläre Belege signiert, sind diese Schritte nötig:
- Unternehmen und Kasse anlegen: Das übernimmt entweder TrustNode oder Ihr Kassenhersteller über die Partner-API. Jede Kasse hat eine eindeutige Kassen-ID, zum Beispiel
AT1-CAFE-001. - Signaturzertifikat hinterlegen: TrustNode verknüpft das A-Trust-Signaturzertifikat des Unternehmens mit dem Zugang.
- Startbeleg erstellen: TrustNode erstellt den Startbeleg der Kasse, einen signierten Nullbeleg.
- Registrierung in FinanzOnline: Die Betreiberin oder der Betreiber bzw. die Steuerberatung registriert die Sicherheitseinrichtung und die Kasse. Dafür werden Kassen-ID, Zertifikatsseriennummer, AES-Schlüssel und AES-Prüfwert benötigt. TrustNode stellt diese Daten bereit.
- Startbeleg prüfen mit der BMF-Belegcheck-App.
Es gibt zwei Kennungen. Beim Signieren wird die Kasse über die Kassen-ID (kassen_id, Text) angesprochen. Für Sonderbelege und DEP-Export wird die technische Kassen-UUID (cash_register_id) verwendet, die Sie beim Anlegen erhalten.
Beleg signieren
Für jeden Barumsatz übergibt die Kasse einen Beleg. TrustNode prüft die Daten und nimmt den Beleg zur Signatur an.
Felder der Anfrage
| Feld | Typ | Beschreibung | |
|---|---|---|---|
kassen_id | string | Pflicht | Kassen-ID einer Kasse Ihres Unternehmens |
belegnummer | string | Pflicht | Fortlaufende Belegnummer, je Kasse eindeutig, max. 50 Zeichen |
betrag_normal_cent | integer | 0 | Bruttobetrag zum Normalsteuersatz (20 %) in Cent |
betrag_ermaessigt1_cent | integer | 0 | Bruttobetrag zum ermäßigten Satz 1 (10 %) in Cent |
betrag_ermaessigt2_cent | integer | 0 | Bruttobetrag zum ermäßigten Satz 2 (13 %) in Cent |
betrag_null_cent | integer | 0 | Steuerfreie bzw. nicht steuerbare Beträge (0 %) in Cent |
betrag_besonders_cent | integer | 0 | Bruttobetrag zum besonderen Steuersatz in Cent |
is_training | boolean | false | Trainingsbuchung, verändert den Umsatzzähler nicht |
storno_of_receipt_number | string | null | null | Belegnummer des stornierten Belegs. Pflicht, wenn die Summe negativ ist |
delivery_type | string | null | null | Art der Belegausgabe: "PAPIER" oder "ELEKTRONISCH" |
original_receipt_datetime | string | null | null | Nur bei Nacherfassung: Zeitpunkt des ursprünglich ausgegebenen Ausfallbons, ISO 8601 mit Zeitzone (z. B. 2026-09-19T14:32:07+02:00). Wird gespeichert, der signierte Beleg trägt aber den Zeitpunkt der Nacherfassung. |
original_receipt_number | string | null | null | Nur bei Nacherfassung: Nummer des ursprünglich ausgegebenen Offline-Belegs (max. 50 Zeichen). Muss sich von belegnummer unterscheiden, denn die Nacherfassung erhält eine neue Belegnummer (siehe Nacherfassung). |
{
"kassen_id": "AT1-CAFE-001",
"belegnummer": "2026-004711",
"betrag_normal_cent": 1250,
"betrag_ermaessigt1_cent": 480,
"delivery_type": "PAPIER"
}
Unbekannte Felder werden ignoriert. Ihre Kasse kann also zusätzliche Felder mitsenden, ohne dass die Anfrage abgelehnt wird. Ausgewertet werden nur die oben genannten.
Antwort
Ist der Beleg angenommen, antwortet TrustNode mit 202 Accepted und einer job_id. Die Signatur läuft im Hintergrund.
{
"receipt_id": null,
"status": "PROCESSING_ASYNC",
"job_id": "3f9c2a7e5b1d4c8e9a0f6b2d7c1e4a93"
}
Status & QR-Code abrufen
Mit der job_id fragt die Kasse ab, ob der Beleg signiert ist. Sobald der Status COMPLETED ist, enthält die Antwort den fertigen maschinenlesbaren Code.
| Feld | Typ | Beschreibung |
|---|---|---|
job_id | string | Die abgefragte Job-ID |
status | string | PENDING = in Arbeit, COMPLETED = signiert und gespeichert, FAILED = abgelehnt, nicht signiert. Unbekannte Werte wie PENDING behandeln. |
receipt_id | uuid | null | Kennung des gespeicherten Belegs (bei COMPLETED) |
qr_code_string | string | null | Maschinenlesbarer Code nach RKSV. Diesen Text 1:1 als QR-Code auf den Beleg drucken |
is_offline_receipt | boolean | null | true, wenn der Beleg bei Signaturausfall ausgestellt wurde (siehe Signaturausfall) |
error_code | string | null | Nur bei FAILED: Grund der Ablehnung (siehe unten) |
{
"job_id": "3f9c2a7e5b1d4c8e9a0f6b2d7c1e4a93",
"status": "COMPLETED",
"receipt_id": "b7e41c0a-5d2f-4e8b-9a61-2c3d4e5f6a7b",
"qr_code_string": "_R1-AT1_AT1-CAFE-001_2026-004711_2026-09-18T14:32:07_12,50_4,80_0,00_0,00_0,00_…",
"is_offline_receipt": false
}
Aufbau des maschinenlesbaren Codes
Der Code folgt dem RKSV-Format und wird von TrustNode vollständig erzeugt. Die Kasse muss ihn nicht zerlegen, nur drucken:
_R1-AT1_{Kassen-ID}_{Belegnummer}_{Datum-Uhrzeit}_{Normal}_{Ermäßigt1}_{Ermäßigt2}_{Null}_{Besonders}_{Umsatzzähler-verschlüsselt}_{Zertifikat-Seriennummer}_{Verkettungswert}_{Signatur}
Wenn ein Beleg abgelehnt wird
Lehnt TrustNode einen Beleg fachlich ab, lautet der Status FAILED und das Feld error_code nennt den Grund. Es wurde dann nichts signiert, und Umsatzzähler und Kette sind unverändert.
error_code | Bedeutung |
|---|---|
DUPLICATE_RECEIPT_NUMBER | Die Belegnummer ist auf dieser Kasse bereits von einem anderen Beleg belegt. |
CASH_REGISTER_INACTIVE | Die Kasse wurde deaktiviert oder mit einem Schlussbeleg außer Betrieb genommen. |
STORNO_OF_TRAINING_RECEIPT | Der Storno verweist auf einen Trainingsbeleg, ist aber selbst nicht als Training gekennzeichnet (siehe Storno & Training). |
INTERNAL_ERROR | Unerwarteter Fehler bei TrustNode. Bitte mit job_id an uns melden. |
Empfohlenes Abfrageverhalten
- Erste Abfrage nach etwa 200 ms, danach mit wachsendem Abstand bis höchstens 2 Sekunden.
- Setzen Sie ein Gesamt-Timeout, zum Beispiel 15 Sekunden, und zeigen Sie der Bedienung danach einen Hinweis.
- Verwerfen Sie die
job_idnach dem Timeout nicht. Fragen Sie im Hintergrund weiter ab: Ein Beleg kann auch später noch fertig signiert werden. - Signierte Belege sind unter ihrer
job_iddauerhaft abrufbar, abgelehnte (FAILED) 8 Tage lang. Ein404kommt nur für Jobs, die es nicht gibt oder die einem anderen Unternehmen gehören.
Beleg sicher erneut senden
Ist die Antwort auf POST /receipts/sign verloren gegangen, etwa durch einen Netzwerkfehler oder Timeout, dürfen Sie den Beleg mit derselben Belegnummer und denselben Daten erneut senden. TrustNode erkennt die Wiederholung und liefert die job_id des ursprünglichen Belegs zurück. Es entsteht kein zweiter Beleg.
- Gleiche Belegnummer, gleiche Daten:
202mit der ursprünglichenjob_id. - Gleiche Belegnummer, andere Beträge oder Kennzeichen:
409 Conflict. - War der vorherige Versuch abgelehnt (
FAILED), wird der neue Versuch normal verarbeitet.
Nie mit einer neuen Belegnummer wiederholen. Das würde einen zweiten, echten Beleg mit demselben Umsatz erzeugen.
Steuersätze & Beträge
Die Kasse teilt den Umsatz jedes Belegs auf die fünf Betragsfelder der RKSV auf. TrustNode addiert sie zum Umsatz, schreibt den Umsatzzähler fort und formatiert die Beträge für den signierten Code.
| RKSV-Feld | Satz | API-Feld |
|---|---|---|
| Betrag-Satz-Normal | 20 % | betrag_normal_cent |
| Betrag-Satz-Ermaessigt-1 | 10 % | betrag_ermaessigt1_cent |
| Betrag-Satz-Ermaessigt-2 | 13 % | betrag_ermaessigt2_cent |
| Betrag-Satz-Null | 0 % | betrag_null_cent |
| Betrag-Satz-Besonders | besonderer Satz | betrag_besonders_cent |
Alle Beträge sind ganze Zahlen in Cent, brutto. 12,50 € wird als 1250 übertragen. Kommazahlen wie 12.5 oder Zahlen als Text wie "1250" werden mit 422 abgelehnt. So entstehen keine Rundungsfehler.
Storno & Training
Stornobeleg
Ein Storno ist ein eigener Beleg mit eigener Belegnummer und negativen Beträgen. Bei negativer Summe ist storno_of_receipt_number Pflicht und verweist auf den ursprünglichen Beleg.
{
"kassen_id": "AT1-CAFE-001",
"belegnummer": "2026-004712",
"betrag_normal_cent": -1250,
"storno_of_receipt_number": "2026-004711"
}
Trainingsbuchung
Mit "is_training": true wird ein Trainingsbeleg erstellt. Er wird signiert und verkettet, der Umsatzzähler bleibt aber unverändert.
Der Storno eines Trainingsbelegs muss ebenfalls mit "is_training": true gesendet werden. Sonst würde ein echter negativer Umsatz entstehen. TrustNode lehnt einen solchen Storno mit STORNO_OF_TRAINING_RECEIPT ab.
Signaturausfall
Ist die Signaturerstellungseinheit vorübergehend nicht erreichbar, stellt TrustNode den Beleg trotzdem aus. Statt der Signatur enthält der Code dann den Hinweis „Sicherheitseinrichtung ausgefallen“, und is_offline_receipt ist true.
- Drucken Sie den
qr_code_stringwie gewohnt. Die Kasse kann normal weiterarbeiten. - Zeigen Sie der Bedienung einen gut sichtbaren Hinweis, damit ein längerer Ausfall bemerkt wird. Für länger andauernde Ausfälle gelten die gesetzlichen Meldepflichten.
- Ist die Signatur wieder verfügbar, erstellen Sie einen Sammelbeleg. Er schließt an die Kette an und dokumentiert das Ende des Ausfalls. Die Ausfallbelege selbst bleiben unverändert gespeichert.
Nacherfassung
Anders als beim Signaturausfall erreicht die Kasse TrustNode hier gar nicht, zum Beispiel ohne Internet. Die Kasse gibt dann einen eigenen Offline-Beleg aus und erfasst den Vorgang später in TrustNode nach.
- Offline-Belege mit einer eigenen Nummernserie ausgeben (z. B.
OFF-2026-00017), getrennt von den Belegnummern, die an TrustNode gehen. - Vor der Nacherfassung prüfen, ob der ursprüngliche Vorgang TrustNode nicht doch erreicht hat, etwa wenn nur die Antwort verloren ging (siehe unten).
- Nur wenn TrustNode ausdrücklich
NOT_FOUNDmeldet: den Vorgang mit einer neuen Belegnummer senden, dazuoriginal_receipt_numberundoriginal_receipt_datetimedes Offline-Belegs.
Belegnummer nachschlagen
status | Bedeutung | Empfohlene Reaktion |
|---|---|---|
FOUND | Beleg ist signiert und gespeichert. Die Antwort enthält qr_code_string, receipt_date und job_id. | Nicht nacherfassen. Den vorhandenen Beleg verwenden. |
PENDING | Der Beleg ist angekommen und wird noch verarbeitet. | Mit der job_id den Status abfragen. |
FAILED | Ein Versuch unter dieser Nummer wurde abgelehnt. error_code nennt den Grund. | Grund prüfen, erst dann entscheiden. |
NOT_FOUND | Unter dieser Nummer ist bei TrustNode nichts angekommen. | Nacherfassung ist sicher. |
Nur ein 200 mit "status": "NOT_FOUND" bedeutet „nicht vorhanden“. Ein HTTP-404 bedeutet eine unbekannte Kasse. Er kann auch von einem Proxy während einer Wartung kommen. Timeouts und 5xx sind ebenfalls keine Aussage über den Beleg. In diesen Fällen nicht nacherfassen, sondern später erneut prüfen.
{
"kassen_id": "AT1-CAFE-001",
"belegnummer": "2026-004718",
"betrag_normal_cent": 1250,
"original_receipt_number": "OFF-2026-00017",
"original_receipt_datetime": "2026-09-18T14:32:07+02:00"
}
Codebeispiele
Das vollständige Muster: Beleg senden, Status abfragen, QR-Code-Text zurückgeben. API-Adresse und Schlüssel kommen aus Umgebungsvariablen.
# 1. Beleg zur Signatur übergeben
curl -sS -X POST "$TRUSTNODE_URL/api/v1/receipts/sign" \
-H "X-API-Key: $TRUSTNODE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"kassen_id": "AT1-CAFE-001",
"belegnummer": "2026-004711",
"betrag_normal_cent": 1250,
"betrag_ermaessigt1_cent": 480
}'
# 2. Status abfragen (job_id aus der Antwort von Schritt 1)
curl -sS "$TRUSTNODE_URL/api/v1/receipts/jobs/3f9c2a7e5b1d4c8e9a0f6b2d7c1e4a93" \
-H "X-API-Key: $TRUSTNODE_API_KEY"
import os
import time
import requests
BASE = os.environ["TRUSTNODE_URL"] + "/api/v1"
HEADERS = {"X-API-Key": os.environ["TRUSTNODE_API_KEY"]}
def sign_receipt(beleg: dict, timeout_s: float = 15.0) -> dict:
r = requests.post(f"{BASE}/receipts/sign", json=beleg, headers=HEADERS, timeout=10)
r.raise_for_status() # 202 Accepted
job_id = r.json()["job_id"]
deadline = time.monotonic() + timeout_s
delay = 0.2
while time.monotonic() < deadline:
s = requests.get(f"{BASE}/receipts/jobs/{job_id}", headers=HEADERS, timeout=10)
if s.status_code == 404:
raise LookupError(f"Job {job_id} unbekannt oder abgelaufen")
s.raise_for_status()
job = s.json()
if job["status"] == "COMPLETED":
return job # job["qr_code_string"] als QR-Code drucken
if job["status"] == "FAILED":
raise RuntimeError(f"Beleg abgelehnt: {job['error_code']}")
time.sleep(delay)
delay = min(delay * 1.5, 2.0)
raise TimeoutError(f"Job {job_id} nicht rechtzeitig abgeschlossen")
job = sign_receipt({
"kassen_id": "AT1-CAFE-001",
"belegnummer": "2026-004711",
"betrag_normal_cent": 1250, # 12,50 € zu 20 %
"betrag_ermaessigt1_cent": 480, # 4,80 € zu 10 %
})
print(job["qr_code_string"])
const BASE = `${process.env.TRUSTNODE_URL}/api/v1`;
const headers = {
"X-API-Key": process.env.TRUSTNODE_API_KEY,
"Content-Type": "application/json",
};
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
export async function signReceipt(beleg, timeoutMs = 15000) {
const res = await fetch(`${BASE}/receipts/sign`, {
method: "POST",
headers,
body: JSON.stringify(beleg),
});
if (res.status !== 202) {
throw new Error(`Beleg abgelehnt: ${res.status} ${await res.text()}`);
}
const { job_id } = await res.json();
const deadline = Date.now() + timeoutMs;
for (let delay = 200; Date.now() < deadline; delay = Math.min(delay * 1.5, 2000)) {
const s = await fetch(`${BASE}/receipts/jobs/${job_id}`, { headers });
if (s.status === 404) throw new Error(`Job ${job_id} unbekannt oder abgelaufen`);
const job = await s.json();
if (job.status === "COMPLETED") return job; // job.qr_code_string drucken
if (job.status === "FAILED") throw new Error(`Beleg abgelehnt: ${job.error_code}`);
await sleep(delay);
}
throw new Error(`Job ${job_id} nicht rechtzeitig abgeschlossen`);
}
// Nur serverseitig verwenden – der API-Schlüssel gehört nicht in den Browser.
const job = await signReceipt({
kassen_id: "AT1-CAFE-001",
belegnummer: "2026-004711",
betrag_normal_cent: 1250,
betrag_ermaessigt1_cent: 480,
});
console.log(job.qr_code_string);
using System.Net;
using System.Net.Http.Json;
var http = new HttpClient
{
BaseAddress = new Uri(Environment.GetEnvironmentVariable("TRUSTNODE_URL") + "/api/v1/"),
Timeout = TimeSpan.FromSeconds(10),
};
http.DefaultRequestHeaders.Add("X-API-Key", Environment.GetEnvironmentVariable("TRUSTNODE_API_KEY"));
var beleg = new
{
kassen_id = "AT1-CAFE-001",
belegnummer = "2026-004711",
betrag_normal_cent = 1250L, // 12,50 € zu 20 %
betrag_ermaessigt1_cent = 480L, // 4,80 € zu 10 %
};
var res = await http.PostAsJsonAsync("receipts/sign", beleg);
res.EnsureSuccessStatusCode(); // 202 Accepted
var start = await res.Content.ReadFromJsonAsync<JobStart>();
var deadline = DateTime.UtcNow.AddSeconds(15);
var delay = 200;
while (DateTime.UtcNow < deadline)
{
var s = await http.GetAsync($"receipts/jobs/{start!.job_id}");
if (s.StatusCode == HttpStatusCode.NotFound)
throw new InvalidOperationException($"Job {start.job_id} unbekannt oder abgelaufen");
s.EnsureSuccessStatusCode();
var job = await s.Content.ReadFromJsonAsync<JobStatus>();
if (job!.status == "COMPLETED")
{
Console.WriteLine(job.qr_code_string); // als QR-Code drucken
return;
}
if (job.status == "FAILED")
throw new InvalidOperationException($"Beleg abgelehnt: {job.error_code}");
await Task.Delay(delay);
delay = Math.Min(delay * 3 / 2, 2000);
}
throw new TimeoutException($"Job {start.job_id} nicht rechtzeitig abgeschlossen");
record JobStart(string job_id, string status);
record JobStatus(string job_id, string status, Guid? receipt_id, string? qr_code_string, bool? is_offline_receipt, string? error_code);
Sonderbelege
Sonderbelege sind signierte Nullbelege, die die RKSV zu bestimmten Anlässen verlangt. Sie werden über die Kassen-UUID angesprochen und antworten mit 202 Accepted. Den Status fragen Sie wie bei normalen Belegen über GET /api/v1/receipts/jobs/{job_id} ab. Nach COMPLETED enthält die Antwort den qr_code_string des Sonderbelegs.
{
"job_id": "8a1d0c5e7f2b4a6c9e3d1b0a7c5e2f48",
"receipt_type": "SAMMELBELEG"
}
| Endpunkt | Beleg | Wann |
|---|---|---|
POST /api/v1/cash-registers/{cash_register_id}/monthly-receipt | Monatsbeleg | Wird von TrustNode am letzten Tag jedes Monats automatisch erstellt. Manuell nur bei Bedarf. |
POST /api/v1/cash-registers/{cash_register_id}/annual-receipt | Jahresbeleg | Automatisch Ende Dezember. Danach mit der BMF-Belegcheck-App prüfen. |
POST /api/v1/cash-registers/{cash_register_id}/summary-receipt | Sammelbeleg | Nach Ende eines Signaturausfalls. Schließt signiert an die Kette an. |
POST /api/v1/cash-registers/{cash_register_id}/control-receipt | Kontrollbeleg | Auf Verlangen der Finanzbehörde. |
POST /api/v1/cash-registers/{cash_register_id}/closing-receipt | Schlussbeleg | Bei Außerbetriebnahme. Die Kasse ist danach dauerhaft inaktiv. |
Sonderbelege können nur für aktive Kassen erstellt werden, sonst antwortet TrustNode mit 409 Conflict und error_code: CASH_REGISTER_INACTIVE. Führen Sie vor einem Schlussbeleg einen DEP-Export durch und lösen Sie den Schlussbeleg in Ihrer Kasse nur nach einer ausdrücklichen Bestätigung aus.
QR-Codes von Sonderbelegen abrufen
Den Jahresbeleg muss die Betreiberin oder der Betreiber bis 15. Februar mit der BMF-Belegcheck-App prüfen, den Startbeleg nach der Registrierung in FinanzOnline. Die Kasse kann dafür den maschinenlesbaren Code aller Sonderbelege abrufen und als QR-Code anzeigen.
| Parameter | Beschreibung | |
|---|---|---|
kassen_id | Pflicht | Kassen-ID |
receipt_type | alle | STARTBELEG, MONATSBELEG, JAHRESBELEG, SAMMELBELEG, SCHLUSSBELEG oder KONTROLLBELEG |
limit | 20 | Anzahl, 1 bis 100, neueste zuerst |
{
"kassen_id": "AT1-CAFE-001",
"receipts": [
{
"receipt_number": "JAHRESBELEG-2026-12",
"receipt_type": "JAHRESBELEG",
"receipt_date": "2026-12-31T22:50:01Z",
"qr_code_string": "_R1-AT1_AT1-CAFE-001_JAHRESBELEG-2026-12_2026-12-31T23:50:01_0,00_…",
"is_offline_receipt": false
}
]
}
Den qr_code_string unverändert als QR-Code darstellen. Sonderbelege bleiben auch nach dem Deaktivieren oder Stilllegen einer Kasse abrufbar. Normale Belege sind über diesen Endpunkt nicht abrufbar.
DEP-Export
Das Datenerfassungsprotokoll enthält alle Belege einer Kasse im RKSV-Exportformat, gruppiert nach Signaturzertifikat.
{
"job_id": "c2e9b4a17d3f4e0a8b6c5d2e1f0a9b87"
}
Der Export wird im gesicherten Speicher von TrustNode abgelegt. Zusätzlich sichert TrustNode das DEP jedes Quartal automatisch. Für eine Betriebsprüfung stellen wir Ihnen den Export bereit.
Partner-API
Über die Partner-API legen Kassenhersteller und Softwarepartner ihre Kundschaft und deren Kassen direkt aus der eigenen Software an, so wie Ablionis. Alle Aufrufe verwenden den Partner-Schlüssel. Partner sehen ausschließlich die eigene Kundschaft.
Unternehmen und erste Kasse anlegen
Legt in einem Schritt ein neues Unternehmen samt erster Kasse an und liefert den Mandanten-Schlüssel dieses Unternehmens.
| Feld | Typ | Beschreibung | |
|---|---|---|---|
tenant_name | string | Pflicht | Name des Unternehmens |
tenant_slug | string | Pflicht | Eindeutiges Kürzel, z. B. cafe-mustermann |
kassen_id | string | Pflicht | Kassen-ID der ersten Kasse |
{
"tenant_name": "Café Mustermann GmbH",
"tenant_slug": "cafe-mustermann",
"kassen_id": "AT1-CAFE-001"
}
{
"tenant_id": "0e6f1a2b-3c4d-4e5f-8a9b-0c1d2e3f4a5b",
"api_key": "<Mandanten-Schlüssel – wird nur jetzt angezeigt>",
"cash_register_id": "5a4b3c2d-1e0f-4a9b-8c7d-6e5f4a3b2c1d",
"kassen_id": "AT1-CAFE-001"
}
Speichern Sie api_key sofort verschlüsselt und dem Unternehmen zugeordnet ab. Er wird nur in dieser Antwort übertragen und kann nicht erneut abgerufen werden.
Weitere Kasse hinzufügen
{
"kassen_id": "AT1-CAFE-002"
}
| Antwortfeld | Beschreibung |
|---|---|
id | Kassen-UUID (cash_register_id) |
kassen_identifikationsnummer | Kassen-ID |
status | Status der Kasse, z. B. ACTIVE |
tenant_id | Zugehöriges Unternehmen |
aes_256_key_base64 | AES-256-Schlüssel der Kasse für die FinanzOnline-Registrierung. Vertraulich behandeln. |
aes_pruefwert | Prüfwert des AES-Schlüssels für die FinanzOnline-Registrierung |
Eigene Kundschaft auflisten
Liefert alle Unternehmen, die Ihrem Partnerzugang zugeordnet sind, jeweils mit id, name, slug, status und den Angaben zum hinterlegten Signaturzertifikat.
Statuscodes & Fehler
Fehler werden als JSON mit dem Feld detail zurückgegeben. Bei 409 enthält die Antwort zusätzlich ein maschinenlesbares error_code. Werten Sie dieses aus, nicht den Text in detail.
{
"detail": "Kasse 'AT1-CAFE-009' nicht gefunden oder nicht autorisiert"
}
| Code | Bedeutung | Was tun? |
|---|---|---|
200 | OK | – |
201 | Angelegt (Partner-API) | Rückgabewerte speichern, insbesondere api_key. |
202 | Angenommen, Verarbeitung läuft | Status über die job_id abfragen. |
401 | API-Schlüssel fehlt, ist ungültig oder der Zugang ist deaktiviert | Header X-API-Key prüfen. Nicht automatisch wiederholen. |
404 | Kasse, Unternehmen oder Job nicht gefunden, oder kein Zugriff darauf. Diese Antworten haben immer einen JSON-Body mit detail. | Kassen-ID bzw. UUID prüfen. Bei Jobs: abgelaufen oder unbekannt. Ein 404 ohne JSON-Body kommt nicht von TrustNode, sondern z. B. während einer Wartung vom vorgelagerten Proxy. Behandeln Sie ihn wie einen 5xx. |
409 | Belegnummer bereits mit anderen Daten verwendet (error_code: DUPLICATE_RECEIPT_NUMBER), oder Kasse nicht aktiv (error_code: CASH_REGISTER_INACTIVE) | Beleg nicht mit neuer Nummer wiederholen, sondern den Vorgang prüfen. Bei CASH_REGISTER_INACTIVE die Betreiberin oder den Betreiber deutlich warnen: Die Kasse wurde deaktiviert oder stillgelegt. |
422 | Ungültige Daten | Die Antwort nennt Feld und Grund. Häufige Ursachen: Kommazahl statt Cent, fehlendes Pflichtfeld, negative Summe ohne Storno-Verweis. |
5xx | Vorübergehender Serverfehler, z. B. 503 für einige Sekunden während einer Wartung | Mit Pause erneut versuchen, bei Signaturanfragen mit derselben Belegnummer (siehe Beleg sicher erneut senden). |
{
"detail": [
{
"type": "int_type",
"loc": ["body", "betrag_normal_cent"],
"msg": "Input should be a valid integer",
"input": 12.5
}
]
}
Best Practices
- Schlüssel nur serverseitig: Rufen Sie TrustNode aus Ihrem Kassen-Backend oder der Kassenanwendung auf, nie aus einem Browser oder einer öffentlich verteilten App mit eingebettetem Schlüssel.
- Ein Schlüssel pro Unternehmen: Verwenden Sie für jedes Unternehmen dessen eigenen Mandanten-Schlüssel. Belege nie über einen fremden Zugang signieren.
- Belegnummern lückenlos und eindeutig: Vergeben Sie die Belegnummer in der Kasse, bevor Sie den Beleg senden, und speichern Sie sie zusammen mit der
job_id. - Beträge in Cent rechnen: Rechnen Sie auch in Ihrer Kasse durchgehend mit ganzen Cent und wandeln Sie erst für die Anzeige in Euro um.
- QR-Code unverändert drucken: Den
qr_code_stringexakt so übernehmen, ohne Kürzen, Trimmen oder Umkodieren. - Ausfälle sichtbar machen:
is_offline_receiptauswerten und die Bedienung informieren. - Übertragung dauerhaft speichern: Legen Sie jeden Beleg vor dem Senden in einer dauerhaften Warteschlange ab und senden Sie ihn bei Verbindungsproblemen später mit denselben Daten erneut. So geht kein Umsatz verloren (siehe Beleg sicher erneut senden und Nacherfassung).
- Erreichbarkeit prüfen:
GET /api/v1/health/liveantwortet ohne Schlüssel mit200und{"status": "ok"}. Das eignet sich für Monitoring, ersetzt aber nicht die Auswertung der eigentlichen Antworten. - Protokollieren ohne Geheimnisse:
job_id, Belegnummer und Statuscodes loggen, den API-Schlüssel nie.
Go-live-Checkliste
- Zugangsdaten sicher hinterlegtMandanten- bzw. Partner-Schlüssel im Secret-Store, nicht im Code
- Startbeleg erstellt und geprüftKasse und Sicherheitseinrichtung in FinanzOnline registriert, Startbeleg mit der BMF-Belegcheck-App geprüft
- Steuersätze korrekt zugeordnetJeder Artikel landet im richtigen der fünf Betragsfelder
- Storno, Training und Ausfall getestetinkl. Hinweis an die Bedienung bei
is_offline_receipt - QR-Code lesbarGedruckter Code mit der BMF-Belegcheck-App erfolgreich geprüft
- Verbindungsausfall getestetTrustNode nicht erreichbar → Ersatzbeleg der Kasse → Nachschlagen über
by-number→ Nacherfassung mit Verweis - Timeouts & Fehler behandeltWiederholung nur mit derselben Belegnummer,
FAILEDwird ausgewertet,job_idwird protokolliert
Fragen zur Integration oder einen Testzugang? Schreiben Sie an office@jln-digital.at.