APIs

7 Seiten

Stand: 6. November 2026

Revenue API

The Revenue API returns every fiscal transaction in a date range — with its positions, payments and tax breakdown — in a single call. Use it for accounting exports, revenue reconciliation, BI warehouses or ad-hoc reports.

Tip: Revenue API or SQL Views?
The Revenue API and the SQL Views expose the
same underlying data, but in different shapes for different runtimes:

- Revenue API — served over the public cloud endpoint
(my.apro.at). Returns a hierarchical JSON document per receipt
with positions, payments, tax, staff and table already nested. Ideal
for off-premise systems, SaaS integrations, mobile clients and any
consumer that wants a ready-to-use object tree.
- SQL Views — read-only views on the on-premise cash register
database. Flat, relational, queryable with any tool that speaks
SQL Server. Ideal for on-premise integrations, ETL pipelines and
existing BI workflows (Power BI, Tableau, Qlik, Metabase).

Pick the cloud API for real-time system-to-system integration, pick
the SQL views for bulk analytics and dashboards.
  • Host: my.apro.at
  • Auth: API key in x-api-key
  • Content type: application/json

Endpoints

Variant

Endpoint

Location derived from

Path + key

POST /api/v1/locations/{location}/reports/receipts

{location} must match the key's location.

Key only

POST /api/v1/reports/receipts

The API key itself.

Both variants require a location-scoped API key. The key itself determines which location's data is returned — there is no tenant-wide variant. Pick whichever feels more explicit in your client code — both return the same data and require the same key.

Request body

Field

Type

Required

Description

after

ISO 8601 timestamp

✅

Return receipts after this point in time.

before

ISO 8601 timestamp

✅

Return receipts before this point in time.

limit

integer

—

Cap the number of results returned.

afterTransactionID

GUID

—

Cursor — return transactions after this ID. Combine with limit for paging.

Example request

POST /api/v1/reports/receipts

{
  "after":  "2026-01-01T00:00:00Z",
  "before": "2026-02-01T00:00:00Z",
  "limit":  100,
  "afterTransactionID": "f03e11de-6330-4a03-a8a8-b6e5fd4e32a5"
}

Download: revenue-request.json

Response

An array of receipts. Each element is one transaction with its positions, payments and metadata.

Top-level fields

Field

Type

Description

transactionId

GUID

Unique transaction ID.

transactionType

string

See Transaction types.

receiptType

string

Beleg, Ausgangsrechnung, or Lieferschein.

totalAmount

decimal

Gross total of the receipt.

tip

decimal

Tip included in totalAmount.

discount

decimal

Discount applied to the receipt.

dateTime

ISO 8601

When the transaction happened.

business

Business

Tenant / operator record.

location

Location

The APRO location the data was queried for.

staff

Staff

Operator / waiter.

table

Table

Table or delivery spot (e.g. "Bar 1", "Room 301").

area

Area

Room / zone the table belongs to (e.g. Garden, Bar).

customer

Customer · nullable

Invoice recipient. Always null on this endpoint — see below.

paymentGroup

Payment group

Grouping the payment types roll up into.

payments

[Payment]

Payments settling the receipt.

items

[Item]

Line items.

guestCount

integer

Number of guests, summed over the receipt's line items. 0 when not recorded.

Item

Field

Type

Description

id

string

Unique item ID.

number

integer

Product number (as used in APRO). 0 when it cannot be parsed.

name

string

Product name.

uniqueId

string · nullable

Stable per-line identifier. Use it to deduplicate or reconcile lines across repeated queries. null on older query configurations.

externalId

string · nullable

Reference assigned by an external system. null on older query configurations.

bookDate

ISO 8601

Time the line was booked.

grossAmount

decimal

Gross price.

netAmount

decimal

Net price.

purchasePrice

decimal

Cost price of the line, for margin reporting. 0 on older query configurations.

quantity

decimal

Units sold.

course

integer · nullable

Course number in a multi-course service (Gang). null when not assigned.

tax

Tax

Applied tax rate.

category

Category

Product category (a.k.a. "Sparte").

sideDishes

[Side dish] · nullable

Side dishes / modifiers booked on this line. null when there are none.

Side dish

Side dishes (Unterartikel) are the modifiers booked underneath a line item — the Pommes under a schnitzel, the ohne Zwiebel on a burger. They are returned nested inside their parent item, never as items of their own.

A side dish reuses the Item shape, but only the following fields are populated. Every other field — bookDate, externalId, purchasePrice, course, tax, category, sideDishes — is null or its zero value, and side dishes do not nest recursively.

Field

Type

Description

id

string

Unique side dish ID.

number

integer

Product number of the side dish. 0 when it cannot be parsed.

name

string

Side dish name.

uniqueId

string

Stable per-side-dish identifier. Also the key the API deduplicates on.

grossAmount

decimal

Gross price of the side dish.

netAmount

decimal

Net price of the side dish.

quantity

decimal

Units of the side dish.

Caution: Do not add side dishes to your totals
A side dish that carries a surcharge is **already included in its
parent item's grossAmount / netAmount** and in the receipt's
totalAmount. Summing sideDishes[].grossAmount on top of the parent
line double-counts revenue. Treat the amounts as a breakdown for
display, not as separate positions.

sideDishes is null — not [] — when a line has none, and also on older query configurations that predate the feature. Test for null-or-empty rather than assuming an array is present.

Tax

Field

Type

Description

id

string

Unique tax rate ID.

value

decimal

Rate, e.g. 20.0 for 20 %.

name

string

Display name, e.g. Mwst 20 %.

Category

Field

Type

Description

id

string

Unique category ID.

number

integer

Category number.

name

string

Display name.

Payment

Field

Type

Description

id

string

Unique payment ID. Payments are deduplicated on this value.

amount

decimal

Amount paid.

paymentType

PaymentType

Method used.

fibuAccountNr

string · nullable

Financial-accounting account number the payment posts to. Use it to map payments onto ledger accounts in an export.

Payment type

Field

Type

Description

id

integer · nullable

Unique payment method ID.

nr

integer · nullable

Payment method number. Note the key is nr, not number.

name

string

Display name, e.g. Bar, Kreditkarte.

Business

Field

Type

Description

id

string

Tenant ID from the POS database.

name

string

Legal name.

Staff

Field

Type

Description

id

string

Unique staff ID.

number

integer

Personnel number.

name

string

Staff name.

Table

Field

Type

Description

id

string

Unique table ID.

number

integer

Table number.

name

string

Table label, e.g. Bar 1, Restaurant 5.

Location

Field

Type

Description

id

string

APRO location ID — the locationId the query resolved to.

name

string · nullable

Location name.

Area

The room or zone a table sits in.

Field

Type

Description

id

string

Unique area ID.

number

integer

Area number.

name

string

Area label, e.g. Garten, Bar.

Payment group

The grouping payment types roll up into — used for cash-book and settlement reporting.

Field

Type

Description

id

integer

Payment group ID. 0 when not set.

name

string

Group label.

Customer

Field

Type

Description

id

string

Unique customer ID.

