Zum Inhalt springen

Externer Katalog API

Der Externe-Katalog-API bietet eine schlanke, nur lesende Sicht auf die Stammdaten eines APRO-Standorts — Artikel, Warengruppen und Spots (Tische, Zimmer, Abholpunkte) — zur Verwendung in White-Label- Apps, Kiosks, externen Speisekarten und Partnerintegrationen.

Die drei Endpunkte teilen sich dieselben Konventionen: Scope per Standort, verschachtelte DTO-Struktur, Filterung über Query-Parameter und Paginierung.

  • Host: my.apro.at
  • Auth: API-Key im x-api-key
  • Content type: application/json

Endpunkte

MethodeURLLiefert
GET/api/v1/external/productPaginierte Liste von Product
GET/api/v1/external/product-categoryPaginierte Liste von ProductCategory
GET/api/v1/external/spotPaginierte Liste von Spot

Gemeinsame Konventionen

Standort-Scope

Jede Anfrage muss genau einen Standort adressieren. Geben Sie einen der folgenden Werte mit:

  1. locationId — numerische ID
  2. locationShortCode — Kurz-Identifier (z. B. vienna-main)

Lässt sich aus keinem von beiden ein Standort auflösen, wird die Anfrage mit 400 Bad Request abgelehnt.

Paginierung

Query-ParameterDefaultBeschreibung
skip0Anzahl der zu überspringenden Datensätze.
take50Seitengröße. Limit 500.

Jede Antwort steckt in einem Paged-Envelope:

{
"page": { "offset": 0, "limit": 50, "total": 1234 },
"data": [ /* ... */ ]
}
  • offset und limit spiegeln die Anfrage.
  • total ist die Gesamtanzahl aller passenden Datensätze über alle Seiten — Grundlage für skip/take-Schleifen.

Filterung

Filter werden als bracketed Query-Parameter unter dem Schlüssel filter übergeben — filter['<name>']=<value>. Schlichtes ?<name>=<value> wird ignoriert. Paginierung (skip, take) bleibt als gewöhnliche Query-Parameter.

Listen-Filter (productIds, gtins, externalSpotIds, …) werden kommagetrennt übergeben. Literale Kommas mit \, escapen.

GET /api/v1/external/product?filter['locationShortCode']=vienna-main
&filter['productNrs']=A-100,A-200
&filter['isVisible']=true
&filter['search']=schnitzel
&skip=0&take=100

Hinweise:

  • Der Klammer-Key ist case-insensitivefilter['LocationShortCode'] und filter['locationShortCode'] sind äquivalent.
  • filter["…"] (doppelte Anführungszeichen, URL-kodiert als %22) funktioniert ebenfalls.
  • Unbekannte Filterparameter werden ignoriert.

Artikel

GET https://my.apro.at/api/v1/external/product

Query-Parameter

ParameterTypBeschreibung
locationIdint?Pflicht (oder locationShortCode).
locationShortCodestringPflicht (oder locationId).
cashboxIdint?Auf eine Kassa einschränken. Muss zum Standort gehören.
searchstringVolltextsuche über Name, externer Name, productNr, GTIN und externe Artikel-ID.
productIdsint[]Filter nach APRO-Artikel-ID.
productNrsstring[]Filter nach productNr (der Wert, der bei External-Order-Einträgen zurückgegeben wird).
gtinsstring[]Filter nach GTIN (Barcode).
externalProductIdsstring[]Filter nach externer Artikel-ID (Drittsystem-Referenz).
isVisiblebool?Filter nach Sichtbarkeit.
isLockedbool?Filter nach Sperr-Flag.

Product

FeldTypBeschreibung
idintAPRO-Cloud-Artikel-ID.
productNrstringIdentifier, mit dem die Bestell-Pipeline diesen Artikel auflöst. Diesen Wert direkt als productNr des Eintrags beim Anlegen einer Bestellung via Externe-Bestellung-API verwenden. Basiert auf der POS-Artikelnummer.
gtinstringBarcode / GTIN.
externalProductIdstringPOS-Artikel-ID — die im lokalen Kassensystem vergebene ID. Informativ; nicht der Wert, gegen den die External-Order-productNr aufgelöst wird.
namestringAnzeigename.
externalNamestringName für externe Oberflächen — Fallback auf name, wenn leer.
subtitlestringKurz-Untertitel.
descriptionstringAusführliche Beschreibung.
pricingPricingPreis- und Steuerinfo.
cashboxProductCashboxKassa / Eltern-Artikel / Produktionsplatz.
mediaMediaBild-URLs.
flagsFlagsSichtbarkeit, Sperre, Review-Status.
nutritionNutritionMenge, Kalorien, Allergene, Altersgrenze.
productGroupExternalIdsstring[]Externe IDs der Warengruppen, denen der Artikel angehört.
localizationsLocalization[]Gepflegte Übersetzungen der Artikeltexte und des Bildes, ein Eintrag je Sprache. Leer, wenn keine vorhanden sind.

