Zum Hauptinhalt springen

Webhooks

Das conomoPORTAL kann jede abgeschlossene Bestellung automatisch an ein eigenes System melden — zum Beispiel an eine Warenwirtschaft, ein Kassensystem oder eine Auswertung. Dazu ruft conomo eine von Ihnen hinterlegte Adresse auf und übergibt die Bestelldaten als JSON.

Wer sieht das?

Eingetragen wird der Webhook in der Kundenverwaltung — einem Bereich, den keine der vier Kundenrollen aus Allgemeines — Rollen im conomoPORTAL sieht. Die Einrichtung nimmt deshalb jemand mit einer internen Rolle vor; wenden Sie sich dazu an Ihren Ansprechpartner. Alles übrige auf dieser Seite — Nutzdaten, Kopfzeilen, Signatur und Felder — betrifft Ihr eigenes System und lässt sich unabhängig davon vorbereiten.

Einrichtung im Portal​

Die Einstellung sitzt im Portal unter Kundenverwaltung → Kunde öffnen → Reiter „Einstellungen“ → Bestell-Webhook.

FeldBedeutung
URLAdresse, die conomo aufruft. Muss mit https:// beginnen. Maximal 2048 Zeichen.
SecretGemeinsames Geheimnis, mit dem jede Anfrage signiert wird. Maximal 255 Zeichen.
Zu beachten

Es wird nur gesendet, wenn beide Felder gefüllt sind. Eine Adresse ohne https:// und ein fehlendes Secret führen dazu, dass die Bestellung stillschweigend nicht gemeldet wird.

Ein gespeichertes Secret wird aus Sicherheitsgründen nie wieder angezeigt. Lassen Sie das Feld leer, bleibt das gespeicherte Secret unverändert; ein neuer Wert ersetzt es. Über Secret entfernen löschen Sie es — der Versand ist damit abgeschaltet, die URL bleibt erhalten.

Leeren Sie das Feld URL, wird der Webhook ebenfalls abgeschaltet.

Wann wird gesendet?​

AuslöserInhalt
Bestellung an einem Automaten abgeschlossenEin Aufruf mit genau einem Eintrag in orders; hubOrder ist null.
Bestellung über einen Hub abgeschlossenEin Aufruf mit hubOrder und allen dazugehörigen Teilbestellungen in orders.

Hub ist dabei der Name, den die Nutzdaten für den SB-Kiosk führen: Felder wie hubOrder und hub meinen genau das Gerät, das dieses Handbuch als SB-Kiosk beschreibt. Eine Bestellung über einen Hub ist damit dieselbe Sammelbestellung, die die Liste unter Verkäufe unter der Kiosk-Bestellnummer führt.

Der Versand erfolgt, nachdem die Bestellung gespeichert wurde, und läuft im Hintergrund — der Bezahl- und Ausgabevorgang wird dadurch nicht verzögert.

Aufbau der Anfrage​

conomo sendet ein POST mit Content-Type: application/json (UTF-8) und diesen zusätzlichen Kopfzeilen:

KopfzeileInhalt
X-Conomo-TimestampZeitpunkt des Ereignisses, ISO 8601 in UTC — identisch mit occurredAt im Body.
X-Conomo-Signaturesha256= gefolgt von der Signatur als Hexadezimalwert in Kleinbuchstaben.

Für Verbindungsaufbau und Antwort gilt jeweils ein Zeitlimit von 10 Sekunden. Antworten Sie deshalb sofort mit 200 und verarbeiten Sie die Daten anschließend.

Zu beachten

Es gibt keine automatische Wiederholung. Ist Ihr System nicht erreichbar oder antwortet es mit einem Fehler, wird das nur im conomo-Serverlog vermerkt; die Meldung ist verloren.

Signatur prüfen​

Signiert wird der Zeitstempel, ein Punkt und der rohe JSON-Body:

<X-Conomo-Timestamp> + "." + <Body wie empfangen>

Darüber wird ein HMAC mit SHA-256 und Ihrem Secret gebildet und als Hexadezimalwert in Kleinbuchstaben übertragen.

const crypto = require("crypto");