number

integer

Customer number.

name

string

Customer name.

Note: Always null today
The receipts endpoint never populates customer — it is reserved in the
response contract but not filled by the query. Do not build invoice-
recipient logic on it; resolve customers through their own endpoints
instead.

Example response

Response · two receipts settled back-to-back

[
  {
    "transactionId": "f03e11de-6330-4a03-a8a8-b6e5fd4e32a5",
    "transactionType": "Abrechnen",
    "receiptType": "Beleg",
    "totalAmount": 3.2,
    "tip": 0.0,
    "discount": 0.0,
    "dateTime": "03.10.2021 21:59:57",
    "business": { "id": "1", "name": "APRO Kassensysteme" },
    "location": { "id": "7236", "name": "Wien Haupthaus" },
    "staff":    { "id": "8", "number": 9500, "name": "Onlinekellner" },
    "table":    { "id": "76", "number": 76, "name": "Daniel Z" },
    "area":     { "id": "3", "number": 3, "name": "Garten" },
    "customer": null,
    "paymentGroup": { "id": 2, "name": "Kartenzahlung" },
    "payments": [
      {
        "id": "04b2d2f5-8765-41b5-ad5c-aed802dc23bc",
        "amount": 3.2,
        "paymentType": { "id": 26, "nr": 200, "name": "Smorder Kreditkarte" },
        "fibuAccountNr": "2751"
      }
    ],
    "items": [
      {
        "id": "38",
        "number": 216,
        "name": "Wiener Schnitzel",
        "uniqueId": "0f6c2c14-3f8a-4f0b-9a1e-2b7d5c8e4411",
        "externalId": null,
        "bookDate": "03.10.2021 21:59:55",
        "grossAmount": 3.2,
        "netAmount": 3.2,
        "purchasePrice": 1.1,
        "quantity": 1.0,
        "course": 2,
        "tax": { "id": "5", "value": 5.0, "name": "Mwst 5 %" },
        "category": { "id": "12", "number": 12, "name": "Hauptspeisen" },
        "sideDishes": [
          {
            "id": "512",
            "number": 9001,
            "name": "Pommes",
            "uniqueId": "b41d9a77-55c2-4e3d-8f10-6cc0a2b93d88",
            "grossAmount": 1.5,
            "netAmount": 1.36,
            "quantity": 1.0
          },
          {
            "id": "518",
            "number": 9007,
            "name": "ohne Zwiebel",
            "uniqueId": "c9a70e31-1d44-4a92-b7e5-30f8c1d6a204",
            "grossAmount": 0.0,
            "netAmount": 0.0,
            "quantity": 1.0
          }
        ]
      }
    ],
    "guestCount": 2
  },
  {
    "transactionId": "db2b90d1-ab37-4557-b169-2289c3004a0a",
    "transactionType": "Abrechnen",
    "receiptType": "Beleg",
    "totalAmount": 3.2,
    "tip": 0.0,
    "discount": 0.0,
    "dateTime": "03.10.2021 22:00:12",
    "business": { "id": "1", "name": "APRO Kassensysteme" },
    "location": { "id": "7236", "name": "Wien Haupthaus" },
    "staff":    { "id": "8", "number": 9500, "name": "Onlinekellner" },
    "table":    { "id": "76", "number": 76, "name": "Daniel Z" },
    "area":     { "id": "3", "number": 3, "name": "Garten" },
    "customer": null,
    "paymentGroup": { "id": 2, "name": "Kartenzahlung" },
    "payments": [
      {
        "id": "bb3e6300-0cc3-4922-80c4-a8a4c3530d3c",
        "amount": 3.2,
        "paymentType": { "id": 26, "nr": 200, "name": "Smorder Kreditkarte" },
        "fibuAccountNr": "2751"
      }
    ],
    "items": [
      {
        "id": "38",
        "number": 216,
        "name": "Cola 0,5",
        "uniqueId": "7d2f3b90-6c11-4c8e-93aa-5e4419c7f0d3",
        "externalId": null,
        "bookDate": "03.10.2021 22:00:11",
        "grossAmount": 3.2,
        "netAmount": 3.2,
        "purchasePrice": 0.7,
        "quantity": 1.0,
        "course": null,
        "tax": { "id": "5", "value": 5.0, "name": "Mwst 5 %" },
        "category": { "id": "20", "number": 20, "name": "Alkoholfreie Getränke" },
        "sideDishes": null
      }
    ],
    "guestCount": 1
  }
]

Download the same payload as a standalone file: revenue-response.json

Transaction types

The transactionType discriminates what the row represents. For revenue-effective reports, filter on Abrechnen (and RechnungZurückholen for reversals).

Type

Meaning

Bonieren

Item booked on an open table.

Verschieben

Table change.

Abrechnen

Settlement — a paid receipt.

Stornieren

Reversal of a booking.

RechnungZurückholen

Reversal of a settlement.

DebitBonieren

Debit booking.

ARAusstellen · ARPreview · ARNachdruck · ARNachdruckOriginal

Outgoing invoice lifecycle.

Tagesabschluss · TagesabschlussPreview

Day-end close / preview.

Kellnerabschlag · KellnerabschlagPreview · KellnerabschlagUndo · Kelleruebergabe

Waiter hand-over / settlement.

Startbeleg · Schlussbeleg · Nullbeleg · Monatsbeleg · JahresBeleg · Nacherfassungssammelbeleg

Legal fiscal receipts (RKSV).

TischOeffnen · TischSchliessen · GaesteanzahlAendern · GaesteanzahlKorrigieren

Table lifecycle.

BestellmanagerBestellung · …NextState · …SetFinaleState · …Undo · …Stornieren · …Verschieben · …NaechsterGang · …Statistik

Order-manager events.

WarenkorbErstellen · WarenkorbUpdate · WarenkorbVerwerfen

Cart lifecycle.

DatenExport · UndoDatenExport

Data export markers.

Kassabuch · Bargeldzählung · TerminalKassenschnitt · Lieferantabschlag · LieferantabschlagVorschau · Ansehen · Information · SchankanlageQrCode · Abrechnen HSK · Abrechnen AR · AbrechnenNachdruck

Other operational events.

Calculating revenue

To compute revenue-effective turnover over a response, keep only paid settlements and their reversals, and sum the net line totals:

response
  .filter(x =>
    x.paymentGroup.name === "Umsatzwirksam" &&
    ["Abrechnen", "RechnungZurückholen"].includes(x.transactionType)
  )
  .map(x =>
    x.items.reduce((sum, item) => sum + item.netAmount * item.quantity, 0)
  )
  .reduce((sum, val) => sum + val, 0);

Paging

There is no cursor-in-response — use the deterministic transactionId of the last row you saw as afterTransactionID on the next request, keeping after/before unchanged. Continue until the response contains fewer rows than your limit.

Errors

See Authentication → Error responses.

External Order API

The External Order API accepts a fully-formed order from any external system — a delivery aggregator, a white-label app, a kiosk, a voice agent — and routes it into the running APRO POS at the target location. A single POST is enough; the order appears on the order-manager screen immediately.

  • Endpoint: POST https://my.apro.at/api/v1/external/order
  • Auth: API key in x-api-key
  • Content type: application/json

