# Gastmanager Booking Portal API A read-only JSON API for AI agents to discover availability and pricing of a Gastmanager booking portal. The agent can NOT create bookings, take payments, or write any data — use the interactive booking portal for that. Instead, every priced response carries a `booking_url` deeplink: hand it to the human to finish the booking. The example URLs below are live — fetch them as-is. ## Available Portals ### Camping (`camping`) * Config: GET https://buchen.digitalbits.dev/booking/api/v1/portals/camping * Categories: GET https://buchen.digitalbits.dev/booking/api/v1/portals/camping/categories?from=2026-08-01&to=2026-08-08&guests[]=adult&guests[]=adult * Quote: POST https://buchen.digitalbits.dev/booking/api/v1/portals/camping/quote * Book (human): https://buchen.digitalbits.dev/booking/camping ### Hotel (`hotel`) * Config: GET https://buchen.digitalbits.dev/booking/api/v1/portals/hotel * Categories: GET https://buchen.digitalbits.dev/booking/api/v1/portals/hotel/categories?from=2026-08-01&to=2026-08-08&guests[]=adult&guests[]=adult * Quote: POST https://buchen.digitalbits.dev/booking/api/v1/portals/hotel/quote * Book (human): https://buchen.digitalbits.dev/booking/hotel ## Quickstart Using the portal `camping` as an example: 1. Load the portal config: GET https://buchen.digitalbits.dev/booking/api/v1/portals/camping 2. Check availability for a stay: GET https://buchen.digitalbits.dev/booking/api/v1/portals/camping/categories?from=2026-08-01&to=2026-08-08&guests[]=adult&guests[]=adult 3. Get a price preview: POST https://buchen.digitalbits.dev/booking/api/v1/portals/camping/quote Content-Type: application/json Body: { "timeframe": {...}, "guests_per_slot": [...], "selection": [...] } ## Example Responses `GET …/portals/camping` → { "data": { "identifier": "camping", "name": "Summer Campsite", "languages": ["de", "en"], "payment_methods": [{ "id": 1 }], "max_accommodations": 3, "has_extras": true, "blocked_periods": [], "url": "https://buchen.digitalbits.dev/booking/camping" } } `GET …/categories?from=2026-08-01&to=2026-08-08` → the `available` boolean is the availability flag; `booking_url` opens that category with the search prefilled: { "data": [ { "id": 12, "external_id": "STANDARD", "name": "Standard Pitch", "max_guests": 6, "available": true, "selection_options": [ { "id": null, "type": "default", "name": "Standard" } ], "extras": [ { "id": 42, "external_id": "BREAKFAST", "type": "consumable", "name": "Breakfast" } ], "price": { "amount": 18000, "currency": "EUR", "gross": 180.0 }, "booking_url": "https://buchen.digitalbits.dev/booking/camping/category/12?guests=2&timeframe=2026-08-01:2026-08-08" } ] } `POST …/quote` → { "data": { "timeframe": { "from": "2026-08-01", "to": "2026-08-08" }, "total": { "amount": 19400, "currency": "EUR", "gross": 194.0 }, "net_total": { "amount": 17636, "currency": "EUR", "gross": 176.36 }, "taxes": { "amount": 1764, "currency": "EUR", "gross": 17.64 }, "tax_rates": [ { "rate": 10, "taxes": { "amount": 1764, "currency": "EUR", "gross": 17.64 } } ], "items": [ { "name": "Standard Pitch", "type": "accommodation", "amount": 7, "total": { "amount": 18000, "currency": "EUR", "gross": 180.0 }, "unit_price": { "amount": 18000, "currency": "EUR", "gross": 180.0 } } ], "booking_url": "https://buchen.digitalbits.dev/booking/camping?guests=2:10|1&selection=c.STANDARD&timeframe=2026-08-01:2026-08-08" } } ## Defaults Categories endpoint query-params default to: * `guests=adult,adult` (two adults) * `dogs=0` * `vehicles=[]` ## Selection JSON Format The `selection` array contains one entry per selected accommodation (`mode: "category"`) plus optional booking-wide entries (`mode: "global"`). The order of `mode: "category"` entries MUST match the order of `guests_per_slot` 1:1 — the first category-mode selection is for the first guests slot, etc. `global` entries are not slot-bound; they apply to the booking as a whole. Fields per selection item: * `mode` (required) — `"category"` or `"global"`. * `category_id` (required for `mode: "category"`) — internal accommodation-group ID from the categories endpoint (the `id` field, not `external_id`). * `accommodation_id` (optional) — lock the selection to a specific accommodation (plot/unit) inside the category. * `group_id` (optional) — lock to a plot-plan area. * `extras` (optional, list) — extras and at most one package for this slot. Each entry has `type`, `id`, optional `modifier` (see "Modifier Kinds" below). At most one entry per slot may have `type: "package"`. Full example body for the quote endpoint: { "timeframe": { "from": "2026-08-01", "to": "2026-08-08" }, "guests_per_slot": [ { "people": ["adult", "adult", 10], "dogs": 1, "vehicles": [1] } ], "selection": [ { "mode": "category", "category_id": 12, "extras": [ { "type": "package", "id": 7 }, { "type": "consumable", "id": 42, "modifier": { "kind": "amount", "value": 14 } } ] }, { "mode": "global", "extras": [ { "type": "service", "id": 99 } ] } ] } ## Modifier Kinds Modifiers configure how an extra is applied. There are six kinds. All datetimes are naive ISO 8601 (no timezone); the server interprets them in Europe/Vienna. * `guests` — apply to specific guests (by 0-based index into the slot's `people` array). All indices must be valid. Example: `{ "kind": "guests", "indices": [0, 2] }` * `amount` — set a positive quantity for the extra. Example: `{ "kind": "amount", "value": 14 }` * `daterange` — only apply on dates in [from, to). `from` must be strictly before `to`. Example: `{ "kind": "daterange", "from": "2026-08-02", "to": "2026-08-05" }` * `date_amounts` — different quantity per date. Example: `{ "kind": "date_amounts", "entries": [ { "date": "2026-08-02", "amount": 3 }, { "date": "2026-08-03", "amount": 5 } ] }` * `datetime` — pin to a specific naive datetime. Example: `{ "kind": "datetime", "value": "2026-08-01T18:30:00" }` * `time_window` — naive `start` plus `HH:MM:SS` `duration`. Example: `{ "kind": "time_window", "start": "2026-08-01T18:00:00", "duration": "02:00:00" }` ## Booking Links (deeplink format) The API is read-only, so the highest-value output is a prefilled `booking_url` you can hand to the human. Every category entry and the quote response already contain one. If you want to build a link yourself, it is the interactive portal URL plus these query params: * `timeframe=FROM:TO` — ISO dates, e.g. `2026-08-01:2026-08-08`. Variants: `FROM:+N` (N nights), `…|2` (exactly 2 nights within the range), `…|2-3` (2–3 nights), `!FROM` / `!TO` (fixed arrival/departure), `…|a3` (also search ±3 nights for alternatives). * `guests=ADULTS[:CHILDAGES][,Ddogs][|Vtype,…]`, slots separated by `+`. Examples: `2` (two adults); `2:4,5` (two adults + children aged 4 and 5); `2:4,5,D2|V1` (… + 2 dogs + one vehicle of type 1); `2+3` (slot 1 two adults, slot 2 three adults). * `selection=c.EXTERNAL_ID[.ACCOMMODATION_ID][:p.PKG,x.EXTRA~MODIFIER]`, slots separated by `+`. `c.` = category (by `external_id`), `p.` = package, `x.` = extra. Example: `c.STANDARD:p.SUMMER-DEAL,x.BREAKFAST~G0,G2+c.DELUXE`. Example deeplink: https://buchen.digitalbits.dev/booking/camping/category/12?guests=2:10|1&timeframe=2026-08-01:2026-08-08 ## Error Codes All errors follow the same shape: `{ "error": { "code": ..., "message": ..., "field"?: ..., "retry_after"?: ... } }` * `invalid_param` (HTTP 400) — request validation failed; `field` names the offending parameter. * `not_found` (HTTP 404) — portal or resource not found (also used for inactive portals to avoid leaking existence). * `rate_limited` (HTTP 429) — too many requests from this IP; the `retry_after` field tells you how many seconds to wait. * `internal` (HTTP 500) — unexpected server error. ## Rate Limits Per-IP, 60 second sliding window: * Portal endpoint — 60 requests/min * Categories endpoint — 30 requests/min * Quote endpoint — 20 requests/min * Discovery (openapi/llms/ai-plugin) — 120 requests/min * Global cap across all endpoints — 100 requests/min ## Conventions * Dates are ISO 8601 (`YYYY-MM-DD`). * Datetimes are naive ISO 8601 (`YYYY-MM-DDTHH:MM:SS`) — no timezone suffix; interpreted in Europe/Vienna server-side. * Money is reported as integer minor unit (`amount`, e.g. cents) plus a decimal convenience field (`gross`). * No authentication required — rate-limited per source IP. * The client is identified by the request host (`.gastmanager.`); there is no client path segment. ## Discovery * OpenAPI spec: https://buchen.digitalbits.dev/booking/api/v1/openapi.json * AI-plugin manifest: https://buchen.digitalbits.dev/booking/.well-known/ai-plugin.json * This file: https://buchen.digitalbits.dev/booking/llms.txt