Zum Inhalt springen

JTL-Wawi API Dokumentation

SQL2REST bietet eine Read-Only REST-API Schnittstelle für JTL-Wawi. Hier finden Sie die vollständige Dokumentation zum Anbinden, Einrichten und Nutzen der API-Endpunkte.

Lesender Zugriff auf deine Warenwirtschaft: als REST-Schnittstelle für eigenen Code, oder als Verbindung, über die Claude und ChatGPT direkt Fragen an deine Daten stellen.

Update: Seit SQL2REST v1.4.0 geht es auch einfacher

Statt der Konfigurationsdatei fügst du SQL2REST in Claude als eigenen Konnektor hinzu und meldest dich an. Keine Datei, kein Schlüssel, und es läuft auch im Browser und am Handy.

JTL-Wawi mit Claude verbinden
    Der Suchindex ist deutsch. Endpunkte, Parameter und Werkzeugnamen werden in beiden Sprachen gefunden.

    Erste Schritte

    1. Installation

    1. ZIP-Datei auf Ihren Windows-Server entpacken (z.B. C:\SQL2REST\)
    2. sql2rest.exe doppelklicken. Windows fragt einmal nach Administrator-Rechten, die der letzte Schritt braucht, um den Dienst einzurichten.
    3. Der Assistent führt Sie durch alles: Verbindung testen, Views erstellen, Dienst installieren
    4. Danach liegt im Startmenü ein Eintrag SQL2REST. Von dort kommen Sie jederzeit zurück, ohne den Installationsordner zu öffnen. (StartWizard.bat im Installationsordner tut dasselbe.)ab 1.4.5

    ODBC Driver 17 wird benötigt, ist aber auf den meisten JTL-Wawi-Servern bereits installiert. Der Assistent prüft die Verbindung automatisch.

    Setup

    So funktioniert es (interaktiv)

    Vom SQL Server zur REST API in 5 Schritten, vollautomatisch.

    SQL2REST Setup-Wizard
    DESKTOP-JTL01\JTLWAWI
    sa
    ••••••••••
    Admin-Konto nur für die Einrichtung. Der Assistent erstellt automatisch einen dedizierten Nur-Lese-Benutzer mit minimalen Berechtigungen. Ihre Admin-Zugangsdaten werden nicht gespeichert.
    Reiner Lesezugriff: die API liest ausschließlich, kein Schreibzugriff möglich.

    2. Erster API-Aufruf

    Nach Abschluss des Wizards ist Ihre API sofort einsatzbereit:

    bash
    curl -H "X-API-Key: $KEY" https://deinname.jtl2api.com/customers

    Ersetzen Sie $KEY durch den API-Schlüssel aus dem Setup-Wizard (gespeichert in config.ini).

    Das Beispiel zeigt Ihre öffentliche Tunnel-Adresse. Für Tests auf demselben Rechner funktioniert stattdessen http://localhost:8000, im eigenen Netzwerk der Hostname des Servers mit Port 8000. Alle Varianten stehen unter Basis-URL.

    Basis-URL: lokal, im Netzwerk, von außen

    Die Beispiele auf dieser Seite verwenden https://deinname.jtl2api.com als Platzhalter für den Zugriff von außen. Die Endpunkte und Parameter sind immer identisch, nur die Basis-URL davor ändert sich:

    Gleicher Rechner
    http://localhost:8000

    Für Tests direkt auf dem Server, auf dem SQL2REST läuft.

    Im eigenen Netzwerk
    http://192.168.1.50:8000

    Hostname oder IP des Servers, erreichbar von anderen Rechnern im LAN.

    Von außen (Internet)
    https://dein-tunnel.jtl2api.com

    HTTPS über den SQL2REST-Tunnel, ohne Portangabe und ohne offene Ports. Siehe unten.

    Die API ist also nicht auf localhost beschränkt: derselbe API-Key und dieselben Endpunkte funktionieren lokal, im Netzwerk und über das Internet.

    SQL2REST-Tunnel: HTTPS von außen, ohne Portfreigabe

    Der Tunnel ist der Standardweg für den Zugriff von außen. Ein Klick im Dashboard, und SQL2REST bekommt eine feste HTTPS-Adresse. Es muss kein Port in der Firewall geöffnet und keine Domain oder Zertifikat eingerichtet werden, die Datenbank bleibt unsichtbar im eigenen Netz.

    • Feste HTTPS-URL, verschlüsselt, weiterhin nur Lesezugriff.
    • Keine Portfreigabe, kein Reverse Proxy, keine eigene Domain nötig.
    • Der MCP-Endpunkt liegt unter /mcp derselben Adresse.

    Im Team-Tarif enthalten, für Personal als Add-on buchbar. Tarife ansehen.

    Statt des Tunnels kannst du den externen Zugang auch selbst aufsetzen, mit einem eigenen Reverse Proxy (Cloudflare Tunnel oder Caddy). SQL2REST selbst läuft in beiden Fällen auf deinem Server.

    Claude oder ChatGPT verbinden

    Per Anmeldung (Konnektor)

    Konnektor hinzufügen, anmelden, fertig. Keine Konfigurationsdatei, kein Schlüssel im Client.

    • Läuft im Browser, in der Desktop-App (auch Claude Desktop) und am Handy.
    • Keine lokale Sitzung auf dem Rechner nötig.
    • Die Freigabeseite zeigt, welche Datengruppen der Client bekommt.

    Zwei Voraussetzungen: eine öffentliche Adresse, also Tunnel oder eigener Reverse Proxy, und der Team-Tarif oder höher, weil das Verbinden per Anmeldung über Remote-MCP läuft.

    Für ChatGPT gilt genau dieser Weg ebenso: Konnektor hinzufügen und anmelden. Eine Konfigurationsdatei brauchst du dafür nicht.

    MCP Server (KI)

    SQL2REST kann als MCP-Server für KI-Assistenten genutzt werden. 36 Werkzeuge für Warenwirtschaftsdaten und 2 Resources stehen zur Verfügung.

    Am bequemsten ist das Verbinden per Anmeldung. Die Konfigurationsdatei unten brauchst du nur für Clients, die sich nicht im Browser anmelden.

    Eingeschaltet wird der MCP-Server im API-Dashboard unter Einstellungen, Abschnitt Remote-MCP, mit dem Schalter Remote-MCP aktivieren. Danach nimmt /mcp Anfragen entgegen. Ein Befehl auf der Kommandozeile ist nicht nötig. Für den Zugriff von außen braucht es den Team-Tarif oder höher; im lokalen Netz ist Remote-MCP in jedem Tarif frei.

    Voraussetzung: Die API läuft als Windows-Dienst. Der MCP-Server liest den API-Schlüssel aus der Konfiguration. Für Clients, die ausschließlich über stdio sprechen, gibt es weiterhin sql2rest.exe --mcp auf demselben Rechner.

    Für den Zugriff von außen brauchst du HTTPS. Am einfachsten über den SQL2REST-Tunnel (ein Klick, keine Portfreigabe); der MCP-Endpunkt liegt dann unter /mcp der Tunnel-Adresse. Alternativ ein eigener Reverse Proxy mit Cloudflare Tunnel oder Caddy.

    Claude Desktop Konfiguration (nicht nötig bei Verbindung per Anmeldung)

    json
    {
      "mcpServers": {
        "SQL2REST": {
          "command": "npx",
          "args": [
            "-y", "mcp-remote",
            "https://dein-tunnel.jtl2api.com/mcp",
            "--transport", "http-only",
            "--header", "X-API-Key:${SQL2REST_KEY}"
          ],
          "env": { "SQL2REST_KEY": "dein-api-key" }
        }
      }
    }

    Claude Desktop überträgt native HTTP-MCP-Blöcke nicht zuverlässig, daher läuft die Verbindung über die mcp-remote-Bridge (Node.js 20+ auf der Workstation). http:// für LAN, https:// hinter einem Reverse Proxy. Vollständige Anleitung: JTL-Wawi mit Claude Desktop verbinden.

    Verfügbare Tools

    search_customers get_customer list_orders get_order get_order_items list_returns get_return get_return_items list_invoices get_invoice_pdf search_products get_product get_product_sales get_stock list_stock_by_warehouse list_warehouses list_shipments list_delivery_notes list_purchase_orders get_purchase_order list_suppliers get_supplier list_goods_receipts get_goods_receipt sync_orders sync_customers list_attributes filter_products_by_attribute list_credit_notes get_credit_note list_employees get_employee get_sales_by_employee list_workflow_logs list_workflow_definitions get_workflow_definition

    Die Tools zu Lieferanten, Lieferantenbestellungen und Wareneingang stehen bereit, sobald du den Einkauf im Einrichtungs-Assistenten aktiviert hast.

    Ein Schlüssel zeigt nur die Werkzeuge, die seine Berechtigungen abdecken. Eine Teilauswahl bleibt die Auswahl von damals: Bereiche, die später dazukommen, sind nicht dabei, bis du sie anhakst. Sind dagegen alle Bereiche angehakt, gilt der Schlüssel als nicht eingeschränkt und bekommt neue Bereiche automatisch mit.

    Nach einem Update oder nach neuen Berechtigungen kennt eine bestehende Verbindung die neuen Werkzeuge noch nicht. Über einen Konnektor verbunden: „Tool-Liste aktualisieren" reicht nicht, wenn Bereiche dazugekommen sind, dann in Claude erst Trennen und anschließend Verbinden -- eine Verbindung behält die Bereiche, mit denen sie angelegt wurde. Über die Konfigurationsdatei: den Client vollständig neu starten.

    Kompatibel mit Claude (Browser, Desktop und Handy), Claude Code, ChatGPT, Cursor, VS Code mit Copilot, Copilot Studio, Windsurf und jedem anderen MCP-Client.

    Fester client_id / client_secret

    Die meisten MCP-Clients registrieren sich beim Verbinden selbst (Dynamic Client Registration, RFC 7591) und fragen dich nur nach der Server-URL. Manche Clients können das nicht und erwarten stattdessen eine feste client_id und ein client_secret, die du von Hand einträgst. Für diese gibt es den folgenden Weg.

    Relevant zum Beispiel für Cursor, VS Code / Copilot und Copilot Studio im manuellen Modus. Wenn dein Client nach der Verbindung nur eine URL will und dich danach zur Anmeldung schickt, brauchst du diesen Abschnitt nicht.

    Adressen des Authorization Servers

    SQL2REST betreibt den Authorization Server in der Cloud. Die verbindliche Quelle ist das Metadaten-Dokument; die einzelnen Adressen stehen darin und sind hier nur der Bequemlichkeit halber ausgeschrieben.

    urls
    Metadata  https://sql2rest.com/.well-known/oauth-authorization-server
    Issuer    https://sql2rest.com
    Authorize https://sql2rest.com/oauth/authorize/
    Token     https://sql2rest.com/api/oauth/token/
    Register  https://sql2rest.com/api/oauth/register/
    JWKS      https://sql2rest.com/.well-known/jwks.json

    Die Adressen enden auf einen Schrägstrich. Das ist kein Schönheitsfehler, sondern nötig: ohne ihn antwortet der Server mit einer Weiterleitung, und nicht jeder Client wiederholt dabei seine Anfrage.

    client_id und client_secret anlegen

    Ein einziger Aufruf am Register-Endpunkt legt den Client an. Trage bei redirect_uris genau die Rückleitungs-Adresse ein, die dein Client verwendet. Sie wird später zeichengenau geprüft.

    bash
    curl -X POST https://sql2rest.com/api/oauth/register/ \
      -H "Content-Type: application/json" \
      -d '{
        "client_name": "Cursor auf meinem Notebook",
        "redirect_uris": ["http://localhost:3000/callback"],
        "token_endpoint_auth_method": "client_secret_post"
      }'

    Die Antwort enthält client_id und client_secret:

    json
    {
      "client_id": "c_...",
      "client_secret": "...",
      "client_secret_expires_at": 0,
      "token_endpoint_auth_method": "client_secret_post",
      "redirect_uris": ["http://localhost:3000/callback"]
    }

    Das client_secret wird genau einmal ausgegeben. Wir speichern davon nur einen Hash und können es nicht erneut anzeigen. Verloren heißt neu anlegen. Behandle es wie ein Passwort und lege es nicht in ein Repository.

    Lässt du token_endpoint_auth_method weg, bekommst du einen öffentlichen Client ohne Secret. Das ist der richtige Weg für alles, was PKCE beherrscht, und der Normalfall.

    Im Client eintragen

    1. Authorization- und Token-Adresse aus der Tabelle oben eintragen, dazu client_id und client_secret.
    2. Als Redirect- beziehungsweise Callback-Adresse exakt den Wert eintragen, den du registriert hast. Schon ein zusätzlicher Schrägstrich führt zur Ablehnung.
    3. PKCE mit S256 aktivieren. Der Server akzeptiert nichts anderes.
    4. Als resource die MCP-Adresse deines Systems angeben, also deine öffentliche Adresse mit /mcp am Ende, ohne Schrägstrich dahinter. Damit ist das Token an genau dieses System gebunden.
    5. Verbindung starten. Du landest auf der SQL2REST-Anmeldung, danach auf der Freigabeseite, und siehst dort, welche Datengruppen freigegeben werden.

    Freigegeben wird immer nur die Schnittmenge aus dem, was dein Administrator dir zugewiesen hat, und dem, was der Client anfragt. Ein Client kann sich damit nicht mehr Rechte holen, als du hast.

    Zugang

    Authentifizierung

    Jede API-Anfrage erfordert einen API-Schlüssel als HTTP-Header:

    http
    X-API-Key: your-api-key-here

    Der Header-Name ist standardmäßig X-API-Key und kann in config.ini unter [API] HeaderName geändert werden.

    Hinweis: Query-Parameter-Authentifizierung wird nicht unterstützt. Der API-Schlüssel muss als Header gesendet werden.

    Endpunkt-Referenz

    Alle Daten-Endpunkte erfordern einen gültigen API-Schlüssel und eine aktive Lizenz. Feldnamen hängen von Ihrer Spaltenzuordnung im Setup-Wizard ab.

    Die Beispiele auf dieser Seite zeigen die kanonischen Katalogfelder. Ein Feld, das der Einrichtungs-Assistent auf Ihrer Installation nicht zuordnen konnte, fehlt in der tatsächlichen Antwort, statt als leerer Wert dazustehen.

    Alle Endpunkte unterstützen: sort, order, limit, offset, mandant. Details siehe unten.

    Interne JTL-IDs (JTL_-Präfix)

    Ab v1.1.80 gibt jede Entität interne JTL-IDs zurück, die Sie für Rückschreib-Workflows mit der offiziellen JTL-REST-API benötigen.

    Welche ID an welcher Entität hängt

    JTL_CustomerId (Kunden), JTL_SalesOrderId + JTL_CustomerId (Aufträge), JTL_ItemId + JTL_SalesOrderId + JTL_ProductId (Auftragspositionen), JTL_ProductId (Artikel), JTL_DeliveryNoteId + JTL_SalesOrderId + JTL_CustomerId (Lieferscheine), JTL_ShipmentId + JTL_DeliveryNoteId + JTL_SalesOrderId (Lieferungen).

    Claude

    Jeder Endpunkt hier lässt sich auch im Chat fragen, in Claude wie in ChatGPT. Der Reiter Im Chat zeigt bei jedem, wie die Frage lautet. Verbinden

    Kunden

    GET /customers

    Kunden auflisten mit optionaler Suche, Sortierung und Paginierung.

    ParameterTypBeschreibung
    searchstringLIKE-Suche über Name und Nummer
    customer_numberstringExakter Filter nach Kundennummer
    bash
    curl -H "X-API-Key: $KEY" "https://deinname.jtl2api.com/customers?search=Müller"
    json
    {
      "data": [
        {
          "CustomerNumber": "10001",
          "CompanyName": "Müller GmbH",
          "FirstName": "Thomas",
          "LastName": "Müller",
          "CustomerEmail": "[email protected]",
          "Address": {
            "City": "Frankfurt",
            "Country": "DE"
          }
        }
      ],
      "total": 3,
      "limit": 100,
      "offset": 0
    }

    Kunden abrufen

    GET /customers/{number}

    Einzelnen Kunden nach Kundennummer abrufen.

    bash
    curl -H "X-API-Key: $KEY" https://deinname.jtl2api.com/customers/10001
    json
    {
      "data": {
        "CustomerNumber": "10001",
        "CompanyName": "Müller GmbH",
        "FirstName": "Thomas",
        "LastName": "Müller",
        "CustomerEmail": "[email protected]",
        "CustomerPhone": "069 1234567",
        "Address": {
          "Street": "Musterstraße 5",
          "PostalCode": "60311",
          "City": "Frankfurt",
          "Country": "DE"
        }
      }
    }

    Aufträge

    GET /orders

    Aufträge auflisten mit Filtern nach Kunde, Status und Zeitraum.

    ParameterTypBeschreibung
    customerstringFilter nach Kundennummer
    customer_numberstringExakter Filter nach Kundennummer
    order_numberstringExakter Filter nach Auftragsnummer
    external_order_idstringExakter Filter nach externer Bestell-ID
    statusstringFilter nach Status
    stornointStorno-Flag: 0 = aktive Aufträge, 1 = stornierte (wie JTL nStorno)
    typestringAuftragstyp, z. B. B für normale Aufträge (wie JTL cType). storno=0 und type=B entspricht JTLs "verkauft pro Tag".
    fromdateStartdatum (YYYY-MM-DD)
    todateEnddatum (YYYY-MM-DD)

    Welche Felder deine Antwort trägt, hängt von deiner Einrichtung ab. Der Assistent legt beim Erkennen fest, welche Spalten ausgeliefert werden. Das Beispiel zeigt deshalb eine typische Antwort, nicht die einzig mögliche. Felder, die bei dir fehlen, sind kein Fehler.

    Felder mit dem Präfix CF_ sind deine eigenen Felder aus der Wawi. Wie viele es sind und wie sie heißen, ist bei jeder Anlage anders.

    S2R_CanaryRevOrders22 trägt die Revisionsnummer deiner Datenbanksichten. Für die Auswertung deiner Aufträge kannst du es ignorieren.

    Ein fehlender Wert kommt in zwei Formen zurück, und beide bedeuten dasselbe: null und leerer Text. In den Adressen steht etwa CompanyName als leerer Text da, wo keine Firma hinterlegt ist, Phone dagegen als null.

    Die Liste liefert die Datensätze in data, dazu total, limit und offset für den Seitenlauf. Der Einzelabruf liefert nur data mit einem einzelnen Datensatz, ohne diese drei Angaben. Der Datensatz selbst sieht in beiden Fällen gleich aus.

    bash
    curl -H "X-API-Key: $KEY" "https://deinname.jtl2api.com/orders?from=2024-01-01&status=Versendet"
    json
    {
      "data": [
        {
          "OrderNumber": "40217",
          "CustomerNumber": "10842",
          "OrderDate": "2026-03-11T09:14:02.483000",
          "TotalAmount": 47.95,
          "NetAmount": 40.29,
          "Currency": "EUR",
          "ExternalOrderId": "999-1234567-1234567",
          "PaymentStatus": "Paid",
          "Storno": 0,
          "OrderType": "B",
          "Platform": "Amazon.de",
          "OrderColor": null,
          "OrderStatus": null,
          "OrderHoldReason": null,
          "ShippingCost": null,
          "CreatedByEmployeeId": 1,
          "CreatedByEmployeeName": "Administrator",
          "SalesEmployeeId": 1,
          "SalesEmployeeName": "Administrator",
          "AccountManagerId": null,
          "AccountManagerName": null,
          "ExpectedDeliveryDate": null,
          "OrderAnnotation": "FBA Amazon Order 999-1234567-1234567",
          "OrderHint": null,
          "S2R_CanaryRevOrders22": 22,
          "CF_Lieferhinweis": null,
          "BillingAddress": {
            "Street": "Lindenstrasse 42",
            "PostalCode": "60329",
            "City": "Frankfurt",
            "Country": "Deutschland",
            "CompanyName": "Muster Handels GmbH",
            "FirstName": "Erika",
            "LastName": "Musterfrau",
            "Email": "[email protected]",
            "Phone": "01701234567"
          },
          "ShippingAddress": {
            "Street": "Lindenstrasse 42",
            "PostalCode": "60329",
            "City": "Frankfurt",
            "Country": "Deutschland",
            "CompanyName": "",
            "FirstName": "Erika",
            "LastName": "Musterfrau",
            "Email": "[email protected]",
            "Phone": null
          }
        }
      ],
      "total": 1247,
      "limit": 1,
      "offset": 0
    }

    Auftrag abrufen

    GET /orders/{number}

    Einzelnen Auftrag nach Auftragsnummer abrufen.

    bash
    curl -H "X-API-Key: $KEY" https://deinname.jtl2api.com/orders/86957

    Auftragspositionen

    GET /orders/{number}/items

    Positionen eines Auftrags abrufen.

    bash
    curl -H "X-API-Key: $KEY" https://deinname.jtl2api.com/orders/86957/items

    Retouren

    GET /returns

    Drei Endpunkte für Retouren (RMA): Kopfdaten auflisten, eine einzelne Retoure abrufen und die Positionen einer Retoure abrufen.

    Retouren auflisten mit Filtern nach Kunde, Status, RMA-Nummer und Zeitraum. Standardmäßig nach Retourendatum absteigend sortiert (neueste zuerst).

    ParameterTypBeschreibung
    customerstringFilter nach Kundennummer
    customer_numberstringExakter Filter nach Kundennummer
    statusstringFilter nach Status
    rma_numberstringExakter Filter nach RMA-Nummer
    searchstringLIKE-Suche über die RMA-Nummer
    fromdateStartdatum (YYYY-MM-DD)
    todateEnddatum (YYYY-MM-DD)
    bash
    curl -H "X-API-Key: $KEY" "https://deinname.jtl2api.com/returns?from=2024-01-01&status=Offen"
    json
    {
      "data": [
        {
          "RmaNumber": "Ret-118",
          "ReturnDate": "2026-04-02T11:27:55.120000",
          "Status": "Eingetroffen",
          "CustomerNumber": "10842",
          "ReferenceOrderNumber": "40217",
          "Value": 8.99,
          "ValueGross": 8.99,
          "ValueNet": 7.554622,
          "GoodsValueGross": 8.99,
          "GoodsValueNet": 7.554622,
          "ShippingRefund": 0.0,
          "Warehouse": "Zentrallager",
          "CommentExternal": "",
          "CommentInternal": ""
        }
      ],
      "total": 37,
      "limit": 1,
      "offset": 0
    }

    Retoure abrufen

    GET /returns/{rma_number}

    Einzelne Retoure nach RMA-Nummer abrufen.

    bash
    curl -H "X-API-Key: $KEY" "https://deinname.jtl2api.com/returns/Ret-118"

    Retourenpositionen

    GET /returns/{rma_number}/items

    Positionen einer Retoure abrufen.

    bash
    curl -H "X-API-Key: $KEY" "https://deinname.jtl2api.com/returns/Ret-118/items"

    Felder der /returns-Antwort

    Kopfdaten je Retoure. Feldnamen können je nach Spaltenzuordnung im Setup-Wizard abweichen.

    FeldTypBeschreibung
    RmaNumberstringRMA-Nummer der Retoure
    ReturnDatedateRetourendatum
    StatusstringStatus der Retoure
    CustomerNumberstringKundennummer
    ReferenceOrderNumberstringZugehörige JTL-Auftragsnummer
    ValuenumberGesamtwert der Retoure
    ValueGrossnumberGesamtwert brutto (inkl. Steuer)
    ValueNetnumberGesamtwert netto (ohne Steuer)
    GoodsValueGrossnumberWarenwert brutto
    GoodsValueNetnumberWarenwert netto
    ShippingRefundnumberErstattete Versandkosten
    WarehousestringLager, in das zurückgebucht wird
    CommentExternalstringKommentar für den Kunden, oft leerer Text
    CommentInternalstringInterner Kommentar, oft leerer Text

    Felder der /returns/{rma_number}/items-Antwort

    Eine Zeile je zurückgesendeter Position.

    FeldTypBeschreibung
    SkustringArtikelnummer (SKU)
    ArticleNamestringArtikelname
    QuantitynumberZurückgesendete Menge
    ReturnReasonstringRetourengrund
    ArticleConditionstringZustand des Artikels
    PriceNetnumberPositionspreis netto
    PriceGrossnumberPositionspreis brutto
    DeliveryNoteNumberstringZugehörige Lieferscheinnummer
    ItemStatusstringStatus der Position

    Rechnungen

    GET /invoices

    Rechnungen auflisten mit Filtern nach Kunde, Status und Zeitraum.

    ParameterTypBeschreibung
    customerstringFilter nach Kundennummer
    statusstringFilter nach Status
    from / todateDatumsbereich (YYYY-MM-DD)
    order_numberstringFilter nach Auftragsnummer
    min_dunning_levelab 1.4.7integerNur Rechnungen ab dieser Mahnstufe. 0 liefert die noch nicht angemahnten.

    Mit dem Parameter ?order= können alle Rechnungen zu einem bestimmten Auftrag abgerufen werden:

    Mahnstufen

    Gefiltert wird ab einer Stufe, nicht auf eine genaue. Die Stufen gehören zu einer Mahngruppe, und die Gruppen sind unterschiedlich tief: ein exakter Vergleich ließe ausgerechnet die am härtesten angemahnten Kunden weg. Die Antwort führt DunningLevel und DunningLevelName. Bau die Bezeichnung nie aus der Zahl: die Zahl ist der Bezeichnung einen Schritt voraus, Stufe 3 heißt „2. Mahnung". Nicht angemahnt ist null, nie 0. OpenAmount wird bei Rechnungskorrekturen negativ.

    Die Mahnfelder sind auf den meisten Anlagen leer, weil dort gar nicht gemahnt wird. null heißt also nicht, dass eine Rechnung offen ist.

    OpenAmount kann negativ werden, etwa bei einer Rechnungskorrektur. Wenn du Summen über offene Beträge bildest, filtere vorher auf Werte größer als null, sonst rechnest du dir die Summe klein.

    bash
    curl -H "X-API-Key: $KEY" "https://deinname.jtl2api.com/invoices?min_dunning_level=3"
    json
    {
      "data": [
        {
          "InvoiceNumber": "88214",
          "InvoiceDate": "2026-03-11T09:15:00.221000",
          "InvoiceOrderNumber": "40217",
          "DunningLevel": null,
          "DunningLevelName": null,
          "IsDunned": false,
          "DunningDate": null,
          "DunningFee": 0.0,
          "OpenAmount": 0.0
        }
      ],
      "total": 1193,
      "limit": 1,
      "offset": 0
    }

    Rechnungs-PDFs

    GET /invoices/{number}/pdf

    Rechnungs-PDF nach Rechnungsnummer herunterladen. Standardmäßig deaktiviert; kann im Setup-Wizard oder in config.ini aktiviert werden.

    Die Suche extrahiert den numerischen Teil der Rechnungsnummer (RE-12345 → 12345) und sucht rekursiv in allen konfigurierten Ordnern und Unterordnern. Bei mehreren Treffern wird die neueste Datei verwendet.

    Gibt die PDF-Datei direkt als Download zurück (Content-Type: application/pdf). Bei nicht gefundener Rechnung: 404.

    Konfiguration

    Es reicht, den Hauptordner anzugeben - Unterordner wie Jahres- oder Monatsverzeichnisse werden automatisch durchsucht:

    config.ini
    InvoicePdfPath = \\SERVER\Buchhaltung\Rechnungsausgang
    ; Findet auch: \2026\04\RE-12345.pdf
    bash
    curl -H "X-API-Key: $KEY" https://deinname.jtl2api.com/invoices/86774/pdf --output rechnung.pdf

    Gutschriften ab 1.4.7

    GET /credit-notes

    Gutschriften abrufen wie Rechnungen: Nummer, Datum, Betrag, Status, Kunde, zugehörige Rechnung und Mitarbeiter.

    ParameterTypBeschreibung
    customer_numberstringFilter nach Kundennummer
    invoice_numberstringFilter nach der Rechnungsnummer, zu der die Gutschrift gehört
    employee_idintegerFilter nach der Mitarbeiter-ID, die die Gutschrift ausgestellt hat
    searchstringSuche nach der Gutschriftsnummer
    from / todateDatumsbereich (YYYY-MM-DD)
    mandantintegerMandant (Datenbank), Standard 1
    bash
    curl -H "X-API-Key: $KEY" "https://deinname.jtl2api.com/credit-notes?customer_number=10842&from=2026-03-01"
    json
    {
      "data": [
        {
          "CreditNoteId": 412,
          "CreditNoteNumber": "118",
          "CreditNoteDate": "2026-03-14T16:02:41.755000",
          "Amount": 8.99,
          "Status": null,
          "Cancelled": 0,
          "ExternalOrderId": null,
          "EmployeeId": 1,
          "CustomerNumber": "10842",
          "InvoiceNumber": "88214",
          "EmployeeName": "Administrator",
          "OrderNumber": "40217",
          "OrderCount": 1
        }
      ],
      "total": 7
    }

    Gutschrift abrufen

    GET /credit-notes/{nummer}

    Felder je Gutschrift

    FeldTypBeschreibung
    CreditNoteIdintegerInterne Kennung der Gutschrift
    CreditNoteNumberstringGutschriftsnummer
    CreditNoteDatestringErstellungsdatum
    AmountnumberBetrag der Gutschrift
    StatusstringStatus aus der Wawi
    CancelledintegerStorniert (1) oder nicht (0)
    ExternalOrderIdstringExterne Bestell-ID, oft null
    EmployeeIdintegerKennung des Mitarbeiters, der die Gutschrift ausgestellt hat
    CustomerNumberstringKundennummer
    InvoiceNumberstringNummer der Rechnung, zu der die Gutschrift gehört
    EmployeeNamestringName dieses Mitarbeiters
    OrderNumberstringZugehörige Auftragsnummer, falls ermittelbar
    OrderCountintegerAnzahl der verknüpften Aufträge

    Die Zuordnung zum Auftrag läuft über die Rechnung. Hängen mehrere Aufträge an einer Rechnung, nennt OrderNumber den ersten davon und OrderCount sagt, wie viele es sind, damit eine 1 zu n nicht als 1 zu 1 gelesen wird.

    Du bekommst den Gesamtbetrag je Gutschrift, nicht die einzelnen Artikelzeilen. Welche Artikel in welcher Menge gutgeschrieben wurden, kommt in einer späteren Version.

    bash
    curl -H "X-API-Key: $KEY" https://deinname.jtl2api.com/credit-notes/118

    Artikel

    GET /products

    Artikel auflisten mit optionaler Suche.

    ParameterTypBeschreibung
    searchstringLIKE-Suche über Name und Nummer
    skustringExakter Filter nach Artikelnummer (SKU)
    bash
    curl -H "X-API-Key: $KEY" "https://deinname.jtl2api.com/products?search=Fahrradhelm"
    json
    {
      "data": [
        {
          "SKU": "ART-001",
          "ProductName": "Fahrradhelm",
          "ProductPrice": 39.90,
          "ProductPriceGross": 47.48,
          "ProductVAT": 19,
          "GTIN": "4006381333931"
        }
      ],
      "total": 842,
      "limit": 100,
      "offset": 0
    }

    Artikel abrufen

    GET /products/{sku}

    Einzelnen Artikel nach Artikelnummer (SKU) abrufen.

    Felder der /products-Antwort

    Die folgende Tabelle listet alle Felder, die der /products-Endpunkt zurückgeben kann.

    FeldTypBeschreibung
    SKUstringArtikelnummer (SKU)
    ProductNamestringArtikelname
    ProductDescriptionstringLange Artikelbeschreibung
    ProductPricenumberNettopreis (ohne Steuer)
    ProductPriceGrossnumberBruttopreis (inkl. Steuer)
    ProductVATnumberSteuersatz in Prozent
    GTINstringEAN/GTIN-Barcode
    ManufacturerNamestringHerstellername
    CategorystringPrimäre Kategorie des Artikels
    ProductGroupstringWarengruppe des Artikels
    TaricCode*stringZolltarifnummer (TARIC)
    CountryOfOrigin*stringHerkunftsland
    JTL_ProductIdnumberAdvanced, opt-in: interner JTL-Artikelschlüssel
    CF_…mixedEigene Felder, opt-in im Setup-Wizard aktiviert
    attributesobjectMerkmale; auf /products via ?include=attributes, auf /products/{sku} immer

    * TaricCode und CountryOfOrigin sind optionale Felder und müssen im Setup-Wizard (Feldauswahl) aktiviert werden, damit sie von der API und dem MCP zurückgegeben werden. Standardmäßig sind sie deaktiviert.

    bash
    curl -H "X-API-Key: $KEY" https://deinname.jtl2api.com/products/ART-001
    json
    {
      "data": {
        "SKU": "ART-001",
        "ProductName": "Fahrradhelm",
        "ProductDescription": "Leichter Fahrradhelm mit Ventilationsschlitzen.",
        "ProductPrice": 39.90,
        "ProductPriceGross": 47.48,
        "ProductVAT": 19,
        "GTIN": "4006381333931",
        "ManufacturerName": "Herstellername GmbH",
        "Category": "Fahrradzubehör"
      }
    }

    Merkmale auflisten

    GET /attributes

    SQL2REST erkennt JTL-Merkmale (standardisierte Varianten-Beschreibungen wie Farbe, Größe, Material) und stellt sie über drei Wege zur Verfügung: einen Katalog-Endpunkt, eingebettete Werte auf Artikeln und einen Filter-Parameter.

    Alle Merkmal-Definitionen mit ihren möglichen Werten, gruppiert nach Name.

    ParameterTypBeschreibung
    mandantintegerMandanten-ID (Standard: 1)

    JTL-IDs für Write-Back

    Jede Position führt bereits die internen JTL-IDs mit: id auf dem Attribut (kMerkmal) und id auf jedem Wert (kMerkmalWert). Nützlich für Write-Back über die offizielle JTL-API.

    Attribute ausblenden

    Über den Abschnitt [MERKMALE] in der config.ini können einzelne Attribute aus allen Antworten ausgeblendet werden (Komma-getrennt, Groß-/Kleinschreibung wird ignoriert). Versuche, diese Namen als Filter zu nutzen, liefern HTTP 400.

    config.ini
    [MERKMALE]
    Disabled = InternalCode, LegacyFlag

    Sprache

    Attribut-Übersetzungen werden bei der View-Erstellung fixiert (Standard: Deutsch, kSprache=1). Eine laufzeitseitige Umschaltung über den Accept-Language-Header ist aktuell nicht aktiv. Bei Bedarf an Mehrsprachigkeit bitte Kontakt aufnehmen.

    bash
    curl -H "X-API-Key: $KEY" https://deinname.jtl2api.com/attributes
    json
    {
      "data": [
        {
          "id": 5,
          "name": "Farbe",
          "values": [
            { "id": 12, "value": "Rot" },
            { "id": 13, "value": "Blau" },
            { "id": 14, "value": "Grün" }
          ]
        },
        {
          "id": 6,
          "name": "Größe",
          "values": [
            { "id": 21, "value": "S" },
            { "id": 22, "value": "M" },
            { "id": 23, "value": "L" }
          ]
        }
      ],
      "total": 2
    }

    Merkmalswerte auf einem Artikel

    GET /products/{sku}

    Der Endpunkt /products/{sku} liefert immer ein attributes-Objekt, gruppiert nach Attributname. Bei /products opt-in über ?include=attributes, um N+1 zu vermeiden.

    bash
    curl -H "X-API-Key: $KEY" https://deinname.jtl2api.com/products/ART-002
    json
    {
      "data": {
        "SKU": "ART-002",
        "ProductName": "T-Shirt",
        "attributes": {
          "Farbe": ["Rot"],
          "Größe": ["M"]
        }
      }
    }

    Artikel nach Attribut filtern

    GET /products

    Der Parameter ?attribute[name]=wert filtert Artikel. Attributnamen sind die deutschen JTL-Originalnamen (kleingeschrieben). Mehrere Filter werden mit UND verknüpft.

    bash
    curl -H "X-API-Key: $KEY" "https://deinname.jtl2api.com/products?attribute[farbe]=rot&attribute[groesse]=m&include=attributes"
    json
    {
      "data": [
        {
          "SKU": "ART-002",
          "ProductName": "T-Shirt",
          "attributes": { "Farbe": ["Rot"], "Größe": ["M"] }
        }
      ],
      "total": 4
    }

    Bestand

    GET /stock

    Lagerbestand auflisten mit optionalem SKU-Filter. Jede Zeile enthält: SKU, AvailableStock, ReservedStock, IncomingStock, BlockedStock und StockLevel. JTL_ProductId ist advanced/opt-in.

    bash
    curl -H "X-API-Key: $KEY" "https://deinname.jtl2api.com/stock?sku=ART-001"
    json
    {
      "data": [
        {
          "SKU": "ART-001",
          "JTL_ProductId": 4821,
          "AvailableStock": 37.0,
          "ReservedStock": 5.0,
          "IncomingStock": 0.0,
          "BlockedStock": 0.0,
          "StockLevel": 42.0
        }
      ]
    }

    Bestand je Lager

    GET /products/{sku}/stock

    Lagerbestand eines Artikels aufgeteilt nach Lager abrufen. Eine Zeile pro Lager mit WarehouseId, WarehouseName, WarehouseActive und StockInWarehouse.

    bash
    curl -H "X-API-Key: $KEY" https://deinname.jtl2api.com/products/ART-001/stock
    json
    {
      "data": [
        {
          "SKU": "ART-001",
          "WarehouseId": 1,
          "WarehouseName": "Hauptlager Hamburg",
          "WarehouseActive": 1,
          "StockInWarehouse": 42.0
        }
      ]
    }

    Bestand aller Artikel je Lager

    GET /stock/by-warehouse

    Lagerbestand aller Artikel aufgeteilt nach Lager (Sammelabruf für den Bestandsabgleich). Eine Zeile je Artikel und Lager mit SKU, WarehouseId, WarehouseName, WarehouseActive, StockInWarehouse und AvailableInWarehouse. Seitenweise über limit und offset.

    ParameterTypBeschreibung
    skustringFilter nach Artikelnummer
    warehouse_idintFilter nach JTL-Lager-ID (kWarenLager)
    active_onlyboolNur aktive JTL-Artikel zurückgeben (inaktive oder gelöschte mit Restbestand überspringen)
    shop_idintNur Artikel, die diesem JTL-Shop zugewiesen sind (kShop). Benötigt aktualisierte Ansichten: einmalig in der JTL-Wawi die SQL2REST-Ansichten neu erstellen. Ohne shop_id wird der vollständige Katalog zurückgegeben (abwärtskompatibel).
    bash
    curl -H "X-API-Key: $KEY" "https://deinname.jtl2api.com/stock/by-warehouse?shop_id=1"

    Lieferungen

    GET /shipments

    Lieferungen auflisten mit Filtern nach Auftrag, Versanddienstleister und Zeitraum.

    ParameterTypBeschreibung
    orderstringFilter nach Auftragsnummer
    carrierstringFilter nach Versanddienstleister
    from / todateDatumsbereich (YYYY-MM-DD)
    sort_orderstringSortierrichtung: asc oder desc (statt order)
    bash
    curl -H "X-API-Key: $KEY" "https://deinname.jtl2api.com/shipments?order=86957"
    json
    {
      "data": [
        {
          "OrderNumber": "86957",
          "CarrierName": "DHL",
          "TrackingID": "00340434161094123456",
          "ShippingDate": "2026-04-15"
        }
      ]
    }

    Lieferscheine

    GET /delivery-notes

    Lieferscheine auflisten mit optionaler Suche und Datumsfilter.

    ParameterTypBeschreibung
    searchstringLIKE-Suche über Name und Nummer
    from / todateDatumsbereich (YYYY-MM-DD)
    includestringitems: Positionen einschließen

    Einzelnen Lieferschein nach Lieferscheinnummer abrufen, Positionen werden automatisch eingeschlossen.

    Lieferscheine mit Positionen in einem Aufruf abrufen.

    bash
    curl -H "X-API-Key: $KEY" "https://deinname.jtl2api.com/delivery-notes?include=items"
    json
    {
      "data": [
        {
          "DeliveryNoteNumber": "LS-10042",
          "DeliveryNoteDate": "2024-03-15",
          "OrderNumber": "86957",
          "CustomerNumber": "10001",
          "items": [
            {
              "ArticleNumber": "ART-001",
              "ArticleName": "Produkt A",
              "Quantity": 2
            }
          ]
        }
      ],
      "total": 12,
      "limit": 100,
      "offset": 0
    }

    Einkauf (Wareneingang)

    GET /goods-receipts

    Drei Endpunkte für Einkaufsprozesse: Wareneingänge (stock movements), Lieferanten (suppliers) und Lieferantenbestellungen (purchase orders). Standardmäßig deaktiviert, im Setup-Wizard unter Schritt 3 "Einkauf / Wareneingang" aktivieren. Wenn aus, geben die Endpunkte 404 zurück.

    Wareneingänge auflisten (Buchungen auf Lagerplätzen). Lieferant und Bestellung sind nur für Buchungsart 10 (Wareneingang aus Bestellung) befüllt; andere Buchungsarten liefern NULL für diese Spalten.

    ParameterTypBeschreibung
    booking_typeintegerJTL Buchungsart (10 = Wareneingang, 170 = Retoure, 40 = FBA, etc.)
    booking_type_not_instringKomma-Liste auszuschließender Buchungsarten (z.B. 40,170)
    supplier_idintegerFilter nach Lieferanten-ID (kLieferant)
    skustringFilter nach Artikel-SKU
    has_pobooleantrue = nur Zeilen mit verknüpfter Bestellung
    since / untildateDatumsbereich auf received_at (YYYY-MM-DD)
    bash
    curl -H "X-API-Key: $KEY" "https://deinname.jtl2api.com/goods-receipts?booking_type=10&since=2026-01-01"
    json
    {
      "data": [
        {
          "receipt_id": 12482,
          "received_at": "2026-04-15",
          "booked_at": "2026-04-15T09:21:00",
          "delivered_at": "2026-04-14",
          "booking_type": 10,
          "booking_type_label": "Wareneingang",
          "qty": 250,
          "unit_cost": 4.85,
          "sku": "RAW-001",
          "article_name": "Rohstoff A",
          "supplier_id": 12,
          "supplier_name": "Musterlieferant GmbH",
          "supplier_no": "LIEF-0012",
          "po_id": 1019,
          "po_number": "1019",
          "po_qty_ordered": 250,
          "po_qty_delivered_total": 250,
          "po_qty_still_open": 0
        }
      ],
      "total": 22,
      "limit": 100,
      "offset": 0
    }

    Wareneingang abrufen

    GET /goods-receipts/{receipt_id}

    Einzelnen Wareneingang nach JTL-ID (kWarenLagerEingang) abrufen.

    bash
    curl -H "X-API-Key: $KEY" https://deinname.jtl2api.com/goods-receipts/12482

    Lieferanten

    GET /suppliers

    Lieferanten auflisten mit Suche, Aktiv-Filter und Adress- und Konditions-Spalten.

    ParameterTypBeschreibung
    searchstringLIKE-Suche über Firmenname, Lieferantennummer, Ansprechpartner
    aktivstringFilter nach Aktiv-Flag (Y/N)
    bash
    curl -H "X-API-Key: $KEY" "https://deinname.jtl2api.com/suppliers?aktiv=Y"
    json
    {
      "data": [
        {
          "supplier_id": 12,
          "supplier_no": "LIEF-0012",
          "company_name": "Musterlieferant GmbH",
          "contact": "Max Mustermann",
          "street": "Musterstraße 1",
          "postal_code": "10115",
          "city": "Berlin",
          "country": "DE",
          "email": "[email protected]",
          "vat_id": "DE123456789",
          "payment_term_days": 30,
          "discount_days": 10,
          "discount_pct": 2.0,
          "currency": "EUR",
          "active": "Y"
        }
      ],
      "total": 17,
      "limit": 100,
      "offset": 0
    }

    Lieferant abrufen

    GET /suppliers/{supplier_id}

    Einzelnen Lieferanten nach JTL-ID (kLieferant) abrufen.

    bash
    curl -H "X-API-Key: $KEY" https://deinname.jtl2api.com/suppliers/12

    Lieferantenbestellungen

    GET /purchase-orders

    Lieferantenbestellungen als flache Header+Position-Zeilen. Eine Zeile pro Bestellposition. Für Soll-Ist-Reporting open_only=true verwenden.

    ParameterTypBeschreibung
    supplier_idintegerFilter nach Lieferanten-ID
    statusintegerFilter nach Bestell-Status (nStatus)
    open_onlybooleantrue = nur Positionen mit qty_open > 0
    since / untildateDatumsbereich auf created_at (YYYY-MM-DD)
    bash
    curl -H "X-API-Key: $KEY" "https://deinname.jtl2api.com/purchase-orders?open_only=true"

    Bestellung abrufen

    GET /purchase-orders/{po_id}

    Einzelne Bestellung mit verschachtelten Positionen unter positions abrufen.

    bash
    curl -H "X-API-Key: $KEY" https://deinname.jtl2api.com/purchase-orders/1019
    json
    {
      "data": {
        "po_id": 1019,
        "po_number": "1019",
        "created_at": "2026-03-12",
        "expected_delivery": "2026-04-15",
        "status": 2,
        "currency": "EUR",
        "supplier_id": 12,
        "supplier_name": "Musterlieferant GmbH",
        "supplier_no": "LIEF-0012",
        "positions": [
          {
            "position_id": 4421,
            "sku": "RAW-001",
            "article_name": "Rohstoff A",
            "qty_ordered": 250,
            "qty_delivered": 250,
            "qty_open": 0,
            "unit_price_net": 4.85
          }
        ]
      }
    }

    Auswertungen (/reports)

    Verkaufszahlen je Artikel ab 1.4.6

    GET /reports/product-sales

    Beantwortet "wie oft wurde dieser Artikel verkauft" in einer Abfrage. Die Summierung passiert in der Datenbank, nicht durch das Durchblättern aller Aufträge. Mehrere Artikelnummern lassen sich mit Komma trennen.

    ParameterTypBeschreibung
    skustringArtikelnummer, mehrere mit Komma getrennt. Pflicht.
    date_fromdateBeginn des Zeitraums (JJJJ-MM-TT). Ohne Angabe der laufende Monat.
    date_todateEnde des Zeitraums (JJJJ-MM-TT). Ohne Angabe heute.
    date_basisstringorder (Standard) zählt die bestellte Menge, delivery die ausgelieferte.
    mandantintegerMandant (Datenbank), Standard 1

    Zwei Zahlen, nicht eine

    Bestellt (order) zählt die Positionen nicht stornierter Verkaufsaufträge im Zeitraum, also dieselbe Abgrenzung wie "verkauft pro Tag" in der Wawi. Noch nicht versendete Mengen sind enthalten, und der Netto-Umsatz kommt mit. Ausgeliefert (delivery) zählt die Lieferscheinpositionen, also was das Lager verlassen hat. Dort gibt es keinen Umsatz, weil Lieferscheinpositionen keinen Preis führen; ein aus dem Auftrag geborgter Wert würde am falschen Ereignis hängen. Jede Antwort nennt die verwendete Basis mit, damit die beiden Zahlen nicht verwechselt werden.

    Felder je Artikel

    FeldTypBeschreibung
    skustringArtikelnummer
    quantity_sold / quantity_deliverednumberMenge im Zeitraum: quantity_sold auf der Basis order, quantity_delivered auf der Basis delivery
    order_count / delivery_note_countnumberAnzahl der Aufträge (Basis order) beziehungsweise Lieferscheine (Basis delivery), in denen der Artikel vorkam
    net_revenuenumberNetto-Umsatz. Nur auf der Basis order, sonst null.
    date_basisstringWelche der beiden Zahlen die Antwort liefert (Top-Level der Antwort, nicht je Artikel)

    Die Artikelzeilen stehen unter results, nicht unter data. Die Antwort führt zusätzlich sku, from_date, to_date, date_basis und mandant auf oberster Ebene.

    bash
    curl -H "X-API-Key: $KEY" "https://deinname.jtl2api.com/reports/product-sales?sku=ART-001&date_from=2026-08-01"
    json
    {
      "sku": ["ART-001"],
      "from_date": "2026-08-01",
      "to_date": "2026-09-13",
      "date_basis": "order",
      "results": [
        {
          "sku": "ART-001",
          "quantity_sold": 34,
          "order_count": 29,
          "net_revenue": 1356.60
        }
      ],
      "mandant": 1
    }

    Umsatz je Mitarbeiter ab 1.4.7

    GET /reports/sales-by-employee

    Beantwortet „was hat jeder verkauft" in einer Abfrage. Die Summierung passiert in der Datenbank, nicht durch das Durchblättern tausender Aufträge.

    ParameterTypBeschreibung
    date_fromdateBeginn des Zeitraums (JJJJ-MM-TT). Ohne Angabe der laufende Monat.
    date_todateEnde des Zeitraums (JJJJ-MM-TT). Ohne Angabe heute.
    attributionstringWem der Auftrag zugerechnet wird: created_by (Standard), sales_employee oder account_manager.
    employee_idintegerAuf eine einzelne Mitarbeiter-ID einschränken. Namen dazu über /employees.
    typestringFilter nach Auftragsart, etwa B für normale Verkaufsaufträge.
    platformstringFilter nach Verkaufskanal
    countrystringFilter nach Rechnungsland
    stornointegerFilter nach Storno-Kennzeichen (0 aktiv, 1 storniert)
    include_gross_profitbooleanRohertrag mitrechnen. Standard false, weil dafür die Positionen dazugelesen werden.
    mandantintegerMandant (Datenbank), Standard 1

    Wem der Umsatz zugerechnet wird

    Derselbe Auftrag zählt je nach Wahl auf eine andere Person, deshalb ist das eine Frage und kein Detail. created_by ist der Ersteller, je Auftrag festgeschrieben und damit die einzige Angabe, deren Bedeutung unstrittig ist: daher der Standard. sales_employee ist der Mitarbeiter am Auftrag, auftragsseitig, aber in der Wawi je Auftrag umsetzbar. account_manager ist der Betreuer am Kundenstamm und damit eine Momentaufnahme: ordnest du einen Kunden neu zu, ändert sich die Zahl auch für seine alten Aufträge. Jede Antwort nennt die verwendete Zurechnung mit.

    Felder je Mitarbeiter

    FeldTypBeschreibung
    employee_idintegerMitarbeiter-ID
    employee_namestringName des Mitarbeiters
    ordersnumberAnzahl der Aufträge, Stornos nicht mitgezählt
    customersnumberAnzahl verschiedener Kunden
    net_order_valuenumberNetto-Umsatz
    gross_order_valuenumberBrutto-Umsatz
    average_order_valuenumberDurchschnittlicher Auftragswert
    cancelled_orders / cancelled_net_valuenumberAnzahl der Stornos und ihr Netto-Wert, getrennt ausgewiesen

    Stornos werden getrennt ausgewiesen statt weggelassen. Umsatz und Stornos zusammen sind alle Aufträge, die durch die Filter kamen, damit nichts unangekündigt aus der Summe fällt.

    Der Rohertrag rechnet mit dem heutigen Einkaufspreis aus dem Artikelstamm, nicht mit dem zum Verkaufszeitpunkt. Bei alten Aufträgen oder geänderten Einkaufspreisen weicht der Wert deshalb ab. Die Antwort schreibt das als cost_basis dazu.

    Die Mitarbeiterzeilen stehen unter results, nicht unter data.

    bash
    curl -H "X-API-Key: $KEY" "https://deinname.jtl2api.com/reports/sales-by-employee?date_from=2026-08-01"
    json
    {
      "from_date": "2026-08-01",
      "to_date": "2026-09-13",
      "attribution": "created_by",
      "results": [
        {
          "employee_id": 7,
          "employee_name": "M. Keller",
          "orders": 128,
          "customers": 94,
          "net_order_value": 48213.40,
          "gross_order_value": 57373.95,
          "average_order_value": 376.66,
          "cancelled_orders": 3,
          "cancelled_net_value": 421.00
        }
      ],
      "mandant": 1
    }

    Mitarbeiter ab 1.4.7

    GET /employees

    Das Mitarbeiterverzeichnis aus der Wawi: Name, Abteilung und ob die Person noch aktiv ist. Damit lassen sich die Mitarbeiter-IDs auflösen, die an Aufträgen, Rechnungen und Gutschriften hängen.

    Im Chat heißen dieselben Werkzeuge list_employees und get_employee.

    ParameterTypBeschreibung
    active_onlybooleanNur aktive Mitarbeiter. Standard true; mit false kommen die Ausgeschiedenen mit.
    mandantintegerMandant (Datenbank), Standard 1

    Einzelnen Mitarbeiter nach ID abrufen: GET /employees/{id}.

    Felder je Mitarbeiter

    FeldTypBeschreibung
    EmployeeIdintegerMitarbeiter-ID, die an Aufträgen und Belegen steht
    EmployeeNamestringName
    LoginstringAnmeldename in der Wawi
    DepartmentstringAbteilung
    EmailstringE-Mail-Adresse
    ActiveintegerNoch aktiv (1) oder ausgeschieden (0)

    Ausgeschiedene Mitarbeiter bleiben auffindbar. Das ist der Zweck des Verzeichnisses: eine Auswertung über das letzte Jahr bleibt richtig, auch wenn jemand das Unternehmen inzwischen verlassen hat.

    Das Verzeichnis kommt vollständig, ohne limit und offset. Eine Belegschaft ist keine Datenmenge, durch die man blättert.

    bash
    curl -H "X-API-Key: $KEY" https://deinname.jtl2api.com/employees
    json
    {
      "data": [
        {
          "EmployeeId": 7,
          "EmployeeName": "M. Keller",
          "Login": "mkeller",
          "Department": "Vertrieb",
          "Email": "[email protected]",
          "Active": 1
        }
      ]
    }

    Workflow-Protokoll ab 1.4.7

    GET /workflow-logs

    Die Protokolleinträge deiner JTL-Workflows: wann welcher Workflow gelaufen ist, was er gemeldet hat und wo er abgebrochen ist. Filterbar nach Schweregrad, Workflow, Objekt und Zeitraum, dazu eine Volltextsuche über die Meldung.

    Eigene Berechtigung. Die Workflow-Protokolle liegen seit 1.4.7 in der Berechtigung Workflows und hängen an keinem anderen Bereich. Im Chat heißt das Werkzeug list_workflow_logs.

    ParameterTypBeschreibung
    severitystringSchweregrad, mehrere per Komma: 1=Fehler 2=Warnung 4=Info 5=Aktion
    workflowintegerGenau ein Workflow, über dieselbe WorkflowId wie in den Definitionen
    searchstringVolltextsuche über Meldung, ObjectId und Namen des Workflows
    from / to / limit / offsetdate / integerZeitraum (YYYY-MM-DD), Seitenlauf mit 100 Einträgen je Seite

    Alle Felder, die Schweregrade und die Beispiele stehen auf der eigenen Seite Workflow-Log.

    bash
    curl -H "X-API-Key: $KEY" "https://deinname.jtl2api.com/workflow-logs?severity=1&from=2026-09-01"

    Workflow-Definitionen ab 1.4.8

    GET /workflow-definitions

    Das Workflow-Protokoll sagt, was passiert ist. Die Workflow-Definitionen sagen, warum: bei welchem Ereignis ein Workflow startet, welche Bedingungen er prüft und welche Aktionen er ausführt. Rein lesend wie alles hier: In die Wawi wird nichts geschrieben, und es wird kein Workflow ausgelöst.

    WorkflowId ist derselbe Wert wie im Workflow-Protokoll. Darüber werden aus zwei Listen eine Antwort: Der Protokolleintrag nennt den Abbruch, der Workflow dahinter nennt den Grund. Keine Übersetzungstabelle, kein Suchen über den Namen.

    Keine neue Berechtigung nötig. Die Workflow-Definitionen liegen in derselben Berechtigung wie die Workflow-Protokolle, denn es ist dieselbe Sache aus zwei Blickwinkeln. Wer die Protokolle schon freigegeben hat, bekommt sie mit dem Update, ohne etwas anzuhaken.

    Im Chat heißen die Werkzeuge list_workflow_definitions und get_workflow_definition. Das zweite liefert Kopf, Bedingungen und Aktionen eines Workflows in einer einzigen Antwort.

    ParameterTypBeschreibung
    searchstringSuche über den Namen des Workflows
    workflowintegerGenau ein Workflow, über dieselbe WorkflowId wie im Protokoll
    eventintegerFilter nach dem Ereignis-Code
    objectintegerFilter nach dem Objekt-Code
    sort / orderstringSortierung nach Spaltenname, Richtung asc oder desc. Standard ist der Name des Workflows, aufsteigend
    limit / offset / pageintegerSeitenlauf, Standard 100 Workflows je Seite
    mandantintegerMandant (Datenbank), Standard 1

    ObjectType und EventType stehen immer, EventLabel nicht: Die Bezeichnung kommt aus dem Ereigniskatalog der Wawi, und der ist dort oft nur teilweise gefüllt. Filtere deshalb über die Codes, und baue keine Anzeige, die ohne die Bezeichnung bricht.

    Ob ein Workflow aktiv ist, steht nicht in den Daten: Dieses Feld führt die Wawi nicht. Ob er läuft, zeigt das Workflow-Protokoll.

    bash
    curl -H "X-API-Key: $KEY" "https://deinname.jtl2api.com/workflow-definitions?search=Auslieferung"
    json
    {
      "data": [
        {
          "WorkflowId": 42,
          "WorkflowName": "Auslieferung vorbereiten",
          "ObjectType": 6,
          "EventType": 1,
          "EventLabel": null,
          "Position": 0,
          "WorkflowType": 0,
          "Application": 63,
          "ConditionLink": 0
        }
      ],
      "total": 79,
      "limit": 100,
      "offset": 0
    }

    Bedingungen eines Workflows ab 1.4.8

    GET /workflow-definitions/{id}/conditions

    Die Bedingungen eines Workflows in ihrer Reihenfolge: die geprüfte Eigenschaft, der Operator im Klartext und der Wert, gegen den verglichen wird.

    Ein Beispiel. Im Protokoll steht, ein Auslieferungs-Workflow sei an der Bedingung Auftragspositionen.KomplettLieferbarAusLager abgebrochen. Dieselbe Bedingung findest du hier wieder, aber unter dem Namen GruppePositionen.KomplettLieferbarAusLager. Warum sie anders heißt, steht gleich unter der Tabelle.

    So ist eine Bedingung aufgebaut

    Eine Bedingung besteht aus drei Angaben. Am Beispiel von oben:

    Die geprüfte EigenschaftGruppePositionen.KomplettLieferbarAusLager.contains.BrowsePK
    Der Operatorist gleich
    Der Wert1  (Zentrallager)

    Eine Regel kann mehrere Bedingungen haben. Jede Bedingung hat genau einen Pfad. Ein Pfad besteht aus Teilen. Die Teile bekommst du einzeln in PropertyParts, jeweils mit der Angabe, woher sie stammen. Zusammengesetzt mit Punkten ergeben sie den PropertyPath.

    Felder je Bedingung

    FeldTypBeschreibung
    ConditionIdintegerKennung der Bedingung
    PositionintegerReihenfolge innerhalb des Workflows
    PropertyPathstringDie geprüfte Eigenschaft als lesbarer Pfad
    PropertyPartsarrayDieselben Teile einzeln, je mit Herkunft
    OperatorCodeintegerDer Vergleichsoperator als Zahl
    OperatorLabelstringDerselbe Operator im Klartext, etwa enthält
    OperatorMemberstringDerselbe Operator als technischer Bezeichner
    OperatorTakesOperandbooleanOb dieser Operator überhaupt einen Vergleichswert hat
    OperatorProvenancestringWorauf die Bezeichnung steht
    ComparisonValuestringDer Wert, gegen den verglichen wird
    ComparisonValueLabelstring | nullDerselbe Wert als Name, wenn er auf einen Datensatz verweist

    Jede Angabe trägt ihre Herkunft mit. OperatorLabel ist die deutsche Formulierung, die du aus der Wawi kennst, und OperatorProvenance sagt, worauf sie steht. Einen Operator, den wir noch nicht benennen können, bekommst du mit seiner Zahl und ohne Bezeichnung: Die Bedingung verschwindet nie, sie bleibt nur unbenannt.

    Die Wawi zeigt andere Namen an, als die API liefert, weil die API den gespeicherten Bezeichner zurückgibt und die Wawi daraus einen Anzeigenamen macht. Gemeint ist dasselbe:

    In der Wawi stehtDie API liefert
    AuftragspositionenGruppePositionen
    enthältcontains
    AnmerkungcAnmerkung
    Enthält_Standard-LagerEnthaeltStandardLager

    Zurückrechnen kannst du das nicht. Umlaute und Sonderzeichen fallen beim Speichern weg, und aus EnthaeltStandardLager lässt sich Enthält_Standard-Lager nicht wiederherstellen. Vergleiche den Pfad deshalb nicht Zeichen für Zeichen mit dem, was am Bildschirm steht.

    Wie viele Teile ein Pfad hat, ist nicht festgelegt. Baue also nichts, was eine bestimmte Anzahl voraussetzt.

    Auch der Aufbau kann sich unterscheiden, obwohl dieselbe Frage gemeint ist. Mal steht die Antwort im Pfad selbst, etwa …EnthaeltStandardLager verglichen gegen True. Mal steht sie im Vergleichswert:

    BrowsePK am Ende des Pfades heißt: Hier wurde ein Datensatz über einen Auswahldialog gewählt. Der Vergleichswert ist dann dessen Nummer. Zeigt er auf ein Lager, steht der Name daneben in ComparisonValueLabel. Eines siehst du hier allerdings nicht: ob jemand die Auswahl in der Wawi offen gelassen hat. Eine unfertige Regel sieht in den Daten aus wie eine fertige.

    Der Vergleichswert ist immer Text. Und zwar so, wie die Wawi ihn gespeichert hat, nicht so, wie er eingetippt wurde. Aus einer eingetippten 3 wird 3,0000, mit Komma. Wenn du damit rechnest, ersetze das Komma vorher durch einen Punkt. Manche Operatoren haben gar keinen Wert, etwa ist leer. Ob einer zu erwarten ist, sagt dir OperatorTakesOperand.

    bash
    curl -H "X-API-Key: $KEY" https://deinname.jtl2api.com/workflow-definitions/42/conditions
    json
    {
      "data": [
        {
          "ConditionId": 311,
          "WorkflowId": 42,
          "Position": 0,
          "PropertyPath": "GruppePositionen.KomplettLieferbarAusLager.contains.BrowsePK",
          "PropertyParts": [
            { "PropertyIndex": 1, "PropertyName": "GruppePositionen", "PropertyParentType": "jtlCore.Models.PropertyProxy.Auftrag.Auftrag" },
            { "PropertyIndex": 2, "PropertyName": "KomplettLieferbarAusLager", "PropertyParentType": "jtlCore.Models.PropertyProxy.Auftrag.AuftragPositionen" },
            { "PropertyIndex": 3, "PropertyName": "contains", "PropertyParentType": "jtlDatabase.classes.jtlDBClasses.jtlWarenlagerList" },
            { "PropertyIndex": 4, "PropertyName": "BrowsePK", "PropertyParentType": "jtlDatabase.classes.jtlDBClasses.jtlWarenlagerList" }
          ],
          "OperatorCode": 1,
          "OperatorLabel": "ist gleich",
          "OperatorMember": "Equal",
          "OperatorTakesOperand": true,
          "OperatorProvenance": "BELEGT",
          "ComparisonValue": "1",
          "ComparisonValueLabel": "Zentrallager"
        },
        {
          "ConditionId": 312,
          "WorkflowId": 42,
          "Position": 1,
          "PropertyPath": "GruppePositionen.KomplettLieferbarAusLager.EnthaeltStandardLager",
          "PropertyParts": [
            { "PropertyIndex": 1, "PropertyName": "GruppePositionen", "PropertyParentType": "jtlCore.Models.PropertyProxy.Auftrag.Auftrag" },
            { "PropertyIndex": 2, "PropertyName": "KomplettLieferbarAusLager", "PropertyParentType": "jtlCore.Models.PropertyProxy.Auftrag.AuftragPositionen" },
            { "PropertyIndex": 3, "PropertyName": "EnthaeltStandardLager", "PropertyParentType": "jtlDatabase.classes.jtlDBClasses.jtlWarenlagerList" }
          ],
          "OperatorCode": 1,
          "OperatorLabel": "ist gleich",
          "OperatorMember": "Equal",
          "OperatorTakesOperand": true,
          "OperatorProvenance": "BELEGT",
          "ComparisonValue": "True",
          "ComparisonValueLabel": null
        },
        {
          "ConditionId": 313,
          "WorkflowId": 42,
          "Position": 2,
          "PropertyPath": "GruppePositionen.BruttopreisGesamt",
          "PropertyParts": [
            { "PropertyIndex": 1, "PropertyName": "GruppePositionen", "PropertyParentType": "jtlCore.Models.PropertyProxy.Auftrag.Auftrag" },
            { "PropertyIndex": 2, "PropertyName": "BruttopreisGesamt", "PropertyParentType": "jtlCore.Models.PropertyProxy.Auftrag.AuftragPositionen" }
          ],
          "OperatorCode": 3,
          "OperatorLabel": "ist größer als",
          "OperatorMember": "GreaterThan",
          "OperatorTakesOperand": true,
          "OperatorProvenance": "BELEGT",
          "ComparisonValue": "3,0000",
          "ComparisonValueLabel": null
        },
        {
          "ConditionId": 314,
          "WorkflowId": 42,
          "Position": 3,
          "PropertyPath": "GruppeZahlungen.Zahlungen.contains.Auftrag.cAnmerkung",
          "PropertyParts": [
            { "PropertyIndex": 1, "PropertyName": "GruppeZahlungen", "PropertyParentType": "jtlCore.Models.PropertyProxy.Auftrag.Auftrag" },
            { "PropertyIndex": 2, "PropertyName": "Zahlungen", "PropertyParentType": "jtlCore.Models.PropertyProxy.Auftrag.AuftragZahlung" },
            { "PropertyIndex": 3, "PropertyName": "contains", "PropertyParentType": "jtlDatabase.classes.jtlDBClasses.jtlZahlungList" },
            { "PropertyIndex": 4, "PropertyName": "Auftrag", "PropertyParentType": "jtlCore.Models.PropertyProxy.Zahlung.Zahlung" },
            { "PropertyIndex": 5, "PropertyName": "cAnmerkung", "PropertyParentType": "jtlCore.Models.PropertyProxy.Auftrag.AuftragBestellung" }
          ],
          "OperatorCode": 9,
          "OperatorLabel": "enthält",
          "OperatorMember": "Contains",
          "OperatorTakesOperand": true,
          "OperatorProvenance": "BELEGT",
          "ComparisonValue": "Express",
          "ComparisonValueLabel": null
        }
      ],
      "total": 4,
      "workflow_id": 42
    }

    Aktionen eines Workflows ab 1.4.8

    GET /workflow-definitions/{id}/actions

    Was ein Workflow tut, sobald seine Bedingungen zutreffen: die Aktionen in ihrer Reihenfolge, jede mit ihrer Art und ihrem Parametersatz.

    Felder je Aktion

    FeldTypBeschreibung
    ActionIdintegerKennung der Aktion
    PositionintegerReihenfolge innerhalb des Workflows
    ActionTypestringDie Art der Aktion, wie die Wawi sie führt
    ParametersobjectDie Parameter der Aktion; leer bei unbekannter Art

    Die Aktionsparameter sind eine feste Auswahl. Was nicht dazugehört, wird nicht ausgeliefert, und eine Aktionsart, die wir noch nicht auflösen, erscheint mit ihrer Bezeichnung und mit leerem Parameters. Ein leerer Parametersatz heißt also: diese Art lösen wir noch nicht auf. Er heißt nie, dass die Aktion nichts tut.

    Was dieser Endpunkt nicht auflöst

    Einen Namen bekommst du nur für Warenlager. Bei jedem anderen Verweis bekommst du die Nummer. Worauf sie zeigt, sagt dir der PropertyParentType des letzten Pfadteils: eine Zahlungsart, ein Shop, eine Kundengruppe. Sag also „Zahlungsart Nr. 9" statt „ist gleich 9".

    Ein Vergleichswert kann mehrere Nummern enthalten, getrennt durch Semikolon, etwa 16;17. Das ist eine Mehrfachauswahl und keine große Zahl. Für solche Werte kommt nie ein Name.

    Zwei Dinge sind ausdrücklich keine Lücke. Alle Operatoren, die die Wawi kennt, sind benannt. Und die Aktionsparameter sind eine feste Auswahl: Eine Aktionsart, die wir noch nicht auflösen, behält ihre Bezeichnung und kommt ohne Parameter. Ein leerer Parametersatz heißt „diese Art lösen wir noch nicht auf", nie „diese Aktion tut nichts".

    Die Form der Antwort steht fest. Was heute nicht aufgelöst wird, füllt später ein Feld, das jetzt null ist. Es fällt nichts weg und nichts wird umbenannt. Du kannst also darauf bauen.

    bash
    curl -H "X-API-Key: $KEY" https://deinname.jtl2api.com/workflow-definitions/42/actions
    json
    {
      "data": [
        {
          "ActionId": 187,
          "WorkflowId": 42,
          "Position": 0,
          "ActionType": "a:jtlAktionEmail",
          "Parameters": {
            "Subject": "Ihre Sendung ist unterwegs",
            "Empfaenger": "{{ Vorgang.Kunde.EMail }}",
            "CancelOnError": "true"
          }
        },
        {
          "ActionId": 188,
          "WorkflowId": 42,
          "Position": 1,
          "ActionType": "a:jtlAktionUnbekannt",
          "Parameters": {}
        }
      ],
      "total": 2,
      "workflow_id": 42
    }

    Cross-API: SQL2REST lesen + JTL schreiben

    GET /orders

    Die Felder mit JTL_-Präfix sind Schlüsselwerte aus Ihrer JTL-Datenbank. Sie sind im Wizard standardmäßig deaktiviert (siehe Abschnitt "Erweitert" im Setup) und werden nur benötigt, wenn Sie zusätzlich zur read-only API von SQL2REST auch über die offizielle JTL-API in JTL schreiben (zum Beispiel Auftragsstatus aktualisieren). Reine Leseintegrationen können diese Felder ignorieren.

    http
    # 1. Read order via SQL2REST (this API)
    GET /orders?order_number=12345
    # response includes: JTL_SalesOrderId: 9, JTL_CustomerId: 42
    
    # 2. Use JTL_SalesOrderId to update via the official JTL API
    PATCH https://your-jtl-server/api/eazybusiness/v1/salesOrder/9

    Sync-Endpunkte

    GET /sync/orders

    Vorverbundene Endpunkte für CRM-Integrationen. Liefern Daten aus mehreren Tabellen in einem Aufruf.

    Aufträge mit Kunden-, Rechnungs- und Versanddaten in einem Aufruf.

    bash
    curl -H "X-API-Key: $KEY" "https://deinname.jtl2api.com/sync/orders?since=2026-08-01"

    Kunden synchronisieren

    GET /sync/customers

    Batch-Kundensynchronisation mit höherem Limit (bis zu 1000).

    Firma, Name, Kontakt und Adresse stammen aus der Standardadresse des Kunden.

    bash
    curl -H "X-API-Key: $KEY" "https://deinname.jtl2api.com/sync/customers?limit=1000"
    json
    {
      "data": [
        {
          "CustomerNumber": "10001",
          "CompanyName": "Müller GmbH",
          "FirstName": "Thomas",
          "LastName": "Müller",
          "CustomerEmail": "[email protected]",
          "CustomerPhone": "069 1234567",
          "CustomerCreatedDate": "2024-01-15",
          "Address": {
            "Street": "Musterstraße 5",
            "PostalCode": "60311",
            "City": "Frankfurt",
            "Country": "DE"
          }
        }
      ],
      "total": 842,
      "limit": 1000,
      "offset": 0
    }

    Abfrage-Grundlagen

    Filtern & Suchen

    Zwei Arten von Filtern stehen zur Verfügung:

    Textsuche (LIKE)

    Der search-Parameter durchsucht Name und Nummer. Verfügbar auf /customers und /products.

    Exakte Filter

    Filterparameter liefern exakte Übereinstimmungen:

    ParameterVerfügbar auf
    customer/orders, /invoices, /returns, /sync/orders
    customer_number/customers, /orders, /returns
    order_number/orders
    external_order_id/orders
    status/orders, /invoices, /returns
    rma_number/returns
    sku/stock, /products
    order/shipments
    carrier/shipments

    Sortierung & Paginierung

    Die meisten Listen-Endpunkte unterstützen Sortierung und Paginierung.

    ParameterStandardBeschreibung
    sortSortierfeld (beliebiger Spaltenname der View)
    orderascSortierrichtung: asc oder desc
    limit100Ergebnisse pro Seite (1–500)
    offset0Ergebnisse überspringen

    Bei /invoices und /delivery-notes filtert ?order_number= nach der Auftragsnummer, und das ist die empfohlene Schreibweise. Aus Rücksicht auf bestehende Integrationen gilt weiterhin: ein Wert auf ?order=, der nicht asc oder desc ist, wird als Auftragsnummer gelesen. Ein Tippfehler in der Richtung liefert deshalb eine leere Seite statt einer Fehlermeldung.

    Nicht alle Endpunkte sortieren und blättern. Ohne sort, order, limit und offset arbeiten: /employees, /attributes, /warehouses, /shops, /mandanten sowie die beiden Auswertungen unter /reports. Sie liefern ihre Menge vollständig. mandant versteht dagegen jeder Endpunkt außer /mandanten.

    Hinweis: /shipments verwendet sort_order statt order.

    Datumsfilter

    Zeitraum-Filter über from/to-Parameter (jeweils einschließlich).

    Multi-Mandant

    Wenn Sie mehrere JTL-Datenbanken konfiguriert haben, verwenden Sie den mandant-Parameter, um die Zieldatenbank auszuwählen. Standard ist 1.

    Mandanten-Limits nach Tarif

    TarifMandanten
    Trial1
    Personal1
    Agency5
    EnterpriseUnbegrenzt

    Vertiefung

    Auto-Discovery

    Der Setup-Wizard analysiert automatisch Ihr JTL-Datenbankschema und erkennt die richtigen Spalten für jeden Endpunkt.

    • Erkennt Ihre JTL-Wawi Version automatisch (1.5 bis 2.0)
    • Ordnet Standardfelder automatisch zu (Kundennummer, Bestelldatum, SKU etc.)
    • Nicht erkannte Felder können manuell per Dropdown zugeordnet werden
    • Spaltenvorschau zeigt echte Beispieldaten aus Ihrer Datenbank

    Eigene Felder

    SQL2REST erkennt automatisch benutzerdefinierte Felder (Eigene Felder) in Ihrer JTL-Datenbank und macht sie per API verfügbar.

    • Unterstützt Artikel-, Kunden- und Bestellungsfelder
    • Alle JTL-Feldtypen: Text, Ganzzahl, Dezimalzahl, Datum
    • Pro Gruppe aktivierbar/deaktivierbar im Setup-Wizard (Schritt 3)
    • Standardmäßig deaktiviert - nur aktive Felder erscheinen in der API

    Eigene Felder erscheinen in der API-Antwort mit dem Präfix CF_:

    json
    {
      "SKU": "ART-001",
      "ProductName": "Widget",
      "CF_Energie_KJ": 500,
      "CF_Zutaten": "Mehl, Zucker, Butter",
      "CF_WEEE_Pickup": 1
    }

    Um Eigene Felder nachträglich zu aktivieren, führen Sie den Setup-Wizard erneut aus.

    Community Mapping

    Wenn Sie eine Spalte manuell zuordnen, können Sie diese Zuordnung anonym teilen, um die Erkennung für alle Nutzer zu verbessern.

    • Nur bei expliziter Zustimmung im Wizard (Opt-in)
    • Es werden nur Tabellen-/Spaltennamen geteilt, niemals Geschäftsdaten

    Details zum Datenschutz: /datenschutz

    Add-ons

    Diese Add-ons erweitern SQL2REST um eigene Bereiche mit eigener Dokumentation:

    Dazu kommt ein Add-on anderer Art: es erweitert nicht den Funktionsumfang, sondern die Erreichbarkeit.

    SQL2REST-Tunnel

    Eine verschlüsselte Adresse (HTTPS) für API und MCP, ohne offene Ports und ohne eigene Domain. Ohne sie geht das Verbinden per Anmeldung nicht, weil ein Konnektor deine Wawi von außen erreichen muss.

    In Team, Agency und Enterprise enthalten. Im Personal-Tarif für 19 €/Monat zubuchbar, im laufenden Monat anteilig; nur bei monatlicher Zahlung, bei Jahresabo über den Support.

    Einrichtung: im Dashboard dazubuchen (inklusive in Team oder höher), dann in den Einstellungen unter Fernzugriff Von überall wählen und die Adresse bestätigen. Einzelheiten unter Basis-URL und Tunnel.

    Betrieb

    Aktualisierung

    Meldet das API-Dashboard eine neue Version, genügt ein Klick auf den Hinweis: SQL2REST lädt das Update, prüft es per SHA-256 und spielt es ein. update.bat im Installationsordner tut dasselbe von Hand und ist die Notlösung, wenn der Knopf nicht durchläuft.

    Update-Benachrichtigung

    Wenn eine neue Version verfügbar ist, zeigt das API-Dashboard automatisch einen Hinweis mit dem Knopf, der das Update startet. Sie öffnen es über den Startmenü-Eintrag SQL2REST.

    Von Hand aktualisieren (Notlösung)

    1. Rechtsklick auf update.bat → „Als Administrator ausführen"
    2. Versionsvergleich prüfen und mit Y bestätigen

    Manuelles Update (offline)

    1. Laden Sie die neueste Version aus Ihrem Web-Dashboard herunter
    2. Führen Sie als Administrator aus: update.bat C:\Pfad\zur\sql2rest-latest.zip

    Was passiert

    • Prüft auf neue Versionen (Auto-Update) oder nutzt die angegebene ZIP-Datei
    • Dienst wird über NSSM gestoppt (Fallback: taskkill)
    • config.ini wird gesichert und nach dem Update wiederhergestellt
    • Download wird per SHA-256-Hash verifiziert
    • Dienst wird automatisch neu gestartet

    Wenn kein Windows-Dienst installiert ist, wird sql2rest.exe direkt beendet. Nach dem Update müssen Sie das Programm manuell neu starten.

    Was sich je Version geändert hat, steht im Changelog.

    Setup erneut öffnen

    Um den Setup-Wizard erneut auszuführen (z.B. nach einem JTL-Update, damit neue Felder wie ProductGroup in die Views übernommen werden):

    1. Im Startmenü den Eintrag SQL2REST öffnen. Das öffnet das API-Dashboard im Browser, ohne den Dienst zu stoppen. (Ohne Startmenü-Eintrag: ApiDashboard.bat im Installationsordner.)
    2. Auf der Karte "Lokale REST-API" auf das Zahnrad-Symbol klicken. Das startet den Setup-Wizard erneut.
    3. Den Schema-Schritt erneut durchlaufen. Danach werden neue und aktualisierte Felder in die Views übernommen.
    API-Dashboard mit Zahnrad-Symbol auf der Karte Lokale REST-API / API Dashboard showing the gear icon on the Local REST-API card
    Zahnrad-Symbol auf der Karte "Lokale REST-API" im API-Dashboard

    Der Setup-Wizard legt die SQL-Views neu an. Bestehende Konfiguration (API-Schlüssel, Port, Eigene Felder) bleibt erhalten.

    Deinstallieren

    Deinstallieren entfernt SQL2REST von diesem Rechner: den Nur-Lese-Benutzer und die Ansichten aus der Datenbank, die lokale Konfiguration sowie den Windows-Dienst. Ihre JTL-Wawi Daten bleiben unberührt.

    • Im API-Dashboard unter Einstellungen, Eintrag Deinstallieren
    • Erfordert die SQL-Server-Admin-Zugangsdaten aus der Einrichtung, nicht den SQL2REST-Benutzer (der ist Nur-Lese)
    • Das lässt sich nicht rückgängig machen. Um SQL2REST danach wieder zu nutzen, richten Sie es neu ein: sql2rest.exe erneut ausführen.

    Fehlercodes

    StatusBedeutung
    200Erfolg
    402Lizenz erforderlich oder Tarif-Limit überschritten
    403Ungültiger API-Schlüssel
    404Ressource nicht gefunden / Mandant nicht konfiguriert
    500Datenbankfehler
    503Setup erforderlich (Wizard unter /setup ausführen)

    402-Antwortformat

    json
    {
      "detail": {
        "message": "Valid license required",
        "url": "https://sql2rest.com/jtl-wawi#pricing"
      }
    }

    Testversion

    Die 14-Tage-Testversion wird automatisch beim Setup erstellt und hat folgende Einschränkungen:

    MerkmalTrialBezahlt
    Endpunkte/customers, /orders, /products, /syncAlle
    Mandanten1Je nach Tarif
    Laufzeit14 TageUnbegrenzt

    Nicht verfügbare Endpunkte liefern Status 402 mit Upgrade-Link. Upgraden Sie unter /jtl-wawi#pricing.

    Über diese Schnittstelle

    Diese Entwicklerdokumentation beschreibt die REST-API Schnittstellen von SQL2REST für die Anbindung an JTL-Wawi. Mit SQL2REST können Sie externe Systeme effizient an Ihre Warenwirtschaft anbinden, Kundendaten und Artikeldaten auslesen, Bestellungen abrufen und Workflows automatisieren.

    SQL2REST funktioniert als API-Anbindung zwischen Ihrer JTL-Wawi Datenbank und externen Anwendungen. Die Erweiterung läuft als Windows-Dienst lokal auf Ihrem Server und stellt zuverlässig Read-Only Endpunkte bereit. So können Sie JTL-Wawi API anbinden, ohne einen JTL-Connector oder teure Zusatzfunktionen zu benötigen.

    Typische Anwendungsfälle sind die Integration in CRM-Systeme, E-Commerce Plattformen wie JTL-Shop oder Shopware, Multichannel-Handel über JTL-eazyAuction, sowie die zentrale Verwaltung von Prozessen über Tools wie n8n, Make oder Zapier.

    SQL2REST hält Ihre Datenbank konsistent durch ausschließlich lesende Zugriffe. Updates und neue Features werden regelmäßig bereitgestellt. JTL Servicepartner und Entwickler können die API-Anbindung individuell anpassen und integrieren.