Routing the order to a location

The order must be addressable to one concrete location. Provide one of the following — the first that is non-null wins:

  1. LocationID — numeric ID
  2. LocationShortCode — short string identifier (e.g. vienna-main)
  3. SpotID — a specific table/room/pickup spot (location is derived)
  4. QRCode — a scanned APRO QR code (location and spot are derived)

If none is provided, the request is rejected.

Request body

Field

Type

Required

Description

guid

string

—

Idempotency key. If the same guid arrives twice, the duplicate is silently dropped.

locationId

int?

◻︎

See routing above.

locationShortCode

string

◻︎

See routing above.

spotId

int?

—

ID of the target spot (table, room, pickup, delivery — all are spots, just different types). Matches spot.id on the External Catalog API.

spotNumber

int?

—

Spot number, when ID is unknown. Resolved by spot.number.

spotName

string

—

Fallback when neither spotId nor spotNumber resolves. Looked up by spot.name — prefers a spot of type Table, then any spot at the location.

roomNumber

int?

—

Number of the room spot for hotel / room-charge flows. Resolved against a SpotType.Room spot by spot.number (offset by the location's RoomNrSpotNrOffset); falls back to spot.alternativeName matching the raw number. When found, it sets spotId (if not already provided) and binds the order to the room's currently active reservation.

roomId

int?

—

Reserved for forward compatibility. Currently accepted but ignored — use spotId of a Room-type spot to address a room directly.

userInfo

UserInfo

✅

Who placed the order. email is required.

deliveryInfo

DeliveryInfo

—

Delivery address. Required for home delivery.

takeaway

bool

✅

true for takeaway, false for dine-in.

note

string

—

Free-form note from the guest (allergens, special requests).

qrCode

string

—

See routing.

entries

[Entry]

✅

Ordered items.

preOrderTime

long?

—

Pre-order slot, Unix time in milliseconds UTC.

shortCode

string

—

Pickup / delivery reference (e.g. zw1356).

tip

decimal?

—

Tip amount.

isPaid

bool

—

true if the order has already been paid externally.

grandTotal

decimal?

✅

Total of all entries + tip. Used to validate against server-side pricing.

Entry

Field

Type

Required

Description

productNr

string

✅

Product number in APRO.

name

string

—

Display name — informational only; APRO trusts productNr.

preOrderTime

long?

—

Per-item pre-order slot.

quantity

double

✅

Quantity ordered.

price

double?

—

Override the system price for this line (discounts, campaigns).

note

string

—

Item-level note (e.g. Ohne Zwiebeln).

addons

[Addon]

—

Add-on selections.

Addon

Field

Type

Required

Description

productNr

string

✅

Product number of the add-on.

name

string

—

Display name.

quantity

double

✅

Quantity.

price

double?

—

Override the system price.

UserInfo

Field

Type

Required

Description

firstName

string

—


lastName

string

—


email

string

✅

Contact email for receipts and status updates.

phone

string

—


locale

string

—

IETF locale, e.g. de-DE.

nickname

string

—

Short name shown at pickup.

DeliveryInfo

Field

Type

Description

name

string

Recipient label.

companyName

string


city

string


zip

string


street

string


country

string


floor

string


stairway

string


door

string


note

string

Delivery instructions.

Examples

Minimal order

POST /api/v1/external/order

{
  "locationShortCode": "vienna-main",
  "takeaway": true,
  "userInfo": { "email": "guest@example.com" },
  "entries": [{ "productNr": "123", "quantity": 1 }],
  "grandTotal": 9.99
}

Download: external-order-minimal.json

Full order with delivery and add-ons

POST /api/v1/external/order

{
  "guid": "123e4567-e89b-12d3-a456-426614174000",
  "locationId": 101,
  "spotId": 15,
  "spotNumber": 10,
  "shortCode": "zw1356",
  "userInfo": {
    "firstName": "Max",
    "lastName": "Mustermann",
    "email": "max.mustermann@example.com",
    "phone": "+491234567890",
    "locale": "de-DE",
    "nickname": "Max"
  },
  "takeaway": false,
  "note": "Bitte schnell liefern",
  "qrCode": "QR1234567890",
  "deliveryInfo": {
    "name": "Max Mustermann",
    "city": "Wien",
    "zip": "1010",
    "street": "Stephansplatz 1",
    "country": "AT",
    "floor": "3",
    "stairway": "A",
    "door": "12"
  },
  "entries": [
    {
      "productNr": "123",
      "name": "Wiener Schnitzel",
      "quantity": 2,
      "price": 9.99,
      "note": "Extra scharf",
      "addons": [
        { "productNr": "501", "name": "Pommes", "quantity": 2, "price": 3.5 }
      ]
    },
    { "productNr": "31231", "name": "Burger Classic", "quantity": 1, "note": "Ohne Zwiebeln" }
  ],
  "preOrderTime": 1723574400000,
  "tip": 1.2,
  "isPaid": true,
  "grandTotal": 28.18
}

Download: external-order-full.json

Idempotency

Always set guid to a stable, client-generated UUID. APRO uses it to deduplicate retries — if a response is lost in transit and you retry, the second POST is a no-op and returns success.

Pricing

APRO re-prices every line server-side from productNr. price on an entry or add-on only takes effect when explicitly passed; grandTotal is checked against the server total and an inconsistent payload is rejected. Use it both as a safety net and as a mechanism to pass through campaign-specific discounts.

External Catalog API

The External Catalog API exposes a slim, read-only view of the master data inside an APRO location — the products, product categories and spots (tables, rooms, pickup points) — for use in white-label apps, kiosks, external menus and partner integrations.

The three endpoints share the same conventions: scoping by location, nested DTO shape, filtering via query parameters, and pagination.

  • Host: my.apro.at
  • Auth: API key in x-api-key
  • Content type: application/json
Tip: Try it in Postman
Download the ready-to-run collection:
apro-external-catalog.postman_collection.json.
Import it into Postman, set apiKey and locationShortCode in the
collection's Variables tab, hit Send.
Caution: Filters use a bracketed query syntax
All filter fields — including the required locationId /
locationShortCode — must be passed as filter['<name>']=<value>,
not as plain query parameters. Plain ?locationShortCode=… is
silently ignored and the request fails with "location not resolved".
See Filtering for full rules.

Endpoints

Method

URL

Returns

GET

/api/v1/external/product

Paged list of Product

GET

/api/v1/external/product-category

Paged list of ProductCategory

GET

/api/v1/external/spot

Paged list of Spot

Common conventions

Scoping to a location

Every request must address one concrete location. Provide one of:

  1. locationId — numeric ID
  2. locationShortCode — short string identifier (e.g. vienna-main)

If neither resolves to an existing location, the request is rejected with 400 Bad Request.

Pagination

Query param

Default

Description

skip

0

Number of records to skip.

take

50

Page size. Capped at 500.

Every response is wrapped in a paged envelope:

{
  "page": { "offset": 0, "limit": 50, "total": 1234 },
  "data": [ /* ... */ ]
}
  • offset and limit echo the request.
  • total is the total number of records matching the filter, across all pages — use it to drive skip/take loops.

Filtering

Filters are passed as bracketed query parameters under the filter key — filter['<name>']=<value>. Plain ?<name>=<value> is ignored. Pagination (skip, take) stays as plain query params.

List-valued filters (productIds, gtins, externalSpotIds, …) are comma-separated. Escape literal commas with \,.

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

Notes:

  • The bracket key is case-insensitive — filter['LocationShortCode'] and filter['locationShortCode'] are equivalent.
  • filter["…"] (double quotes, URL-encoded as %22) works too.
  • Unknown filter parameters are ignored.

Products

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

Query parameters

Param

Type

Description

locationId

int?

Required (or locationShortCode).

locationShortCode

string

Required (or locationId).

cashboxId

int?

Restrict to one cashbox. Must belong to the location.

search

string

Free-text match over name, external name, productNr, GTIN and external product ID.

productIds

int[]

Filter by APRO product ID.

productNrs

string[]

Filter by productNr (the value passed back in External Order entries).

gtins

string[]

Filter by GTIN (barcode).

externalProductIds

string[]

Filter by external product ID (third-party reference).

isVisible

bool?

Filter by visibility flag.

isLocked

bool?

Filter by locked flag.

Product

Field

Type

Description

id

int

APRO Cloud product ID.

productNr

string

Identifier the order pipeline uses to resolve this product. Pass this value straight back as the entry's productNr when placing an order via the External Order API. Backed by the POS article number.

gtin

string

Barcode / GTIN.

externalProductId

string

POS product ID — the ID assigned by the local POS system. Informational; not what External Order's productNr resolves against.

name

string

Display name.

externalName

string

Name for external surfaces — fallback to name if empty.

subtitle

string

Short subtitle.

description

string

Long-form description.

pricing

Pricing

Price and tax info.

cashbox

ProductCashbox

Cashbox / parent / production-place wiring.

media

Media

Image URLs.

flags

Flags

Visibility, locking, review state.

nutrition

Nutrition

Amount, calories, allergens, age limit.

productGroupExternalIds

string[]

External IDs of the product categories this product belongs to.

localizations

Localization[]

Maintained translations of the product's texts and image, one entry per locale. Empty when none exist.

Pricing

Field

Type

Description

defaultPrice

double?

Default price.

referencePrice

double?

Reference / strike-through price.

taxRateId

int?

APRO tax rate ID.

allowClientPrice

bool

Whether clients may override the price.

ProductCashbox

Field

Type

Description

cashboxId

int

Owning cashbox.

productionPlaceId

int?

Kitchen / bar where the product is prepared.

parentProductId

int?

Set on variants / template-derived products.

Media

Field

Type

Description

imageUrl

string

Public image URL.

Flags

Field

Type

Description

isVisible

bool

Visible in storefronts.

isLocked

bool

Locked from automatic sync changes.

queuedForReview

bool

Pending manual review.

Nutrition

Field

Type

Description

amount

double?

Net amount.

amountUnit

string

Unit, e.g. g, ml.

calories

double?

Calories per amount.

allergens

int (flags)

Bit-flag of declared allergens.

minimumAge

int

Minimum buyer age (0 if none).

ingredients

[Ingredient]

Declared ingredients list. Empty array when not maintained.

nutritionalValues

[NutritionalValue]

Structured nutritional values. Empty array when not maintained.

Ingredient

Field

Type

Description

name

string

Ingredient name (e.g. Lamm, Weizenmehl).

NutritionalValue

Field

Type

Description

name

string

Value label (e.g. Gewicht, Kalorien, Fett).

unit

string

Unit, free-form (e.g. g, ml, kcal).

value

string

Numeric value, serialized as a string to preserve the source format. Parse client-side if needed.

belongsTo

int?

Optional reference to a sub-component / variant (null when the value applies to the whole product).

Localization

Field

Type

Description

locale

string

Locale / culture key of the translation (e.g. en, it, de-DE).

name

string

Translated display name.

subtitle

string

Translated subtitle.

description

string

Translated long-form description.

imageUrl

string

Locale-specific image, when one is maintained.

The top-level name, subtitle, description and media.imageUrl always carry the default (German) values — localizations never replaces them, it only adds the translations that exist. Entries without a locale key are omitted, and individual fields inside an entry may be null when that particular text was not translated. Resolve a display string by looking for the caller's locale in localizations and falling back to the top-level field.

Example

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
        }
      ]
    },
    {
      "id": 9002,
      "productNr": "A-110",
      "gtin": null,
      "externalProductId": null,
      "name": "Tafelspitz",
      "externalName": null,
      "subtitle": null,
      "description": null,
      "pricing": { "defaultPrice": 24.5, "referencePrice": null, "taxRateId": 3, "allowClientPrice": false },
      "cashbox": { "cashboxId": 17, "productionPlaceId": 2, "parentProductId": null },
      "media": { "imageUrl": null },
      "flags": { "isVisible": true, "isLocked": false, "queuedForReview": false },
      "nutrition": { "amount": null, "amountUnit": null, "calories": null, "allergens": 0, "minimumAge": 0, "ingredients": [], "nutritionalValues": [] },
      "productGroupExternalIds": ["mains"],
      "localizations": []
    }
  ]
}

