Übersicht
NoyesStorage ist so konzipiert, dass er eigenständig mittels App betrieben werden kann, ohne eine Integration zu erfordern. Soll der NoyesStorage automatisiert mit anderen Systemen kommunizieren, steht eine Schnittstelle (engl. Application Programming Interface, API) zur Verfügung. Dieser Abschnitt bietet eine Schnellstartanleitung zur Interaktion mit NoyesStorage über seine API und ermöglicht so die Integration von NoyesStorage in Anwendungsfälle wie:
- die Übergabe von Bestelldaten aus einem Online-Shop an NoyesStorage,
- das automatische Drucken von Rechnungen, wenn Kundenaufträge kommissioniert werden, und
- die Synchronisation des ERP-Systems mit den Lagerbeständen in NoyesStorage.
Die NoyesStorage-API basiert auf REST, verwendet das JSON-Datenformat für Anfragen und Antworten und nutzt gängige HTTP-Methoden (siehe Tabelle unten), um eine konsistente und vorhersehbare Schnittstelle bereitzustellen. Integrationen nutzen die öffentliche REST-API unter /api/v2/… (OpenAPI unter /api/openapi.json), z. B. POST /api/v2/requests für Abruf-, Kommissionier- und Nachschubaufträge.
Die API, ihre Endpunkte und Parameter sind ausführlich online dokumentiert unter
docs.noyes-tech.com.
HTTP Kommunikation in der NoyesStorage API
| Methode | Erklärung |
|---|---|
| POST | Create new resources or send data to NoyesStorage (e.g., submitting a fulfillment request). Example: /api/v2/requests |
| GET | Retrieve data from NoyesStorage (e.g., checking the status of requests). Example: /api/v2/requests |
| PUT | Update existing resources. |
| DELETE | Remove resources from NoyesStorage. |
Standard HTTP Antworten und Status der NoyesStorage API
| Status | Erklärung |
|---|---|
| 200 OK | The request succeeded. |
| 201 Created | A new resource was successfully created. |
| 202 Accepted | The request has been accepted but is being processed asynchronously. |
| 400 Bad Request | The request had a syntax error that could not be resolved. |
| 401 Unauthorized | Authentication is required. |
| 404 Not Found | The requested resource does not exist. |
| 500 Internal Server Error | The server encountered an error. |
Um die Schnittstelle vor böswilligen Nutzern zu schützen, benötigen alle Endpunkte eine Authentifizierung über einen API-Schlüssel. Jede Anfrage muss diesen API-Schlüssel im Header enthalten, andernfalls wird die Anfrage abgelehnt. Zusätzlich werden nur Anfragen akzeptiert, die über HTTPS gesendet werden. Dies stellt sicher, dass die Daten vor der Übertragung verschlüsselt und nur von der NoyesStorage-API entschlüsselt werden können. Anfragen, die über unverschlüsseltes HTTP oder ohne gültigen API-Schlüssel gesendet werden, schlagen fehl.
API-Key verwalten
Hinweis
Nur verfügbar für Admin-Nutzer.
- Öffnen Sie in der NoyesStorage App das Menü
Einstellungenund wählen SieAPI-Key. - Bestehende Schlüssel können Sie dort einsehen und kopieren; erstellen Sie bei Bedarf einen neuen Schlüssel für Integrationen.
- Bewahren Sie den Schlüssel sicher auf und teilen Sie ihn nur mit autorisierten Systemen.