function istGueltig(rawBody, timestamp, signatureHeader, secret) {
const erwartet = crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${rawBody}`)
.digest("hex");

const empfangen = signatureHeader.replace(/^sha256=/, "");

return crypto.timingSafeEqual(Buffer.from(erwartet), Buffer.from(empfangen));
}
Hinweis

Verwenden Sie den Body genau so, wie er empfangen wurde. Wird er zuerst geparst und neu serialisiert, ändert sich das Ergebnis und die Prüfung schlägt fehl.

Doppelte Meldungen erkennen​

eventId wird aus der Bestellnummer abgeleitet und ist für dieselbe Bestellung immer gleich. Speichern Sie den Wert und verwerfen Sie eine bereits verarbeitete eventId.

Beispiel: Bestellung an einem Automaten​

{
"version": "1",
"eventId": "0b2f2b7c-8f1d-3c4e-9a52-7b1f0c6d5e21",
"occurredAt": "2026-08-30T09:15:23.412Z",
"customer": {
"name": "Muster GmbH",
"customerNumber": "10042"
},
"hubOrder": null,
"orders": [
{
"orderNumber": "2026-000123",
"totalPrice": 3.15,
"currency": "EUR",
"createdAt": "2026-08-30T09:15:02Z",
"completedAt": "2026-08-30T09:15:23Z",
"machine": {
"name": "Automat Halle 1",
"machineIdentifier": "A1-0007"
},
"items": [
{
"productName": "Mineralwasser 0,5 l",
"articleNumber": "4001234567890",
"selectionNumber": "12",
"state": "COMPLETED",
"unitPrice": 1.40,
"discount": null,
"depositAmount": 25,
"depositType": "REUSABLE",
"type": "SALE"
},
{
"productName": "Schokoriegel",
"articleNumber": "4009876543210",
"selectionNumber": "31",
"state": "COMPLETED",
"unitPrice": 1.50,
"discount": 10.0,
"depositAmount": null,
"depositType": null,
"type": "SALE"
}
]
}
]
}

Beispiel: Bestellung über einen Hub​

Bei einem Hub kommt hubOrder hinzu, und orders enthält jede Teilbestellung mit dem jeweiligen Automaten:

{
"version": "1",
"eventId": "7d41a6b0-2e33-3f18-b0c9-5a2e4d8f1234",
"occurredAt": "2026-08-30T12:02:44.907Z",
"customer": {
"name": "Muster GmbH",
"customerNumber": "10042"
},
"hubOrder": {
"orderNumber": "H-2026-000045",
"totalPrice": 6.30,
"createdAt": "2026-08-30T12:02:10Z",
"completedAt": "2026-08-30T12:02:44Z",
"hub": {
"name": "Kiosk Empfang",
"identifier": "HUB-0003"
}
},
"orders": [
{
"orderNumber": "2026-000501",
"totalPrice": 3.15,
"currency": "EUR",
"createdAt": "2026-08-30T12:02:10Z",
"completedAt": "2026-08-30T12:02:44Z",
"machine": { "name": "Automat links", "machineIdentifier": "A1-0011" },
"items": []
},
{
"orderNumber": "2026-000502",
"totalPrice": 3.15,
"currency": "EUR",
"createdAt": "2026-08-30T12:02:10Z",
"completedAt": "2026-08-30T12:02:44Z",
"machine": { "name": "Automat rechts", "machineIdentifier": "A1-0012" },
"items": []
}
]
}

Felder im Überblick​

Oberste Ebene​

FeldTypBeschreibung
versionTextVersion des Formats, derzeit "1".
eventIdTextKennung der Meldung, aus der Bestellnummer abgeleitet und pro Bestellung stabil.
occurredAtTextZeitpunkt der Meldung, ISO 8601 in UTC.
customerObjektKunde, zu dem die Bestellung gehört.
hubOrderObjekt oder nullNur bei einer Bestellung über einen Hub gefüllt.
ordersListeEine Bestellung je Automat.

customer​

FeldTypBeschreibung
nameTextName des Kunden zum Zeitpunkt der Bestellung.
customerNumberText oder nullKundennummer aus den Stammdaten.

hubOrder​

FeldTypBeschreibung
orderNumberTextBestellnummer der Hub-Bestellung.
totalPriceZahl oder nullGesamtbetrag über alle Teilbestellungen.
createdAtText oder nullBeginn der Bestellung, ISO 8601 in UTC.
completedAtText oder nullAbschluss der Bestellung, ISO 8601 in UTC.
hub.nameText oder nullName des Hubs.
hub.identifierText oder nullKennung des Hubs.

orders[]​

FeldTypBeschreibung
orderNumberTextBestellnummer im Portal, identisch mit der Anzeige unter „Verkäufe“.
totalPriceZahl oder nullGesamtbetrag der Bestellung in der Währung von currency.
currencyTextWährung nach ISO 4217, zum Beispiel EUR.
createdAtText oder nullBeginn der Bestellung, ISO 8601 in UTC.
completedAtText oder nullAbschluss der Bestellung, ISO 8601 in UTC.
machine.nameText oder nullName des Automaten.
machine.machineIdentifierText oder nullKennung des Automaten.
itemsListePositionen der Bestellung.

orders[].items[]​

FeldTypBeschreibung
productNameText oder nullProduktname zum Zeitpunkt der Bestellung.
articleNumberText oder nullArtikelnummer aus den Produktstammdaten.
selectionNumberText oder nullWahlnummer am Automaten.
stateText oder nullStatus der Position, zum Beispiel COMPLETED oder VEND_ERROR.
unitPriceZahl oder nullPreis der Position inklusive Pfand, Rabatt bereits abgezogen.
discountZahl oder nullGewährter Rabatt in Prozent.
depositAmountGanzzahl oder nullPfand in Cent.
depositTypeText oder nullSINGLE_USE (Einweg) oder REUSABLE (Mehrweg).
typeTextSALE für eine verkaufte Position, DEPOSIT_RETURN für Pfand, das vor dem Einkauf an einem Pfandautomaten zurückgegeben wurde — dann ist unitPrice negativ.
Hinweis

Neue Felder können jederzeit hinzukommen. Werten Sie nur die Felder aus, die Sie benötigen, und ignorieren Sie unbekannte — dann bleibt Ihre Anbindung bei Erweiterungen stabil.