Download: external-products-response.json

Product categories

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

Categories correspond to APRO product groups — the cashbox-scoped grouping that drives reports, tax rates and printing order. The productGroupExternalIds field on a product references the same externalId returned here, so you can join the two responses without extra lookups.

Query parameters

Param

Type

Description

locationId

int?

Required (or locationShortCode).

locationShortCode

string

Required (or locationId).

cashboxId

int?

Restrict to one cashbox. Must belong to the location.

search

string

Free-text match over name, external name and external ID.

categoryIds

int[]

Filter by APRO category ID.

externalIds

string[]

Filter by external ID.

ProductCategory

Field

Type

Description

id

int

APRO Cloud category ID.

externalId

string

POS category ID — the ID assigned by the local POS system.

name

string

Display name.

externalName

string

Name for external surfaces.

cashbox

CategoryCashbox

Cashbox / supplier wiring.

tax

CategoryTax

Tax wiring.

localizations

CategoryLocalization[]

Maintained translations of the category's texts and image, one entry per locale. Empty when none exist.

CategoryCashbox

Field

Type

Description

cashboxId

int?

Owning cashbox.

supplierId

int?

Default supplier for products in this category.

CategoryTax

Field

Type

Description

taxRateId

int?

Default tax rate for products in this category.

vat

double

VAT percentage (e.g. 20 for 20 %).

CategoryLocalization

Field

Type

Description

locale

string

Locale / culture key of the translation (e.g. en, it, de-DE).

name

string

Translated display name.

description

string

Translated description.

imageUrl

string

Locale-specific image, when one is maintained.

Same rules as on products: the top-level name stays the default (German) value and localizations only adds what exists.