Pricing

FeldTypBeschreibung
defaultPricedouble?Standardpreis.
referencePricedouble?Referenz-/Streichpreis.
taxRateIdint?APRO-Steuersatz-ID.
allowClientPriceboolOb Clients den Preis überschreiben dürfen.

ProductCashbox

FeldTypBeschreibung
cashboxIdintZugehörige Kassa.
productionPlaceIdint?Küche / Bar, in der zubereitet wird.
parentProductIdint?Bei Varianten / Template-Ableitungen gesetzt.

Media

FeldTypBeschreibung
imageUrlstringÖffentliche Bild-URL.

Flags

FeldTypBeschreibung
isVisibleboolSichtbar in Storefronts.
isLockedboolGegen automatische Sync-Änderungen gesperrt.
queuedForReviewboolWartet auf manuelles Review.

Nutrition

FeldTypBeschreibung
amountdouble?Nettomenge.
amountUnitstringEinheit, z. B. g, ml.
caloriesdouble?Kalorien pro amount.
allergensint (Flags)Bitflag der deklarierten Allergene.
minimumAgeintMindestkäuferalter (0, wenn keines).
ingredientsIngredient[]Deklarierte Zutatenliste. Leer-Array, wenn nicht gepflegt.
nutritionalValuesNutritionalValue[]Strukturierte Nährwerte. Leer-Array, wenn nicht gepflegt.

Ingredient

FeldTypBeschreibung
namestringZutaten-Bezeichnung (z. B. Lamm, Weizenmehl).

NutritionalValue

FeldTypBeschreibung
namestringWert-Bezeichnung (z. B. Gewicht, Kalorien, Fett).
unitstringEinheit, freier Text (z. B. g, ml, kcal).
valuestringNumerischer Wert, als String serialisiert, um das Quellformat zu erhalten. Client-seitig parsen, falls nötig.
belongsToint?Optionale Referenz auf eine Unterkomponente / Variante (null, wenn der Wert für das Gesamtprodukt gilt).

Localization

FeldTypBeschreibung
localestringSprach- / Culture-Schlüssel der Übersetzung (z. B. en, it, de-DE).
namestringÜbersetzter Anzeigename.
subtitlestringÜbersetzter Untertitel.
descriptionstringÜbersetzte ausführliche Beschreibung.
imageUrlstringSprachspezifisches Bild, sofern gepflegt.

name, subtitle, description und media.imageUrl auf oberster Ebene enthalten weiterhin die Standardwerte (Deutsch)localizations ersetzt sie nicht, sondern ergänzt nur die vorhandenen Übersetzungen. Einträge ohne Sprachschlüssel werden weggelassen, und einzelne Felder eines Eintrags können null sein, wenn genau dieser Text nicht übersetzt wurde. Zur Anzeige die gewünschte Sprache in localizations suchen und auf das Feld der obersten Ebene zurückfallen.

Beispiel

GET /api/v1/external/product?filter['locationShortCode']=vienna-main&filter['isVisible']=true&take=2
{
"page": { "offset": 0, "limit": 2, "total": 1247 },
"data": [
{
"id": 9001,
"productNr": "A-100",
"gtin": "9001234567890",
"externalProductId": "wiener-schnitzel",
"name": "Wiener Schnitzel",
"externalName": "Wiener Schnitzel",
"subtitle": "Mit Erdäpfelsalat",
"description": "Klassisches Wiener Schnitzel vom Kalb.",
"pricing": {
"defaultPrice": 22.9,
"referencePrice": null,
"taxRateId": 3,
"allowClientPrice": false
},
"cashbox": { "cashboxId": 17, "productionPlaceId": 2, "parentProductId": null },
"media": { "imageUrl": "https://cdn.smorder.at/locations/7236/p/9001.jpg" },
"flags": { "isVisible": true, "isLocked": false, "queuedForReview": false },
"nutrition": {
"amount": 250,
"amountUnit": "g",
"calories": 720,
"allergens": 67,
"minimumAge": 0,
"ingredients": [{ "name": "Kalb" }, { "name": "Weizenmehl" }, { "name": "Ei" }],
"nutritionalValues": [
{ "name": "Gewicht", "unit": "g", "value": "250", "belongsTo": null },
{ "name": "Fett", "unit": "g", "value": "30", "belongsTo": null }
]
},
"productGroupExternalIds": ["mains", "austrian"],
"localizations": [
{
"locale": "en",
"name": "Viennese Schnitzel",
"subtitle": "With potato salad",
"description": "Classic Viennese veal schnitzel.",
"imageUrl": null
},
{
"locale": "it",
"name": "Cotoletta alla viennese",
"subtitle": null,
"description": null,
"imageUrl": null
}
]
}
]
}