Authentifizierung und API-Key
Produktive Integrationen authentifizieren jede Anfrage mit einem API-Key, der in
der NoyesStorage App ausgegeben wurde. Senden Sie den Schlüssel als Bearer Token
im HTTP-Header Authorization:
Authorization: Bearer <api-key>
Behandeln Sie API-Keys wie Passwörter: nicht in Quellcode einchecken, nur in Secret Stores oder geschützten Umgebungsvariablen speichern und bei Verdacht auf Weitergabe in der App löschen und neu erstellen.
Filtern, Suchen, Sortieren und Pagination
Listen-Endpunkte wie GET /api/v2/skus, GET /api/v2/inventory_view,
GET /api/v2/requests, GET /api/v2/jobs und GET /api/v2/events
unterstützen je nach Ressource Filterparameter, Suche, Sortierung und
Pagination.
Filter verwenden das Schema field__operator=value. Häufige Operatoren sind
__eq für exakte Treffer, __gte und __lte für Bereiche sowie __ilike für
teilweise, groß-/kleinschreibungsunabhängige Treffer. Bei __in wird der
Query-Parameter pro Wert wiederholt:
GET /api/v2/inventory_view?level_id__in=1&level_id__in=2
Kommagetrennte Listen wie ?level_id__in=1,2 werden nicht als mehrere Werte
interpretiert.
Der Parameter search nutzt field:value-Ausdrücke. Die Suche ist teilweise
und groß-/kleinschreibungsunabhängig. Die unterstützten Suchfelder sind
endpunktspezifisch und in der search-Parameterbeschreibung des jeweiligen
Endpunkts aufgeführt:
GET /api/v2/skus?search=id:tee
Mehrere Suchbegriffe können mit den großgeschriebenen Operatoren AND und OR
kombiniert werden. Eine Ebene von Klammern wird unterstützt:
GET /api/v2/inventory_view?search=sku_id:tee AND (box_id:2 OR carrier_id:2)
Mit *:value wird über alle für den jeweiligen Endpunkt unterstützten
Suchfelder gesucht:
GET /api/v2/inventory_view?search=*:tee
Der Parameter sort_by akzeptiert kommagetrennte Feldnamen. Ein Feld ohne
Vorzeichen oder mit + sortiert aufsteigend, - sortiert absteigend:
GET /api/v2/requests?sort_by=created_at,-updated_at
Pagination verwendet page und size. Die Seitennummerierung beginnt bei
page=1; ohne abweichende Anfrage liefert die API standardmäßig page=1 und
size=50:
GET /api/v2/skus?page=1&size=50
Funktionsweise der Anfrageausführung
Das NoyesStorage-System arbeitet mit einer Warteschlange. Ein spezifisches Arbeitspaket kann durch das Senden einer Anfrage angefordert werden. Intern wird diese Anfrage in mehrere Jobs aufgeteilt, die nacheinander ausgeführt werden. Sowohl Anfragen als auch Jobs folgen einem vordefinierten Lebenszyklus, der durch ihren Status ausgedrückt wird.
Requests
Anfragen (engl. Requests) können an den NoyesStorage gesendet werden. Diese Anfrage wird dann intern in eine Reihe von Jobs übersetzt. Die folgende Tabelle listet die verfügbaren Anfragen und deren Beschreibungen auf. Über die API können Fetch, Fulfillment und Replenishment Anfragen an den NoyesStorage gesendet werden, um die Aus- und Einlagerung automatisiert abzuwickeln.
Requests im NoyesStorage
| Request | Description |
|---|---|
| FULFILLMENT | A Logistic Request consisting of Pick-Entities (ProductEntity). |
| REPLENISHMENT | A Logistic Request consisting of Refill-Entities (ProductEntity). |
| FETCH | A Logistic Request consisting of Fetch-Carrier-Entities. |
| RFID_MAINTENANCE | A System Control Request to write and/or check RFID Tags on a specified Level. |
| ONBOARD | A System Control Request to add a Noyes Bot to any Level. |
| OFFBOARD | A System Control Request to remove a specific Noyes Bot from a Level. |
| PAUSE | A System Control Request to remove a specific Noyes Bot from a Level. |
| RESUME | A System Control Request to allow all Bots to get new commands after a PAUSE Request. |
| CHARGING | A System Control Request to move a Noyes Bot to a Charging Station. |
| UNCHARGING | A System Control Request to remove a Noyes Bot from a Charging Station. |
| UPDATE_CONTENT_CODE | An Inventory Update Request to update the content code of a carrier. |
| RECHARGE | A System internal Request sent from the Charging Manager to the Store Orchestrator. |
| LEVEL_INITIALIZATION | An internal Request Type used for initializing each Level of a Storage by connecting to the bots, updating the Database, and clearing the Highway. |
Jobs
Jobs sind die einzelnen Aufgaben, die im System ausgeführt werden. Jede Anfrage kann mehrere Jobs generieren, die jeweils einem vordefinierten Lebenszyklus folgen. Die folgende Tabelle liefert einen Überblick über die im NoyesStorage verwendeten Jobs.
Jobs im NoyesStorage
| Job | Description |
|---|---|
| BUFFERING | Moves a Carrier to a Buffer Space on the same Level. |
| RETRIEVING | Moves a Carrier to a Balcony on the same Level. |
| STORING | Moves a Carrier from the Balcony. |
| PICKING | Lets the user pick a given amount out of a Box. |
| PICKING_ALERT | Lets the user handle fulfillment quantities outside the NoyesStorage when the external picking feature is enabled. |
| REFILLING | Lets the user refill a given amount into a Box. |
| CHECKING | Lets the user check a given Carrier Label. |
| PACKING | Lets the user check the summary of a Request after all Entities have been worked on. |
| ONBOARDING | Lets the user onboard a Bot to any level. |
| ONBOARDING_BD | Adds a Bot to the Database and informs all Brain Components about it. |
| OFFBOARDING | Moves a Bot to the Balcony. |
| OFFBOARDING_BD | Lets the user remove a Bot from the Balcony. |
| OFFBOARDING_BC | Moves a Bot to the Balcony. |
| PAUSE | Ensures no Bot gets new commands. |
| RESUME | Allows all Bots to get new commands after a PAUSE. |
| MOVING_BOT | Moves a Bot to a target location/rotation/lifting state. |
| CONNECT_TO_BOTS | Establishes a connection to all Bots that are in the Database. |
| REFRESH_LEVEL | Updates the digital twin in the Bot Coordinator to the Database. |
| UNCHARGE_ALL | Removes all Bots from the Charging Station. |
| CLEAR_AUTOBAHN | Removes all Carriers from the Highway. |
| CHARGING | Moves a given bot to a Charging Station. |
| UNCHARGING | Removes a given bot from the Charging Station. |
| CHECK_RFID_TAG | Checks the values written on an RFID Tag at the given Location and Rotation. |
| WRITE_RFID_TAG | Writes values to an RFID Tag at the given Location and Rotation. |
| CHANGE_RFID_WRITING_MODE | Changes whether or not a bot should ignore the values on an RFID Tag while driving. |
Hinweis zu PICKING_ALERT
PICKING_ALERT erscheint nur, wenn die externe Kommissionierung für fehlende oder außerhalb des NoyesStorage zu bearbeitende Fulfillment-Mengen aktiviert ist. In diesem Fall können angenommene Fulfillment-Requests strukturierte Warnhinweise zu fehlenden oder extern zu bearbeitenden Mengen enthalten, und nachgelagerte Job-Daten können PICKING_ALERT-Jobs enthalten.
Job States
Jeder Job durchläuft einen Lebenszyklus, der durch seinen Status definiert ist. Die folgenden Statusübergänge sind in der Tabelle dargestellt.
Job States in the NoyesStorage System
| State | Description |
|---|---|
| ACCEPTED | Job is ACCEPTED. These transitions are allowed: EXECUTING, ERROR, CANCELLING, ABORTED. |
| EXECUTING | Job is EXECUTING and waiting to be triggered. These transitions are allowed: ERROR, CANCELLING, ABORTED. |
| SUCCEEDED | Job is SUCCEEDED. No transitions are allowed. This is a final state. |
| CANCELLING | Job is CANCELLING. A user requested this job to be cancelled. These transitions are allowed: CANCELLED, ABORTED. |
| CANCELLED | Job is CANCELLED. A user requested this job to be cancelled. The CANCELLING was successful. This is a final state. |
| ABORTED | Job is ABORTED due to a system problem. The system will try to recover from this. This is a final state. |
Quickstart: Ein einfaches Integrationsbeispiel
Dieser Abschnitt bietet eine einfache Anleitung zur Nutzung der API und konzentriert sich auf zentrale Interaktionen über Endpunkte. Die Beispiele werden in Python demonstriert, jedoch können auch andere Sprachen wie JavaScript, Java oder C# verwendet werden.
Anforderungen
- Python 3.7+ (oder eine ähnliche Programmiersprache, die HTTP-Requests senden kann).
- API Keys für die Authentifizierung an der NoyesStorage API.
Im Folgenden finden Sie ein Beispiel in Python, das zeigt, wie die Integration mit der API von NoyesStorage – insbesondere dem Retrieval-Endpunkt – erfolgen kann. Nach der vollständigen Implementierung wird jeder Abschnitt im Detail erläutert.
import json
from typing import Dict
import time
import uuid
import requests
import logging
from threading import Timer
"""
Examples of Noyes B2B payload
v1/core/retrieval/{request_id}
{
"priority": 10,
"desired_time": "2024-10-08T07:50:36.064257",
"source": "",
"request_type": "FULFILLMENT",
"external_id": "string",
"total_entity_count": 0,
"entities": [
{
"entity_type": "Product",
"sku_id": "string",
"quantity": 0
},
{
"entity_type": "Carrier",
"carrier_id": "string",
"content_codes": [
0
]
}
]
}
"""
class SimpleERPIntegration():
""" An example class that sends requests to the Noyes B2B API and
repeatedly checks the status of those requests until they are finished.
"""
REQ_UPDATE_PERIOD = 10 # seconds
def __init__(self, store_keys: Dict, b2b_url: str) -> None:
self._request_dicts: Dict[uuid.UUID, Dict[str]] = {}
self._store_keys = store_keys
self.b2b_url = b2b_url
def send_request(self, payload: Dict) -> str:
headers = self._store_keys
logging.info(f'sending request {payload["external_id"]} via b2b')
send_time = time.time()
rsp = requests.post(self.b2b_url + 'core/retrieval', headers=headers, data=json.dumps(payload), timeout=1)
assert rsp.status_code == 202, f'got bad response {rsp} {rsp.reason} {rsp.text}'
logging.info(f'sent request {payload["external_id"]} request_id={rsp.json()["request_id"]} via b2b in {time.time()-send_time:.2f}')
self.subscribe_to_updates(rsp.json()['request_id'])
return rsp.json()['request_id']
def subscribe_to_updates(self, request_id: uuid.UUID):
"""Creates a timer for this request which repeatedly asks the B2B for updates.
self._check_request_callback(request_id) will be called at a rate of self.REQ_UPDATE_PERIOD.
"""
self._request_dicts[request_id] = {}
self._request_dicts[request_id]['timer'] = Timer(interval=self.REQ_UPDATE_PERIOD, function=lambda: self._check_request_callback(request_id))
self._request_dicts[request_id]['last_request_update'] = ''
self._request_dicts[request_id]['last_job_update'] = '[]'
def unsubscribe_from_updates(self, request_id: uuid.UUID):
"""
Cancels the timer created for this request_id in subscribe_to_updates.
"""
if request_id in self._request_dicts.keys():
if 'timer' in self._request_dicts[request_id].keys():
self._request_dicts[request_id]['timer'].cancel()
self._request_dicts[request_id] = None
del self._request_dicts[request_id]
def _check_request_callback(self, request_id: uuid.UUID):
"""
Calls the Noyes B2B endpoint for the status of the specified request.
Processes the response in _process_request_updates.
Unsubscribes from request updates if the request is completed.
"""
url = self.b2b_url + f'core/retrieval/{str(request_id)}'
try:
rsp = requests.get(url, headers=self._store_keys, timeout=0.9*self.REQ_UPDATE_PERIOD)
assert rsp.status_code == 200, f'got bad response {rsp} {rsp.reason} {rsp.text}'
except requests.exceptions.ReadTimeout as e:
logging.warn(f'got error while checking requests {e}.')
return
request_update = json.loads(rsp.text)
self._process_request_updates(request_update)
if request_update['status'] in ['FAILED', 'SUCCEEDED', 'ABORTED']:
self.unsubscribe_from_updates(request_id)
def _process_request_updates(self, request_update):
"""
Determines when a subscribed request has updated information and provides a placeholder
for user-defined request update callbacks.
"""
# Parse out different portions of the message.
request_id = request_update['request_id']
request_updates = {k: v for k, v in request_update.items() if k not in ['jobs', 'content_code_updates', 'last_updated']}
dump = json.dumps(request_updates)
if request_id not in self._request_dicts.keys():
self.subscribe_to_updates(request_id=request_id)
logging.warn(f'got request update for {request_id} but it wasn\'t expected, tracking now but that\'s weird')
if dump != self._request_dicts[request_id]['last_request_update']:
pass # Place your request_update callback here.
self._request_dicts[request_id]['last_request_update'] = dump
Erläuterungen zum Code
- Klasseninitialisierung
Die KlasseSimpleERPIntegrationverwaltet alle Interaktionen mit der NoyesStorage-API. Sie wird mit folgenden Parametern initialisiert: store_keys: Ein Dictionary, das die für die Authentifizierung erforderlichen Schlüssel enthält (z. B.store_id,tenant_id,api_key).b2b_url: Die Basis-URL für die API.
Beispiel:
erp_integration = SimpleERPIntegration(
store_keys={'store_id': 'YOUR_STORE_ID',
'tenant_id': 'YOUR_TENANT_ID',
'api_key': 'YOUR_API_KEY'},
b2b_url='https://demo-b2b.noyes-tech.com/'
)
- Anfrage senden (
send_request)
Diese Methode wird verwendet, um eine neue Entnahmeanfrage an das NoyesStorage-System zu senden. - Verwendeter Endpunkt:
POST /core/retrieval - Die Methode erstellt ein Payload und sendet eine
POST-Anfrage an den Endpunkt/core/retrieval. - Bei Erfolg gibt die API den Statuscode
202 Acceptedund einerequest_idzurück.
Wichtige Punkte:
- Der Endpunkt /core/retrieval ist die Hauptschnittstelle zur Erstellung neuer Entnahmeaufträge.
- Die zurückgegebene request_id ist wichtig, um den Status der Anfrage zu verfolgen.
-
Abonnieren von Updates (
subscribe_to_updates)
Nach dem Abschicken einer Anfrage wird eine regelmäßige Abfrage eingerichtet (alle 10 Sekunden), um den Status der Anfrage und der zugehörigen Jobs zu verfolgen. -
Überprüfung des Anfragestatus (
_check_request_callback)
Diese Methode prüft den Status der Anfrage, indem sie eineGET-Anfrage an den Endpunkt/core/retrieval/{request_id}sendet. Die Antwort enthält den Fortschritt (z. B.SUCCEEDED,FAILEDoderABORTED). -
Verarbeitung von Updates (
_process_request_updates)
Die von der API empfangenen Updates werden in dieser Methode verarbeitet. Sie protokolliert den Status und löst je nach Zustand der Anfrage erforderliche Callbacks aus. Sobald der Job abgeschlossen ist (egal obSUCCEEDED,FAILEDoderABORTED), beendet das System die Abfrage der API nach weiteren Updates.
Mögliche Erweiterungen
Dieses einfache Integrations-Setup dient als Vorlage zur Nutzung der NoyesStorage-API und kann erweitert werden, um die Integration von NoyesStorage in Ihre Produktionsumgebung anzupassen.
Im Folgenden sind einige Beispiele aufgeführt, wie Kunden die API nutzen, um NoyesStorage in ihre Prozesse zu integrieren:
Drucken von Rechnungen für kommissionierte Anfragen
Eine Erweiterung, die von einem unserer Kunden genutzt wird, umfasst das automatische Erstellen und Drucken von Rechnungen für Anfragen, die aktuell kommissioniert werden. Diese Rechnungen werden vor dem Verpacken in die Kundenbestellung gelegt.
Im obigen Beispiel kann dies erreicht werden, indem die Methode _process_request_updates so modifiziert wird, dass der Rechnungsdruck ausgelöst wird, sobald eine Anfrage in den Status EXECUTING übergeht.
def _process_request_updates(self, request_update):
request_id = request_update['request_id']
request_status = request_update['status']
if request_status == "EXECUTING":
self.print_invoice_for_request(request_update)
logging.info(f'Update for request {request_id}: {request_status}')
def print_invoice_for_request(self, request_update):
invoice_data = f"Invoice for request {request_update['request_id']}\n"
invoice_data += f"Items: {request_update['entities']}\n"
invoice_data += f"Total Quantity: {sum(item['quantity'] for item in request_update['entities'])}\n"
logging.info(f"Printing invoice:\n{invoice_data}")
Mit dieser Erweiterung wird eine Rechnung automatisch erstellt und protokolliert (oder gedruckt), sobald eine Anfrage in den Status EXECUTING übergeht.
Automatische Bestätigung eines Picks über einen Scanner
Eine zusätzliche Erweiterung umfasst die Verwendung eines Handscanners zur automatischen Bestätigung von Picks. Wenn der Scanner den Barcode eines Artikels liest, kann die Integration den Pick für die entsprechende Anfrage am NoyesStorage automatisch bestätigen.
Hierfür muss die Methode _process_request_updates angepasst werden, um zu erkennen, wann ein Artikel gescannt wurde. Anschließend kann eine Anfrage zur Bestätigung des Picks über den entsprechenden Endpunkt gesendet werden (z. B. POST /v1/core/requests/{request_id}/jobs/{job_id}/trigger).
def _process_request_updates(self, request_update):
request_id = request_update['request_id']
request_status = request_update['status']
if request_status == "EXECUTING" and self.is_item_scanned(request_update):
self.confirm_pick(request_id)
logging.info(f'Update for request {request_id}: {request_status}')
def is_item_scanned(self, request_update):
scanned_item_sku = "scanned_item_sku_example" # Example from the scanner
return any(item['sku_id'] == scanned_item_sku for item in request_update['entities'])
def confirm_pick(self, request_id):
url = self.b2b_url + f'/core/retrieval/{str(request_id)}/confirm'
try:
rsp = requests.put(url, headers=self._store_keys)
assert rsp.status_code == 200, f"Failed to confirm pick: {rsp.status_code}"
logging.info(f'Pick confirmed for request {request_id}')
except requests.exceptions.RequestException as e:
logging.error(f'Error confirming pick: {e}')
Weitere Anwendungsfälle für eine API-Integration
- Bestandsanpassung: Automatische Aktualisierung der Bestandsführungssysteme basierend auf den abgeschlossenen Anfragen am NoyesStorage.
- Verfolgung von Bestellungen: Synchronisieren der NoyesStorage-Anfragen mit einem externen Bestellverfolgungssystem für Echtzeit-Updates.
- Benachrichtigungssystem: Auslösen von SMS- oder E-Mail-Benachrichtigungen, wenn kritische Jobs abgeschlossen oder verzögert werden.
- Automatisierte Nachbestellung: Überwachen der Lagerbestände und automatisches Erstellen von Nachschubanfragen basierend auf festgelegten Schwellenwerten.
Diese Beispiele zeigen, wie sich die Integrationsvorlage an spezifische Anforderungen anpassen lässt. Die NoyesStorage-API bietet die nötige Flexibilität, um Prozesse über Systemgrenzen hinweg zu automatisieren und zu optimieren. Dieses Beispiel verdeutlicht, wie die Integration der NoyesStorage-API eingerichtet, Anfragen gesendet und deren Status verfolgt werden kann. Die Einrichtung kann erweitert werden, um Prozesse wie die Rechnungserstellung, automatische Bestätigungen oder Nachbestellungen zu individualisieren.