Example

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 are the physical / logical destinations a guest can order to: tables, rooms, pickup counters, delivery zones, drive-in lanes.

Query parameters

Param

Type

Description

locationId

int?

Required (or locationShortCode).

locationShortCode

string

Required (or locationId).

areaId

int?

Restrict to one area / room.

search

string

Free-text match over name, alternative name, external spot ID and QR code.

spotIds

int[]

Filter by APRO spot ID.

externalSpotIds

string[]

Filter by external spot ID.

type

enum

Spot / Table / Room / Pickup / Delivery / …

visibleInApp

bool?

Filter on app visibility.

supportsTableOrder

bool?

Filter on table-order capability.

Spot

Field

Type

Description

id

int

APRO Cloud spot ID.

externalSpotId

string

POS spot ID — the ID assigned by the local POS system.

number

int?

Numeric label (e.g. table number).

name

string

Display name.

alternativeName

string

Secondary name used by some surfaces.

description

string

Free-form description.

qrCode

string

QR-code payload bound to the spot.

type

enum

Spot type.

capacity

int?

Number of seats (tables) / max guests (rooms).

location

SpotLocation

Owning location.

area

SpotArea

Owning area, if any.

geo

SpotGeo

Geo coordinates, if any. Omitted when both are null.

flags

SpotFlags

Capability flags.

SpotLocation

Field

Type

Description

locationId

int

Numeric location ID.

locationShortCode

string

Short code for cross-API joins.

SpotArea

Field

Type

Description

areaId

int

APRO area ID.

name

string

Area display name.

externalAreaId

string

External reference for the area.

SpotGeo

Field

Type

Description

latitude

double?

WGS84 latitude.

longitude

double?

WGS84 longitude.

SpotFlags

Field

Type

Description

visibleInApp

bool

Visible in storefronts.

supportsTableOrder

bool

Accepts in-house orders.

supportsCashPayment

bool

Accepts cash.

defaultToTakeAway

bool

Default order mode is takeaway.

displayAsButton

bool

Render as a button instead of a list item.

Example

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
      }
    },
    {
      "id": 1502,
      "externalSpotId": "table-02",
      "number": 2,
      "name": "Tisch 2",
      "alternativeName": null,
      "description": null,
      "qrCode": "QR-02",
      "type": "Table",
      "capacity": 2,
      "location": { "locationId": 7236, "locationShortCode": "vienna-main" },
      "area": { "areaId": 11, "name": "Gastgarten", "externalAreaId": "garden" },
      "geo": { "latitude": 48.2082, "longitude": 16.3738 },
      "flags": {
        "visibleInApp": true,
        "supportsTableOrder": true,
        "supportsCashPayment": true,
        "defaultToTakeAway": false,
        "displayAsButton": false
      }
    }
  ]
}

Download: external-spots-response.json

Cross-API joins

These three endpoints are designed to be joined client-side:

  • product.productNr ↔ productNr (entry / addon) on the External Order API — case-insensitive, matched only against products on the location's primary cashbox. When productNr doesn't resolve, the order pipeline falls back to matching the entry's name against product.name.
  • product.productGroupExternalIds[i] ↔ productCategory.externalId
  • product.cashbox.cashboxId / productCategory.cashbox.cashboxId ↔ cashboxId filter on either endpoint
  • spot.location.locationShortCode ↔ locationShortCode filter everywhere
  • spot.id ↔ SpotID on the External Order API — pass the spot's id straight back to route an order to the same spot it was read from. spot.qrCode maps to the External Order API's QRCode field.

Errors

Status

Meaning

400 Bad Request

Neither locationId nor locationShortCode resolved to a location.

401 Unauthorized

Missing or invalid x-api-key.

403 Forbidden

Key not authorised for the requested location.

429 Too Many Requests

Rate-limited — back off and retry with jitter.

Online Menu API

The Online Menu API serves the public menu structure for white-label apps, self-order kiosks and QR-ordering flows. Reading is unauthenticated; every mutating call requires authentication.

  • Host: my.apro.at
  • Content type: application/json

Fetching a menu

GET https://my.apro.at/api/v1/locations/{locationId}/menus

The response is a tree. Levels, from outermost to innermost:

  1. Menu — top-level grouping (e.g. Food, Drinks, Deals).
  2. Category — section inside a menu (e.g. Mains).
  3. MenuEntry — a concrete sellable item.
  4. MenuEntryFeature — add-on / modifier group on an item.
  5. MenuEntryFeatureValue — one option inside a feature.
  6. Hours — availability windows that can attach to menus and categories.

Example response (abridged)

GET /api/v1/locations/{locationId}/menus

[
  {
    "name": "Speisen",
    "badge": 7,
    "imageUrl": "https://cdn.smorder.at/locations/7236/menus/speisen.jpg",
    "description": "Klassiker aus der österreichischen Küche.",
    "priceName": "Hauspreis",
    "orderableHours": [
      { "days": 254, "startTime": "11:00", "endTime": "22:00" }
    ],
    "categories": [
      {
        "name": "Hauptspeisen",
        "menuEntries": [
          {
            "menuEntryID": 4001,
            "name": "Wiener Schnitzel",
            "badge": 7,
            "price": 22.9,
            "features": [
              {
                "name": "Beilage",
                "groupName": "Beilage",
                "minValuesRequired": 1,
                "maxValuesAllowed": 1,
                "valuesForFree": 1,
                "values": [
                  { "name": "Erdäpfelsalat", "priceAdjustment": 0 },
                  { "name": "Pommes frites",  "priceAdjustment": 0 },
                  { "name": "Reis",           "priceAdjustment": 0 }
                ]
              },
              {
                "name": "Ohne",
                "isDeselectFeature": true,
                "values": [
                  { "name": "Preiselbeeren", "priceAdjustment": 0 }
                ]
              }
            ]
          }
        ]
      }
    ]
  }
]

Download the full, unabridged sample: menu-response.json

1. Menu

Outermost grouping in the menu tree; contains categories. Examples: Food, Drinks, Deals.

Field

Type

Description

name

string

Display name.

badge

enum

Visual badge — see Badges.

imageUrl

string

Header image URL.

description

string

Descriptive text.

categories

Category[]

Categories inside this menu.

orderableHours

Hours[]

When ordering is allowed.

preOrderableHours

Hours[]

When pre-ordering is allowed.

deliverableHours

Hours[]

When delivery is allowed.

priceName

string

Price level to use (e.g. House, Happy Hour).

Mutations (auth required):

Action

Method

Path

Create

POST

/api/v1/customer/locations/{locationId}/menu

Update

PUT

/api/v1/customer/menus/{menuId}

Delete

DELETE

/api/v1/customer/menus/{menuId}

2. Category

Sub-section of a menu. Examples: Non-alcoholic drinks, Beer, Mains, Desserts.

Field

Type

Description

name

string

Display name.

badge

enum

See Badges.

imageUrl

string

Header image URL.

description

string

Descriptive text.

menuEntries

MenuEntry[]

Entries inside this category.

orderableHours

Hours[]