Download: external-products-response.json

Warengruppen

GET https://my.apro.at/api/v1/external/product-category

Warengruppen entsprechen den APRO Product Groups — die kassenbezogene Gruppierung, die Reports, Steuersätze und Druckreihenfolge steuert. Das Feld productGroupExternalIds auf einem Artikel referenziert genau die externalId, die hier zurückkommt — damit lassen sich beide Antworten ohne zusätzliche Lookups verbinden.

Query-Parameter

ParameterTypBeschreibung
locationIdint?Pflicht (oder locationShortCode).
locationShortCodestringPflicht (oder locationId).
cashboxIdint?Auf eine Kassa einschränken. Muss zum Standort gehören.
searchstringVolltextsuche über Name, externer Name und externe ID.
categoryIdsint[]Filter nach APRO-Warengruppen-ID.
externalIdsstring[]Filter nach externer ID.

ProductCategory

FeldTypBeschreibung
idintAPRO-Cloud-Warengruppen-ID.
externalIdstringPOS-Warengruppen-ID — die im lokalen Kassensystem vergebene ID.
namestringAnzeigename.
externalNamestringName für externe Oberflächen.
cashboxCategoryCashboxKassa / Lieferant.
taxCategoryTaxSteuersatz.
localizationsCategoryLocalization[]Gepflegte Übersetzungen der Warengruppentexte und des Bildes, ein Eintrag je Sprache. Leer, wenn keine vorhanden sind.

CategoryCashbox

FeldTypBeschreibung
cashboxIdint?Zugehörige Kassa.
supplierIdint?Standard-Lieferant für Artikel der Warengruppe.

CategoryTax

FeldTypBeschreibung
taxRateIdint?Standard-Steuersatz für Artikel der Warengruppe.
vatdoubleUSt-Prozentsatz (z. B. 20 für 20 %).

CategoryLocalization

FeldTypBeschreibung
localestringSprach- / Culture-Schlüssel der Übersetzung (z. B. en, it, de-DE).
namestringÜbersetzter Anzeigename.
descriptionstringÜbersetzte Beschreibung.
imageUrlstringSprachspezifisches Bild, sofern gepflegt.

Es gelten dieselben Regeln wie bei Artikeln: name auf oberster Ebene bleibt der deutsche Standardwert, localizations ergänzt nur das, was vorhanden ist.

Beispiel

GET /api/v1/external/product-category?filter['locationShortCode']=vienna-main
{
"page": { "offset": 0, "limit": 50, "total": 14 },
"data": [
{
"id": 41,
"externalId": "mains",
"name": "Hauptspeisen",
"externalName": "Mains",
"cashbox": { "cashboxId": 17, "supplierId": null },
"tax": { "taxRateId": 3, "vat": 10 },
"localizations": [
{ "locale": "en", "name": "Mains", "description": null, "imageUrl": null },
{ "locale": "it", "name": "Secondi", "description": null, "imageUrl": null }
]
},
{
"id": 42,
"externalId": "drinks-alcoholic",
"name": "Getränke (Alkohol)",
"externalName": "Alcoholic drinks",
"cashbox": { "cashboxId": 17, "supplierId": null },
"tax": { "taxRateId": 1, "vat": 20 },
"localizations": []
}
]
}

Download: external-product-categories-response.json

Spots

GET https://my.apro.at/api/v1/external/spot

Spots sind die physischen / logischen Ziele, an die ein Gast bestellen kann: Tische, Zimmer, Abholtheken, Lieferzonen, Drive-in.

Query-Parameter

