APRO Dokumentation · Stand 6. November 2026

Externer Katalog API

Zuletzt aktualisiert: 5. November 2026ca. 7 Min. Lesezeit

PDF erzeugen

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
Tipp: In Postman ausprobieren
Lade die fertige Collection herunter:
apro-external-catalog.postman_collection.json.
In Postman importieren, im Variables-Tab der Collection apiKey und
locationShortCode setzen, dann Send drücken.
Achtung: Filter verwenden eine Bracket-Syntax
Alle Filterfelder — inklusive des Pflichtfeldes locationId /
locationShortCode — müssen als filter['<name>']=<value> übergeben
werden, nicht als gewöhnliche Query-Parameter. Schlichtes
?locationShortCode=… wird stillschweigend ignoriert; die Anfrage
schlägt dann mit „location not resolved" fehl. Details unter
Filterung.

Endpunkte

Methode

URL

Liefert

GET

/api/v1/external/product

Paginierte Liste von Product

GET

/api/v1/external/product-category

Paginierte Liste von ProductCategory

GET

/api/v1/external/spot

Paginierte 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-Parameter

Default

Beschreibung

skip

0

Anzahl der zu überspringenden Datensätze.

take

50

Seitengröß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-insensitive — filter['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

Parameter

Typ

Beschreibung

locationId

int?

Pflicht (oder locationShortCode).

locationShortCode

string

Pflicht (oder locationId).

cashboxId

int?

Auf eine Kassa einschränken. Muss zum Standort gehören.

search

string

Volltextsuche über Name, externer Name, productNr, GTIN und externe Artikel-ID.

productIds

int[]

Filter nach APRO-Artikel-ID.

productNrs

string[]

Filter nach productNr (der Wert, der bei External-Order-Einträgen zurückgegeben wird).

gtins

string[]

Filter nach GTIN (Barcode).

externalProductIds

string[]

Filter nach externer Artikel-ID (Drittsystem-Referenz).

isVisible

bool?

Filter nach Sichtbarkeit.

isLocked

bool?

Filter nach Sperr-Flag.

Product

Feld

Typ

Beschreibung

id

int

APRO-Cloud-Artikel-ID.

productNr

string

Identifier, 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.

gtin

string

Barcode / GTIN.

externalProductId

string

POS-Artikel-ID — die im lokalen Kassensystem vergebene ID. Informativ; nicht der Wert, gegen den die External-Order-productNr aufgelöst wird.

name

string

Anzeigename.

externalName

string

Name für externe Oberflächen — Fallback auf name, wenn leer.

subtitle

string

Kurz-Untertitel.

description

string

Ausführliche Beschreibung.

pricing

Pricing

Preis- und Steuerinfo.

cashbox

ProductCashbox

Kassa / Eltern-Artikel / Produktionsplatz.

media

Media

Bild-URLs.

flags

Flags

Sichtbarkeit, Sperre, Review-Status.

nutrition

Nutrition

Menge, Kalorien, Allergene, Altersgrenze.

productGroupExternalIds

string[]

Externe IDs der Warengruppen, denen der Artikel angehört.

localizations

Localization[]

Gepflegte Übersetzungen der Artikeltexte und des Bildes, ein Eintrag je Sprache. Leer, wenn keine vorhanden sind.

Pricing

Feld

Typ

Beschreibung

defaultPrice

double?

Standardpreis.

referencePrice

double?

Referenz-/Streichpreis.

taxRateId

int?

APRO-Steuersatz-ID.

allowClientPrice

bool

Ob Clients den Preis überschreiben dürfen.

ProductCashbox

Feld

Typ

Beschreibung

cashboxId

int

Zugehörige Kassa.

productionPlaceId

int?

Küche / Bar, in der zubereitet wird.

parentProductId

int?

Bei Varianten / Template-Ableitungen gesetzt.

Media

Feld

Typ

Beschreibung

imageUrl

string

Öffentliche Bild-URL.

Flags

Feld

Typ

Beschreibung

isVisible

bool

Sichtbar in Storefronts.

isLocked

bool

Gegen automatische Sync-Änderungen gesperrt.

queuedForReview

bool

Wartet auf manuelles Review.

Nutrition

Feld

Typ

Beschreibung

amount

double?

Nettomenge.

amountUnit

string

Einheit, z. B. g, ml.

calories

double?

Kalorien pro amount.

allergens

int (Flags)

Bitflag der deklarierten Allergene.

minimumAge

int

Mindestkäuferalter (0, wenn keines).

ingredients

[Ingredient]

Deklarierte Zutatenliste. Leer-Array, wenn nicht gepflegt.

nutritionalValues

[NutritionalValue]

Strukturierte Nährwerte. Leer-Array, wenn nicht gepflegt.

Ingredient

Feld

Typ

Beschreibung

name

string

Zutaten-Bezeichnung (z. B. Lamm, Weizenmehl).

NutritionalValue

Feld

Typ

Beschreibung

name

string

Wert-Bezeichnung (z. B. Gewicht, Kalorien, Fett).

unit

string

Einheit, freier Text (z. B. g, ml, kcal).

value

string

Numerischer Wert, als String serialisiert, um das Quellformat zu erhalten. Client-seitig parsen, falls nötig.

belongsTo

int?

Optionale Referenz auf eine Unterkomponente / Variante (null, wenn der Wert für das Gesamtprodukt gilt).

Localization

Feld

Typ

Beschreibung

locale

string

Sprach- / Culture-Schlüssel der Übersetzung (z. B. en, it, de-DE).

name

string

Übersetzter Anzeigename.

subtitle

string

Übersetzter Untertitel.

description

string

Übersetzte ausführliche Beschreibung.

imageUrl

string

Sprachspezifisches 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

Parameter

Typ

Beschreibung

locationId

int?

Pflicht (oder locationShortCode).

locationShortCode

string

Pflicht (oder locationId).

cashboxId

int?

Auf eine Kassa einschränken. Muss zum Standort gehören.

search

string

Volltextsuche über Name, externer Name und externe ID.

categoryIds

int[]

Filter nach APRO-Warengruppen-ID.

externalIds

string[]

Filter nach externer ID.

ProductCategory

Feld

Typ

Beschreibung

id

int

APRO-Cloud-Warengruppen-ID.

externalId

string

POS-Warengruppen-ID — die im lokalen Kassensystem vergebene ID.

name

string

Anzeigename.

externalName

string

Name für externe Oberflächen.

cashbox

CategoryCashbox

Kassa / Lieferant.

tax

CategoryTax

Steuersatz.

localizations

CategoryLocalization[]

Gepflegte Übersetzungen der Warengruppentexte und des Bildes, ein Eintrag je Sprache. Leer, wenn keine vorhanden sind.

CategoryCashbox

Feld

Typ

Beschreibung

cashboxId

int?

Zugehörige Kassa.

supplierId

int?

Standard-Lieferant für Artikel der Warengruppe.

CategoryTax

Feld

Typ

Beschreibung

taxRateId

int?

Standard-Steuersatz für Artikel der Warengruppe.

vat

double

USt-Prozentsatz (z. B. 20 für 20 %).

CategoryLocalization

Feld

Typ

Beschreibung

locale

string

Sprach- / Culture-Schlüssel der Übersetzung (z. B. en, it, de-DE).

name

string

Übersetzter Anzeigename.

description

string

Übersetzte Beschreibung.

imageUrl

string

Sprachspezifisches 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

Parameter

Typ

Beschreibung

locationId

int?

Pflicht (oder locationShortCode).

locationShortCode

string

Pflicht (oder locationId).

areaId

int?

Auf einen Bereich / ein Zimmer einschränken.

search

string

Volltextsuche über Name, Alternativname, externe Spot-ID und QR-Code.

spotIds

int[]

Filter nach APRO-Spot-ID.

externalSpotIds

string[]

Filter nach externer Spot-ID.

type

enum

Spot / Table / Room / Pickup / Delivery / …

visibleInApp

bool?

Filter nach App-Sichtbarkeit.

supportsTableOrder

bool?

Filter nach Tischbestell-Fähigkeit.

Spot

Feld

Typ

Beschreibung

id

int

APRO-Cloud-Spot-ID.

externalSpotId

string

POS-Spot-ID — die im lokalen Kassensystem vergebene ID.

number

int?

Numerisches Label (z. B. Tischnummer).

name

string

Anzeigename.

alternativeName

string

Sekundärer Name für manche Oberflächen.

description

string

Freitext-Beschreibung.

qrCode

string

QR-Code-Payload des Spots.

type

enum

Spot-Typ.

capacity

int?

Sitzplätze (Tisch) / Maximalgäste (Zimmer).

location

SpotLocation

Zugehöriger Standort.

area

SpotArea

Zugehöriger Bereich, falls vorhanden.

geo

SpotGeo

Geokoordinaten. null, wenn beide Werte fehlen.

flags

SpotFlags

Fähigkeits-Flags.

SpotLocation

Feld

Typ

Beschreibung

locationId

int

Numerische Standort-ID.

locationShortCode

string

Kurzcode für Cross-API-Joins.

SpotArea

Feld

Typ

Beschreibung

areaId

int

APRO-Bereichs-ID.

name

string

Anzeigename des Bereichs.

externalAreaId

string

Externe Referenz des Bereichs.

SpotGeo

Feld

Typ

Beschreibung

latitude

double?

WGS84-Breite.

longitude

double?

WGS84-Länge.

SpotFlags

Feld

Typ

Beschreibung

visibleInApp

bool

Sichtbar in Storefronts.

supportsTableOrder

bool

Akzeptiert Tischbestellungen.

supportsCashPayment

bool

Akzeptiert Bargeld.

defaultToTakeAway

bool

Default-Bestellmodus ist Takeaway.

displayAsButton

bool

Als 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.productNr ↔ productNr (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.cashboxId ↔ cashboxId-Filter auf beiden Endpunkten
  • spot.location.locationShortCode ↔ locationShortCode-Filter überall
  • spot.id ↔ SpotID 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

Status

Bedeutung

400 Bad Request

Weder locationId noch locationShortCode lieferten einen gültigen Standort.

401 Unauthorized

x-api-key fehlt oder ist ungültig.

403 Forbidden

Key nicht für den Standort autorisiert.

429 Too Many Requests

Rate-Limit — mit Jitter zurücknehmen und erneut versuchen.