SAP Business One Service Layer: Anleitung mit Beispielen
Von Zain Abbas, Kodenova · Aktualisiert am
Was der Service Layer ist
Der Service Layer ist die Web-API von SAP Business One. Er stellt Geschäftsobjekte wie Geschäftspartner, Artikel, Aufträge und Rechnungen über HTTPS bereit und nutzt dafür das OData-Protokoll. Jede Sprache, die einen HTTP-Request absetzen kann, kommt damit zurecht: JavaScript, Python, C#, PHP oder ein Low-Code-Tool.
Ursprünglich gab es ihn nur für die HANA-Version von SAP Business One. Neuere Feature Packs von 10.0 bieten ihn auch für SQL Server an. Standardmäßig lauscht er per HTTPS auf Port 50000:
https://your-b1-server:50000/b1s/v1/
https://your-b1-server:50000/b1s/v2/
v1 oder v2
Beide Pfade führen zu denselben Geschäftsobjekten.
- /b1s/v1 verwendet OData Version 3. Der Pfad funktioniert weiterhin, und der meiste ältere Code nutzt ihn.
- /b1s/v2 verwendet OData Version 4, das SAP inzwischen als Hauptprotokoll betrachtet.
Der Unterschied, der Ihnen zuerst auffällt, steckt in den Antworten: v1 liefert odata.nextLink, v2 liefert @odata.nextLink. Für neue Projekte wählen Sie v2 und bleiben dabei. Die Beispiele unten verwenden v1-Pfade, weil diese in der Praxis noch am weitesten verbreitet sind. Ersetzen Sie v1 durch v2, dann funktionieren sie genauso.
Schritt 1: Anmelden
Senden Sie einen POST mit Mandantendatenbank, Benutzer und Passwort:
POST https://your-b1-server:50000/b1s/v1/Login
Content-Type: application/json
{
"CompanyDB": "SBODEMOUS",
"UserName": "manager",
"Password": "your-password"
}
Eine erfolgreiche Antwort enthält die Session-ID, die Version und den Timeout in Minuten:
{
"SessionId": "1c9e4b2a-....",
"Version": "1000xxx",
"SessionTimeout": 30
}
Zusätzlich setzt der Server zwei Cookies: B1SESSION und ROUTEID.
- B1SESSION identifiziert Ihre Session.
- ROUTEID teilt dem Load Balancer des Service Layers mit, welcher Knoten Ihre Session hält, damit jeder Request wieder beim selben Knoten landet.
Ein Browser speichert und sendet diese Cookies automatisch. Außerhalb des Browsers müssen Sie beide bei jedem weiteren Aufruf in den Cookie-Header schreiben:
Cookie: B1SESSION=1c9e4b2a-....; ROUTEID=.node1
Standardmäßig läuft die Session nach 30 Minuten ohne Aktivität ab. Jeder Aufruf setzt den Timer zurück. Bauen Sie Ihren Client so, dass eine 401-Antwort genau einen neuen Login samt Wiederholung auslöst und keine Endlosschleife.
Schritt 2: Daten mit GET lesen
Ein einfacher GET liefert eine Seite mit Datensätzen inklusive aller Felder:
GET /b1s/v1/BusinessPartners
Das ist langsam und schwerfällig. Fordern Sie mit den OData-Abfrageoptionen nur das an, was Sie tatsächlich brauchen.
| Option | Wirkung | Beispiel |
|---|---|---|
$select | Nur diese Felder zurückgeben | $select=CardCode,CardName,Balance |
$filter | Nur passende Datensätze zurückgeben | $filter=CardType eq 'cCustomer' |
$orderby | Ergebnisse sortieren | $orderby=DocDate desc |
$top | Höchstens n Datensätze zurückgeben | $top=50 |
$skip | Die ersten n Datensätze überspringen | $skip=100 |
Kombiniert sieht das so aus:
GET /b1s/v1/Orders?$select=DocEntry,DocNum,CardCode,DocDate,DocTotal&$filter=DocDate ge '2026-10-01' and DocumentStatus eq 'bost_Open'&$orderby=DocDate desc&$top=50
Typische Stolperfallen:
- Feldnamen sind die Namen des Service Layers, nicht die SQL-Spaltennamen. Es heißt
DocumentStatus, nichtDocStatus. Enum-Werte sind Namen wiebost_OpenundcCustomer, nichtOundC. - Zeichenketten und Datumswerte stehen in einfachen Anführungszeichen. Zahlen nicht.
- Filteroperatoren sind Wörter:
eq,ne,gt,ge,lt,le, verknüpft mitandundor. - Textsuche funktioniert über Funktionen, zum Beispiel
startswith(CardName, 'Al'). - URL-kodieren Sie Leerzeichen und Anführungszeichen, wenn Ihre HTTP-Bibliothek das nicht selbst erledigt.
Für einen einzelnen Datensatz setzen Sie den Schlüssel in Klammern: GET /b1s/v1/Orders(125) für den Auftrag mit DocEntry 125 oder GET /b1s/v1/BusinessPartners('C20000') für einen Geschäftspartner.
Unser Service Layer Request Builder setzt diese URLs für Sie zusammen, mit korrekt gesetzten Anführungszeichen.
Schritt 3: Paging richtig handhaben
Der Service Layer liefert nie alles auf einmal. Die Standard-Seitengröße beträgt 20. Gibt es weitere Datensätze, endet die Antwort mit einem Link auf die nächste Seite:
{
"value": [ ... 20 records ... ],
"odata.nextLink": "Orders?$select=DocEntry,DocNum&$skip=20"
}
Sie haben zwei Möglichkeiten, damit umzugehen.
- Dem nextLink folgen. Rufen Sie die angegebene URL auf (sie ist relativ zu
/b1s/v1/), bis kein nextLink mehr zurückkommt. Das ist der sicherste Weg, eine vollständige Liste zu lesen. - Größere Seiten anfordern. Senden Sie einen
Prefer-Header:
GET /b1s/v1/Items?$select=ItemCode,ItemName,QuantityOnStock
Prefer: odata.maxpagesize=200
Die tatsächlich verwendete Größe bestätigt der Server im Header Preference-Applied. Mit odata.maxpagesize=0 schalten Sie das Paging ab. Bei großen Tabellen wie Rechnungen oder Journalbuchungen sollten Sie das aber vermeiden: Eine riesige Antwort kann in einen Timeout laufen oder den Server stark belasten.
Serverseitiges Paging: Prefer: odata.maxpagesize
Die Seitengröße bestimmt der Server, sofern der Request nichts anderes verlangt. Pro Request fordern Sie sie mit dem OData-Header an:
GET /b1s/v1/Orders?$select=DocEntry,DocNum
Prefer: odata.maxpagesize=100
Die Antwort bestätigt, was angewendet wurde:
HTTP/1.1 200 OK
Preference-Applied: odata.maxpagesize=100
Der Standardwert 20 stammt aus der Option PageSize in conf/b1s.conf auf dem Service-Layer-Server. Eine Änderung dort wirkt sich auf jeden Client aus, der diesen Service Layer aufruft. Verwenden Sie in Ihrem eigenen Code deshalb lieber den Header und lassen Sie die Servereinstellung unverändert. Sind beide gesetzt, gewinnt der Header odata.maxpagesize.
Sie können auch selbst mit $top und $skip blättern. In dem Fall ergänzen Sie immer ein $orderby auf ein Schlüsselfeld, sonst können Datensätze zwischen den Seiten verrutschen.
Schritt 4: Kundenauftrag mit POST anlegen
POST /b1s/v1/Orders
Content-Type: application/json
Cookie: B1SESSION=...; ROUTEID=...
{
"CardCode": "C20000",
"DocDueDate": "2026-10-30",
"Comments": "Created from web portal",
"DocumentLines": [
{ "ItemCode": "A00001", "Quantity": 5, "WarehouseCode": "01" },
{ "ItemCode": "A00002", "Quantity": 2, "UnitPrice": 150.0 }
]
}
Ein erfolgreicher POST liefert 201 Created mit dem vollständigen neuen Beleg, einschließlich DocEntry und DocNum. Speichern Sie den DocEntry, denn Sie brauchen ihn für jede spätere Änderung.
Schritt 5: Mit PATCH ändern
PATCH ändert nur die Felder, die Sie mitsenden. Alles andere bleibt, wie es ist:
PATCH /b1s/v1/Orders(125)
Content-Type: application/json
{
"Comments": "Delivery moved to Friday",
"DocDueDate": "2026-11-06"
}
Ein erfolgreicher PATCH liefert 204 No Content. Vorsicht beim PATCH von Belegzeilen: Testen Sie zuerst auf einer Kopie des Mandanten und lesen Sie den Beleg danach erneut ein, um zu prüfen, ob die Zeilen so aussehen, wie Sie es erwarten.
Entitäten verknüpfen: $crossjoin und QueryService_PostQuery
Ein normaler $filter arbeitet auf einer einzigen Entitätsmenge und kann daher zum Beispiel keine Aufträge mit Geschäftspartnern verknüpfen. Dafür beschreibt das Benutzerhandbuch des Service Layers die Ressource $crossjoin (ab SAP Business One 9.2, Version für SAP HANA, PL07):
GET /b1s/v1/$crossjoin(Orders,BusinessPartners)?$expand=Orders($select=DocEntry,DocNum),BusinessPartners($select=CardCode)&$filter=Orders/CardCode eq BusinessPartners/CardCode
Um auf Belegzeilen zu filtern (Kopf und Zeilen gemeinsam), beschreibt das Handbuch einen Query Service, QueryService_PostQuery, der Pfad und Abfrageoptionen als zwei getrennte Felder im POST-Body entgegennimmt:
POST /b1s/v1/QueryService_PostQuery
Content-Type: application/json
{
"QueryPath": "$crossjoin(Orders,Orders/DocumentLines)",
"QueryOption": "$expand=Orders($select=DocEntry,DocNum),Orders/DocumentLines($select=ItemCode,LineNum)&$filter=Orders/DocEntry eq Orders/DocumentLines/DocEntry and Orders/DocumentLines/LineNum eq 0"
}
Prüfen Sie die Versionshinweise im Benutzerhandbuch für Ihr Release, bevor Sie sich darauf verlassen. Wird die Verknüpfung komplex, ist eine gespeicherte SQL-Abfrage, die über den Service Layer aufgerufen wird, oft einfacher; siehe SQLQueries: Fehler und Lösungen.
Schritt 6: Abmelden
POST /b1s/v1/Logout
Der Logout gibt die Session auf dem Server frei. Bei einem nächtlichen Job melden Sie sich einmal an, erledigen die Arbeit und melden sich wieder ab. Bei einem Webportal halten Sie eine Session pro Benutzer oder einen kleinen Pool vor, statt sich bei jedem Request neu anzumelden.
Typische Fehler und ihre Bedeutung
| Fehler | Häufige Ursache | Lösung |
|---|---|---|
| 401 Unauthorized | Session abgelaufen oder Cookies nicht mitgesendet | Neu anmelden; prüfen, ob beide Cookies im Request stehen |
| CORS-Fehler im Browser | Origin nicht zugelassen | CORS in der Service-Layer-Konfiguration aktivieren (CorsEnable, CorsAllowedOrigins) oder aus dem Backend aufrufen |
| SSL- oder Zertifikatsfehler | Standardmäßig selbstsigniertes Zertifikat | Ein richtiges Zertifikat installieren; dem selbstsignierten nur im Test vertrauen |
| Login scheitert mit Lizenzmeldung | Benutzer hat keine passende Lizenz | Dem Benutzer in der License Administration (Lizenzverwaltung) eine Lizenz zuweisen |
| 400 mit Fehlermeldung | Fehlendes Feld, falscher Wert oder Geschäftsregel | error.message.value lesen; dort steht das betroffene Feld |
Die Fehlercodes in error.code entsprechen größtenteils denen der DI API; siehe DI-API-Entsprechungen der Service-Layer-Fehlercodes.
Noch ein Hinweis: Wenn Ihre Logins funktionieren, Aufrufe hinter einem Load Balancer aber sporadisch fehlschlagen, prüfen Sie, ob ROUTEID mitgesendet wird. Ohne dieses Cookie kann der Request bei einem Knoten landen, der Ihre Session nicht kennt.
DI API oder Service Layer?
| Aspekt | DI API | Service Layer |
|---|---|---|
| Typ | COM-Bibliothek | REST / OData über HTTPS |
| Plattform | Windows | Jede Client-Plattform |
| Sprachen | Überwiegend .NET | Jede Sprache mit HTTP |
| Am besten geeignet für | Klassische Add-ons neben dem B1 Client | Webportale, mobile Apps, Cloud-Integrationen |
| Parallelität | Eine Verbindung pro Prozess | Viele Sessions, per Load Balancer verteilt |
Für alles Neue, das von außerhalb des Clients mit SAP Business One kommuniziert, setzen wir auf den Service Layer. Darauf bauen wir unsere Business-Portale und mobilen Apps auf. Die DI API hat in bestehenden Windows-Add-ons weiterhin ihre Berechtigung.
Wenn Sie diese Aufrufe gegen Ihr eigenes System testen möchten, starten Sie mit dem Service Layer Request Builder.
Sie brauchen ein Portal, eine App oder eine Integration auf Basis des Service Layers? Kontaktieren Sie Kodenova und nennen Sie uns Ihre SAP Business One Version und das, was Sie anbinden möchten.
Quellen
- SAP Learning: Optionen für den Datenabruf per GET-Request (Service Layer)
- SAP Help Portal: Working with SAP Business One Service Layer (Benutzerhandbuch, PDF)
- SAP Help Portal: Working with SAP Business One Service Layer, Benutzerhandbuch für Version 9.1–9.3 für SAP HANA (Dokumentversion 1.15, PDF)
- SAP Help Portal: Service Layer API Reference
- SAP Community: Anmeldung am B1 Service Layer mit Cookies möglich?
- SAP Community: Service Layer und CORS-Header Access-Control
- Apideck: Leitfaden zur Integration der SAP Business One API
Passende Leistungen
Häufige Fragen
Was ist der Unterschied zwischen /b1s/v1 und /b1s/v2?
Der Pfad v1 verwendet OData Version 3, v2 verwendet OData Version 4. SAP betrachtet v4 inzwischen als Hauptprotokoll. Der auffälligste Unterschied in den Antworten ist das Präfix der Annotationen: v2 liefert @odata.nextLink, v1 dagegen odata.nextLink.
Wie lange ist eine Service-Layer-Session gültig?
Standardmäßig läuft die Session nach 30 Minuten Inaktivität ab. Die Login-Antwort liefert den Wert im Feld SessionTimeout. Jeder Aufruf setzt den Timer zurück, eine aktive Integration hält ihre Session also am Leben.
Wie groß ist die Standard-Seitengröße im SAP B1 Service Layer?
20. Deshalb liefert ein GET nur 20 Datensätze. Folgen Sie dem nextLink in der Antwort oder senden Sie den Header Prefer: odata.maxpagesize=100, um für diesen Request größere Seiten zu erhalten. Der serverweite Standard ist die Option PageSize in conf/b1s.conf, der Prefer-Header hat Vorrang davor.
Sollte ich für ein neues Projekt die DI API oder den Service Layer verwenden?
Für neue Web-, Mobile- und Cloud-Integrationen nehmen Sie den Service Layer. Er funktioniert über HTTPS aus jeder Programmiersprache und auf jeder Plattform. Die DI API ist weiterhin sinnvoll für bestehende .NET-Add-ons, die unter Windows neben dem SAP Business One Client laufen.
Warum scheitert meine Browser-App mit einem CORS-Fehler, obwohl es in Postman funktioniert?
Browser blockieren Cross-Origin-Aufrufe, sofern der Server sie nicht ausdrücklich erlaubt. Im Service Layer ist CORS standardmäßig deaktiviert. Aktivieren Sie es in der Service-Layer-Konfiguration und tragen Sie die Origin Ihrer App ein, oder rufen Sie den Service Layer stattdessen aus Ihrem eigenen Backend auf.
Bereit für den Start?
Sagen Sie uns, was Sie brauchen. Sie erhalten eine klare technische Antwort, einen festen Leistungsumfang und einen Preis, in der Regel innerhalb von 24 Stunden.