ParameterTypBeschreibung
locationIdint?Pflicht (oder locationShortCode).
locationShortCodestringPflicht (oder locationId).
areaIdint?Auf einen Bereich / ein Zimmer einschränken.
searchstringVolltextsuche über Name, Alternativname, externe Spot-ID und QR-Code.
spotIdsint[]Filter nach APRO-Spot-ID.
externalSpotIdsstring[]Filter nach externer Spot-ID.
typeenumSpot / Table / Room / Pickup / Delivery / …
visibleInAppbool?Filter nach App-Sichtbarkeit.
supportsTableOrderbool?Filter nach Tischbestell-Fähigkeit.

Spot

FeldTypBeschreibung
idintAPRO-Cloud-Spot-ID.
externalSpotIdstringPOS-Spot-ID — die im lokalen Kassensystem vergebene ID.
numberint?Numerisches Label (z. B. Tischnummer).
namestringAnzeigename.
alternativeNamestringSekundärer Name für manche Oberflächen.
descriptionstringFreitext-Beschreibung.
qrCodestringQR-Code-Payload des Spots.
typeenumSpot-Typ.
capacityint?Sitzplätze (Tisch) / Maximalgäste (Zimmer).
locationSpotLocationZugehöriger Standort.
areaSpotAreaZugehöriger Bereich, falls vorhanden.
geoSpotGeoGeokoordinaten. null, wenn beide Werte fehlen.
flagsSpotFlagsFähigkeits-Flags.

SpotLocation

FeldTypBeschreibung
locationIdintNumerische Standort-ID.
locationShortCodestringKurzcode für Cross-API-Joins.

SpotArea

FeldTypBeschreibung
areaIdintAPRO-Bereichs-ID.
namestringAnzeigename des Bereichs.
externalAreaIdstringExterne Referenz des Bereichs.

SpotGeo

FeldTypBeschreibung
latitudedouble?WGS84-Breite.
longitudedouble?WGS84-Länge.

SpotFlags

FeldTypBeschreibung
visibleInAppboolSichtbar in Storefronts.
supportsTableOrderboolAkzeptiert Tischbestellungen.
supportsCashPaymentboolAkzeptiert Bargeld.
defaultToTakeAwayboolDefault-Bestellmodus ist Takeaway.
displayAsButtonboolAls Button statt Listeneintrag darstellen.

Beispiel

GET /api/v1/external/spot?filter['locationShortCode']=vienna-main&filter['type']=Table&filter['visibleInApp']=true&take=2
{
"page": { "offset": 0, "limit": 2, "total": 38 },
"data": [
{
"id": 1501,
"externalSpotId": "table-01",
"number": 1,
"name": "Tisch 1",
"alternativeName": null,
"description": null,
"qrCode": "QR-01",
"type": "Table",
"capacity": 4,
"location": { "locationId": 7236, "locationShortCode": "vienna-main" },
"area": { "areaId": 11, "name": "Gastgarten", "externalAreaId": "garden" },
"geo": null,
"flags": {
"visibleInApp": true,
"supportsTableOrder": true,
"supportsCashPayment": true,
"defaultToTakeAway": false,
"displayAsButton": false
}
}
]
}

Download: external-spots-response.json

Cross-API-Joins

Die drei Endpunkte sind so geschnitten, dass sie sich client-seitig verbinden lassen:

  • product.productNrproductNr (Entry / Addon) der Externe-Bestellung-API — case-insensitive, gematcht ausschließlich gegen Artikel auf der Primär-Kassa des Standorts. Wenn productNr nicht auflöst, greift im Bestell-Pipeline ein Fallback über den name-Wert des Entries gegen product.name.
  • product.productGroupExternalIds[i]productCategory.externalId
  • product.cashbox.cashboxId / productCategory.cashbox.cashboxIdcashboxId-Filter auf beiden Endpunkten
  • spot.location.locationShortCodelocationShortCode-Filter überall
  • spot.idSpotID der External-Order-API — die id des Spots direkt zurückgeben, um eine Bestellung an denselben Spot zurückzurouten, von dem sie gelesen wurde. spot.qrCode entspricht dem QRCode-Feld der External-Order-API.

Fehler

StatusBedeutung
400 Bad RequestWeder locationId noch locationShortCode lieferten einen gültigen Standort.
401 Unauthorizedx-api-key fehlt oder ist ungültig.
403 ForbiddenKey nicht für den Standort autorisiert.
429 Too Many RequestsRate-Limit — mit Jitter zurücknehmen und erneut versuchen.