When ordering is allowed.

preOrderableHours

Hours[]

When pre-ordering is allowed.

deliverableHours

Hours[]

When delivery is allowed.

Mutations (auth required):

Action

Method

Path

Create

POST

/api/v1/customer/menus/{menuId}/category

Update

PUT

/api/v1/customer/menus/{menuId}/category/{id}

Delete

DELETE

/api/v1/customer/menus/{menuId}/category/{id}

3. MenuEntry

A concrete sellable item. Examples: Wiener Schnitzel, Kaiser Bier.

Field

Type

Description

menuEntryID

integer

Unique entry ID; sent back with orders.

name

string

Item name.

badge

enum

See Badges.

imageUrl

string

Item image URL.

subtitle

string

Short tagline.

description

string

Descriptive text.

features

MenuEntryFeature[]

Modifier groups (sides, sizes…).

price

money

Selling price.

Mutations (auth required):

Action

Method

Path

Create

POST

/api/v1/customer/categories/{categoryId}/menuEntry

Update

PUT

/api/v1/customer/menuEntries/{id}

Delete

DELETE

/api/v1/customer/menuEntries/{id}

4. MenuEntryFeature

A modifier group for a menu entry. Examples: Size, Side for main.

Field

Type

Description

name

string

Feature label.

description

string

Descriptive text.

groupName

string

Grouping key for paginated rendering.

isDeselectFeature

boolean

true marks a "leave out" toggle (e.g. no onions).

maxValuesAllowed

number

Max options selectable.

minValuesRequired

number

Min options required.

valuesForFree

number

Options included at no extra charge.

values

MenuEntryFeatureValue[]

Available options.

Mutations (auth required):

Action

Method

Path

Create

POST

/api/v1/menuentryfeature

Update

PUT

/api/v1/menuentryfeature/{featureId}

Delete

DELETE

/api/v1/menuentryfeature/{featureId}

5. MenuEntryFeatureValue

One concrete option inside a feature. Examples: Beer 0.3 l, Beer 0.5 l, Fries, Potatoes.

Field

Type

Description

name

string

Option label.

description

string

Descriptive text.

imageUrl

string

Option image URL.

minAmount

number

Minimum quantity required when selected.

maxAmount

number

Maximum quantity allowed.

freeAmount

number

Quantity included at no extra charge.

priceAdjustment

number

Price change per selected unit.

Mutations (auth required):

Action

Method

Path

Create

POST

/api/v1/menuentryfeaturevalue

Update

PUT

/api/v1/menuentryfeaturevalue/{featureValueId}

Delete

DELETE

/api/v1/menuentryfeaturevalue/{featureValueId}

6. Hours

Availability windows attached to menus or categories. They gate ordering, pre-ordering and delivery.

Field

Type

Description

days

enum bitmask

Bitmask of weekdays — see below.

startDate

date

Absolute start.

endDate

date

Absolute end.

startTime

daytime

Start time on matching days.

endTime

daytime

End time on matching days.

exclude

boolean

Invert the window — exclude instead of include.

Weekday bitmask:

Day

Bit

Value

Monday

1 << 1

2

Tuesday

1 << 2

4

Wednesday

1 << 3

8

Thursday

1 << 4

16

Friday

1 << 5

32

Saturday

1 << 6

64

Sunday

1 << 7

128

Example: Mon–Fri is 2 + 4 + 8 + 16 + 32 = 62.

Badges

Visual flags for menus, categories and entries. Values:

Label

Value

NEW

1

Recommended

2

Reduced

3

Coming Soon

4

Our Tip

5

Special

6

Bestseller

7

HOT

8

SALE

9

Hotel API (PMS)

The Hotel API is a two-way bridge between an APRO point-of-sale and any Property Management System (PMS). The PMS pushes reservations into the POS so that guests can charge to their room; the POS exposes those charges back to the PMS so they land on the final hotel invoice.

Typical flow

PMS                                       APRO POS
 │ 1. POST  /pms/reservations  ─────────▶ │   (upsert guest rooms)
 │                                        │
 │                                        │  guests charge to room…
 │                                        │
 │ 2. GET   /pms/charges?hotelId=…  ─────▶│
 │ ◀─── charges (and optionally receipts) │
 │                                        │
 │ 3. POST  /pms/confirm-charges  ───────▶│   (ack; won't be resent)
 │                                        │
 │ 4. DELETE /pms/reservations?…  ───────▶│   (on check-out / close-out)

Step 2 is the PMS's polling loop. Steps 1 and 4 are driven by events in the PMS. Step 3 must always follow a successful poll — otherwise the same charges will be returned again on the next poll.

1. Upsert reservations

Push the current list of active reservations into the POS. Keys are hotelId + Id + guestId; existing rows are updated in place.

POST https://cloudgateway.apro.at/hotelservice/api/v1/pms/reservations

Request body

Field

Type

Required

Description

reservations

Reservation[]

✅

One or more reservations.

Reservation

Field

Type

Required

Description

Id

string

✅

PMS-side reservation ID.

hotelId

string

✅

PMS tenant / property ID.

guestId

string

✅

PMS-side guest ID.

roomNr

string

✅

Assigned room number.

title

string

—

Mr., Mrs., etc.

firstName

string

—


lastName

string

—


arrival

ISO 8601

✅

Check-in timestamp.

departure

ISO 8601

✅

Check-out timestamp.

nrOfIndividuals

integer

—

Guest count, used for covers / buffet rules.

identifiers

Identifier[]

—

Keycard UIDs, NFC tags, room tablets etc. — any token the POS may receive when a guest wants to charge to a room.

Identifier

Field

Type

Description

value

string

The identifier value (keycard UID, token).

Example request

POST /hotelservice/api/v1/pms/reservations

{
  "reservations": [
    {
      "Id": "Aabc123",
      "hotelId": "1",
      "guestId": "Abc123",
      "roomNr": "Abc123",
      "title": "Mr.",
      "firstName": "Max",
      "lastName": "Mustermann",
      "arrival": "2026-04-02T14:00:00",
      "departure": "2026-04-04T14:00:00",
      "nrOfIndividuals": 1,
      "identifiers": [{ "value": "keycard-uid" }]
    }
  ]
}

Download: hotel-reservations-upsert.json

Example response

200 OK

{
  "newReservationsCount": 0,
  "checkedOutReservationsCount": 0,
  "updatedReservationsCount": 1,
  "state": 1,
  "isSuccessfull": true,
  "errorCode": 0,
  "message": null,
  "context": null
}

Download: hotel-reservations-response.json

Tip
Push the full active reservation set on every call, not deltas.
checkedOutReservationsCount reflects reservations the POS was tracking
but which are no longer in your payload — it removes them automatically.

2. Poll charges

Fetch everything booked against rooms since the last confirmed poll.

GET https://cloudgateway.apro.at/hotelservice/api/v1/pms/charges
    ?hotelId=1
    &excludeAlreadyExported=true

Query parameters

Name

Type

Required

Description

hotelId

string

✅

PMS tenant / property ID.

excludeAlreadyExported

bool

—

true = only return items not yet confirmed (the usual polling mode). false = re-fetch everything in the configured window.

Response shape

Each item has a chargeType that controls which fields are populated:

chargeType

Meaning

payments present?

Charge

An open line booked to the room (guest hasn't paid yet — POS is posting to the room folio).

no

Receipt

A closed receipt (guest settled at the POS; PMS may ignore or reconcile).

yes

Top-level envelope:

Field

Type

Description

page.limit

integer

Page size applied.

page.offset

integer

Offset into the backlog.

page.total

integer

Items still unconfirmed in total.

data

Item[]

Charges and receipts.

Item

Field

Type

Description

chargeType

`"Charge"

"Receipt"`

See table above.

bookDate

ISO 8601

When the line was booked in the POS.

receiptNr

integer

POS receipt number.

userId

integer

Operator / waiter ID.

userName

string

Operator name.

outlet

integer

Outlet ID (e.g. 1 = bar, 2 = restaurant).

tableNr

integer

Table number.

table

string

Table label.

totalAmount

decimal

Gross total of the line.

tip

decimal

Included tip.

businesscaseId

GUID

APRO transaction ID — use this as the transactionIds value to confirm.

reservationId

string | null

PMS reservation ID, for Charge items.

guestId

string | null

PMS guest ID.

roomNumber

string | null

Room number.

charges

LineItem[]

Individual article rows.

payments

Payment[]

Only on Receipt rows.

LineItem

Field

Type

Description

articleNr

integer

Product number.

articleName

string

Product name.

productgroupNr

integer

Category (Sparte) number.

productgroupName

string

Category name.

quantity

decimal

Quantity.

unitPrice

decimal

Gross unit price.

vatPercent

decimal

Applied VAT rate.

Example response

200 OK · charge + two receipts

{
  "page": { "limit": 3, "offset": 0, "total": 8096 },
  "data": [
    {
      "chargeType": "Charge",
      "bookDate": "2026-04-15T09:15:04.95",
      "receiptNr": 90,
      "userId": 20,
      "userName": "Johannes",
      "outlet": 1,
      "tableNr": 2,
      "table": "Bar 2",
      "totalAmount": 9.00,
      "tip": 0.30,
      "businesscaseId": "253c225f-7c4a-4d3f-80cd-63de4dbfca9e",
      "reservationId": "   15631",
      "guestId": "    6158",
      "roomNumber": "   414",
      "charges": [
        { "articleNr": 129,  "articleName": "Zipfer 0,2",     "productgroupName": "Bier",        "quantity": 1.00, "unitPrice": 1.10, "vatPercent": 20.00 },
        { "articleNr": 213,  "articleName": "Apfel ltg 0,5",  "productgroupName": "Flaschen AF", "quantity": 1.00, "unitPrice": 2.70, "vatPercent": 20.00 },
        { "articleNr": 1626, "articleName": "Würstel Pommes", "productgroupName": "Hauptspeise", "quantity": 1.00, "unitPrice": 4.90, "vatPercent": 10.00 }
      ]
    }
  ]
}

Download the full sample (Charge + two Receipts): hotel-charges-response.json

Caution
String fields like reservationId, guestId, roomNumber may be left-
padded with spaces when the POS stores them as fixed-width columns
(" 15631"). Trim on the PMS side before matching.

3. Confirm charges

Acknowledge the list you've successfully ingested. Once confirmed, the POS won't return those businesscaseIds on a subsequent poll with excludeAlreadyExported=true.

POST https://cloudgateway.apro.at/hotelservice/api/v1/pms/confirm-charges

Request body

Field

Type

Required

Description

hotelId

string

✅

PMS tenant / property ID.

transactionIds

GUID[]

✅

businesscaseId values from the previous /charges response.

POST /hotelservice/api/v1/pms/confirm-charges

{
  "hotelId": "1",
  "transactionIds": [
    "69F94B1A-3453-7017-8DDB-94E2175D0365",
    "155FFA87-DC19-EB1F-3DD8-540EF62A115D"
  ]
}

Download: hotel-confirm-charges.json

4. Remove a reservation

Explicit removal — useful at check-out or when the guest should no longer be able to charge to the room.

DELETE https://cloudgateway.apro.at/hotelservice/api/v1/pms/reservations
    ?hotelId=1
    &reservationId=Abc123
    &guestId=Abc123

All three query parameters are required. Either the targeted reservation is removed, or the call is a no-op if it doesn't exist.

Note
In practice the full-set upsert on step 1 is enough — any reservation
no longer present in the payload is checked out automatically
(checkedOutReservationsCount). Use this endpoint only when you need
an explicit out-of-band removal.

Idempotency & retries

  • POST /pms/reservations is idempotent on { hotelId, Id, guestId }. Re-sending the same payload converges to the same state.
  • POST /pms/confirm-charges is idempotent on transactionIds. Re-confirming an already-confirmed ID is a no-op.
  • Poll → confirm must be transactional in your own system: write the charges to your ledger first, then confirm. If the confirm fails, the same rows are returned next poll.

Errors

See Authentication → Error responses. On business-logic failures the response shape is:

{
  "isSuccessfull": false,
  "errorCode": 42,
  "message": "No reservation found for guestId 'Abc123'.",
  "context": { "guestId": "Abc123" }
}

Voucher API (Gutscheine)

The Voucher API is the programmatic interface to APRO's central voucher ledger — the same system that powers in-store gift cards, online voucher shops, and loyalty credit. Balances are authoritative server-side; every operation returns the updated voucher so the caller never has to track state locally.

Amounts

All monetary fields are integer minor units (cents) — 5000 means € 50.00. balance is the currently-available amount; amount is the original face value; maxAmount (optional) caps the ceiling for top-ups.

Voucher shape

The voucher object appears in every response. Fields:

Field

Type

Description

id

GUID

Internal APRO voucher ID. Use this in URL paths.

voucherCode

string

Human-readable code printed on the card/receipt.

gift.amount

integer

Original face value (minor units).

gift.balance

integer

Available balance (minor units).

gift.maxAmount

integer | null

Ceiling for top-ups; null = uncapped.

codeId

string | null

Optional secondary identifier.

startDate

ISO 8601 | null

Not valid before.

expirationDate

ISO 8601 | null

Not valid after.

enabled

bool

false = manually disabled/blocked.

comment

string | null

Free-form staff note.

categoryId

integer

Voucher category (issuing channel).

categoryName

string

Category display name.

categoryNrOfTimesRedeemablePerUnit

integer | null

Category-level redemption-count cap.

categoryExternalPaymentProviderId

string | null

Upstream payment-provider reference.

createdAt

ISO 8601

When the voucher was issued.

discriminator

string

"GiftVoucherDto" for gift cards.

1. List vouchers

GET https://cloudgateway.apro.at/voucherservice/api/v1/vouchers

Returns every voucher visible to the API key. Use the validity endpoint instead when you just need to look up one code — it's indexed for that path.

2. Check validity

GET https://cloudgateway.apro.at/voucherservice/api/v1/vouchers/validity
    ?voucherCode=560620085

Query parameters

Name

Type

Required

Description

voucherCode

string

✅

The code printed on the voucher.

Example response

200 OK

{
  "voucher": {
    "gift": { "amount": 5000, "balance": 3880, "maxAmount": null },
    "id": "f5f4d577-3764-4c25-12f8-08db73dad741",
    "voucherCode": "560620085",
    "codeId": null,
    "startDate": null,
    "expirationDate": null,
    "enabled": true,
    "comment": null,
    "categoryId": 4,
    "categoryName": "OnlineGutschein",
    "categoryNrOfTimesRedeemablePerUnit": null,
    "categoryExternalPaymentProviderId": null,
    "createdAt": "2023-06-23T11:13:15.6386753",
    "discriminator": "GiftVoucherDto"
  },
  "errors": [],
  "isValid": true
}

Download: voucher-validity-response.json

isValid: false + a populated errors array indicates why the voucher cannot be redeemed right now (expired, disabled, wrong date window, redemption cap reached). Common error codes:

Error

Meaning

NotFound

voucherCode does not exist.

Expired

Past expirationDate.

NotYetStarted

Before startDate.

Disabled

enabled = false.

NoBalance

balance == 0.

CategoryCapReached

categoryNrOfTimesRedeemablePerUnit exhausted.

3. Issue a voucher

POST https://cloudgateway.apro.at/voucherservice/api/v1/vouchers/gifts

Creates a new gift voucher. On success the HTTP status is 201 Created and the response Location header points at the new resource.

Request body

Field

Type

Required

Description

categoryId

integer

✅

Which voucher category to issue under.

voucherCode

string

✅

Code to print on the voucher. Must be unique.

comment

string

—

Staff note.

gift.amount

integer

✅

Face value in minor units.

gift.maxAmount

integer

—

Top-up ceiling.

clientType

string

✅

Identifier of the calling system. Provided by APRO for your integration — see clientType.

clientContext

string (JSON)

—

Serialised JSON for side-channel context (outlet etc.).

startDate

ISO 8601

—

Not valid before.

expirationDate

ISO 8601

—

Not valid after.

Example request

POST /voucherservice/api/v1/vouchers/gifts

{
  "categoryId": 1,
  "voucherCode": "2131231",
  "comment": "Gift card — reprint for a lost original",
  "gift": { "amount": 50, "maxAmount": 10000 },
  "clientType": "<provided-by-apro>",
  "clientContext": "{\"outletId\":\"231231\"}",
  "startDate": "2026-01-01T00:00:00",
  "expirationDate": "2028-12-31T23:59:59"
}

Download: voucher-issue-request.json

Example response

201 Created

{
  "data": {
    "gift": { "amount": 50, "balance": 50, "maxAmount": 10000 },
    "id": "e961c17d-d037-4d27-734d-08ddda944e39",
    "voucherCode": "2131231",
    "categoryId": 1,
    "categoryName": "Standard",
    "enabled": true,
    "comment": "Gift card — reprint for a lost original",
    "startDate": "2026-01-01T00:00:00",
    "expirationDate": "2028-12-31T23:59:59",
    "createdAt": "2026-04-20T21:33:00.000+02:00",
    "discriminator": "GiftVoucherDto"
  }
}

Download: voucher-issue-response.json

4. Redeem a voucher

POST https://cloudgateway.apro.at/voucherservice/api/v1/vouchers/{voucherId}/redemption

Burns amount minor units off the balance. Path parameter is the voucher's internal id (not the voucherCode) — you get it from /validity.

Request body

Field

Type

Required

Description

redemption.amount

integer

✅

Amount to debit in minor units.

clientReference

string

—

Opaque trace string (e.g. "{ \"waiterId\": 10 }"). Stored and returned on the transaction.

clientContext

string (JSON)

—

Side-channel context.

comment

string

—

Staff note.

clientType

string

✅

Identifier of the calling system. Provided by APRO — see clientType.

Example request

POST /voucherservice/api/v1/vouchers/{id}/redemption

{
  "redemption": { "amount": 50 },
  "clientReference": "{ \"waiterId\": 10 }",
  "clientContext": "{\"outletId\":\"231231\"}",
  "comment": "Redeemed at Bar 2 against receipt 5906",
  "clientType": "<provided-by-apro>"
}

Download: voucher-redeem-request.json

Example response

200 OK

{
  "data": {
    "redemption": { "amount": 50, "items": [] },
    "success": true,
    "errors": [],
    "transactionId": 21142,
    "voucher": {
      "gift": { "amount": 5000, "balance": 3830, "maxAmount": null },
      "id": "f5f4d577-3764-4c25-12f8-08db73dad741",
      "voucherCode": "560620085",
      "categoryId": 4,
      "categoryName": "OnlineGutschein",
      "enabled": true,
      "discriminator": "GiftVoucherDto"
    }
  }
}

Download: voucher-redeem-response.json

The transactionId (integer) is the authoritative ID for this redemption — persist it for reconciliation and refunds.

5. Top up a voucher

POST https://cloudgateway.apro.at/voucherservice/api/v1/vouchers/{voucherId}/booking

Adds amount minor units back onto the voucher, e.g. when a customer re-charges a multi-use card or a refund flows back.

Request body

Field

Type

Required

Description

amount

integer

✅

Amount to credit in minor units. Must respect gift.maxAmount if set.

clientReference

string

—

Opaque trace string.

clientType

string

✅

Identifier of the calling system. Provided by APRO — see clientType.

clientContext

string (JSON)

—

Side-channel context.

comment

string

—

Staff note.

Example request

POST /voucherservice/api/v1/vouchers/{id}/booking

{
  "amount": 1000,
  "clientReference": "{ \"waiterId\": 10 }",
  "clientType": "<provided-by-apro>",
  "clientContext": "{\"outletId\":\"231231\"}",
  "comment": "Top-up after customer added credit"
}

Download: voucher-booking-request.json

Example response

200 OK

{
  "data": {
    "newAmount": 4830,
    "success": true,
    "errors": [],
    "transactionId": 21143,
    "voucher": {
      "gift": { "amount": 5000, "balance": 4830, "maxAmount": null },
      "id": "f5f4d577-3764-4c25-12f8-08db73dad741",
      "voucherCode": "560620085",
      "categoryName": "OnlineGutschein",
      "enabled": true,
      "discriminator": "GiftVoucherDto"
    }
  }
}

Download: voucher-booking-response.json

clientType

clientType identifies the calling system so the voucher ledger knows which integration created a transaction. APRO assigns a clientType value to every integration at onboarding time — the exact string is not self-selectable and is shared with you together with the API key. Always use the value provided by APRO; never hard-code a guess.

Error handling

  • HTTP 4xx on validation failures (bad voucherId, malformed body, missing auth). See Authentication → Error responses.
  • HTTP 200 with success: false on business rule failures (insufficient balance, expired, etc.). Inspect the errors array.
  • Never replay a redemption on the client side. If a response times out, query /validity to confirm the balance before retrying — the operation may already be applied server-side.