{"openapi":"3.1.0","info":{"title":"Billy App-Plattform API","version":"1.1.0","description":"Externe, scope-geschützte API der Billy-Untereinheiten (IoT, Hotel, Arbitrage, Core, Buchhaltung).\n\n## Authentifizierung\nOAuth2. **Client-Credentials** (Maschine/Gateway) und **Authorization-Code + PKCE** (nutzer-delegiert).\nAccess-Token-Lebensdauer 3600 s; Refresh 2592000 s (rotierend); Auth-Code 300 s. Widerruf entwertet Access, Refresh und offene Codes.\n\n## Rate-Limits\nHarte Ratenbegrenzung: 120 Anfragen/min je App auf der Ext-API, 60/min je Client am Token-Endpoint (429 + Retry-After; via EXT_RATE_LIMIT_PER_MIN / EXT_TOKEN_RATE_LIMIT_PER_MIN uebersteuerbar); zusaetzliches IP-Prelimit am Token-Endpoint (120/min je IP, EXT_TOKEN_IP_RATE_LIMIT_PER_MIN) gegen rotierende Client-IDs.\n\n## Paginierung\nListen-Endpunkte akzeptieren ?limit=; Standard 20, keine Cursor.\n\n## Fehler\nAlle Fehler haben die Form `{ error, detail? }`. Codes: 400 → unsupported_grant_type, invalid_scope, invalid_grant · 401 → unauthorized, invalid_token, expired_token, revoked_client, invalid_client · 403 → insufficient_scope, module_disabled · 429 → rate_limited."},"servers":[{"url":"https://billy.servus.taxi","description":"Instanz"}],"tags":[{"name":"oauth","description":"OAuth2/Discovery"},{"name":"core","description":"Billy Core"},{"name":"iot","description":"IoT-Einheit"},{"name":"hotel","description":"Hotel-Einheit"},{"name":"arbitrage","description":"Arbitrage-Einheit"},{"name":"accounting","description":"Buchhaltung"}],"components":{"securitySchemes":{"oauth2":{"type":"oauth2","flows":{"clientCredentials":{"tokenUrl":"https://billy.servus.taxi/api/platform/token","scopes":{"billy:read":"Billy-Einblicke lesen","billy:write":"Billy-Vorschläge/Feedback schreiben","iot:read":"IoT-Messpunkte & Werte lesen","iot:write":"IoT-Werte einspielen (Ingest)","hotel:read":"Hotel-Übersicht & Zimmer lesen","hotel:write":"Hotel-Buchungen/Umsätze schreiben","arbitrage:read":"Arbitrage-Übersicht & Vorschläge lesen","arbitrage:write":"Arbitrage-Vorschläge bestätigen","accounting:read":"Rechnungs-/Buchhaltungs-Übersicht lesen"}},"authorizationCode":{"authorizationUrl":"https://billy.servus.taxi/api/platform/authorize","tokenUrl":"https://billy.servus.taxi/api/platform/token","scopes":{"billy:read":"Billy-Einblicke lesen","billy:write":"Billy-Vorschläge/Feedback schreiben","iot:read":"IoT-Messpunkte & Werte lesen","iot:write":"IoT-Werte einspielen (Ingest)","hotel:read":"Hotel-Übersicht & Zimmer lesen","hotel:write":"Hotel-Buchungen/Umsätze schreiben","arbitrage:read":"Arbitrage-Übersicht & Vorschläge lesen","arbitrage:write":"Arbitrage-Vorschläge bestätigen","accounting:read":"Rechnungs-/Buchhaltungs-Übersicht lesen"}}}}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string"},"detail":{"type":"string"},"required":{"type":"string"},"granted":{"type":"array","items":{"type":"string"}},"retry_after_s":{"type":"integer"}},"required":["error"]},"TokenRequest":{"type":"object","required":["grant_type"],"properties":{"grant_type":{"type":"string","enum":["client_credentials","authorization_code","refresh_token"]},"client_id":{"type":"string"},"client_secret":{"type":"string"},"scope":{"type":"string"},"code":{"type":"string"},"redirect_uri":{"type":"string"},"code_verifier":{"type":"string"},"refresh_token":{"type":"string"}}},"TokenResponse":{"type":"object","properties":{"access_token":{"type":"string"},"token_type":{"type":"string"},"expires_in":{"type":"integer"},"scope":{"type":"string"},"refresh_token":{"type":"string"}}},"IotDevice":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"type_label":{"type":"string"},"unit":{"type":"string"},"location":{"type":"string"},"room":{"type":"string"},"value":{"type":["number","null"]},"ts":{"type":["integer","null"]},"age_min":{"type":["integer","null"]},"status":{"type":"string","enum":["ok","warn","alarm","stale"]},"breach":{"type":["string","null"]}}},"IotOverview":{"type":"object","properties":{"summary":{"type":"object","properties":{"devices":{"type":"integer"},"ok":{"type":"integer"},"warn":{"type":"integer"},"alarm":{"type":"integer"},"stale":{"type":"integer"},"attention":{"type":"integer"}}},"devices":{"type":"array","items":{"$ref":"#/components/schemas/IotDevice"}},"by_type":{"type":"array","items":{"type":"object"}},"types":{"type":"array","items":{"type":"object"}}}},"Reading":{"type":"object","properties":{"ts":{"type":"integer"},"value":{"type":"number"}}},"IotDeviceDetail":{"type":"object","properties":{"ok":{"type":"boolean"},"device":{"type":"object"},"value":{"type":["number","null"]},"ts":{"type":["integer","null"]},"status":{"type":"string"},"breach":{"type":["string","null"]},"age_min":{"type":["integer","null"]},"history":{"type":"array","items":{"$ref":"#/components/schemas/Reading"}}}},"IotIngestRequest":{"type":"object","required":["device_id","value"],"properties":{"device_id":{"type":"string"},"value":{"type":"number"},"ts":{"type":"string","description":"ISO-8601; fehlt = jetzt"},"type":{"type":"string","description":"nur beim Anlegen unbekannter Geräte"},"unit":{"type":"string"}}},"IngestResult":{"type":"object","properties":{"ok":{"type":"boolean"},"device":{"type":"string"},"value":{"type":"number"},"ts":{"type":"integer"},"status":{"type":"string"},"breach":{"type":["string","null"]}}},"IotEventRequest":{"type":"object","properties":{"status":{"type":"string"},"event":{"type":"string"},"state":{"type":"string"},"ts":{"type":"string"},"meta":{"type":"object"}}},"IotFolioPostRequest":{"type":"object","properties":{"room":{"type":"string"},"reservation_id":{"type":"string"},"qty":{"type":"number"},"unit_price":{"type":"number"},"amount":{"type":"number"},"description":{"type":"string"},"category":{"type":"string"},"house_account":{"type":"boolean"}}},"OcppFrameRequest":{"type":"object","required":["frame"],"properties":{"frame":{"type":["array","object"],"description":"OCPP 1.6J JSON frame oder Wrapper des Gateways."}}},"OcppFolioPostRequest":{"type":"object","properties":{"room":{"type":"string"},"reservation_id":{"type":"string"},"price_per_kwh":{"type":"number"},"description":{"type":"string"},"house_account":{"type":"boolean"}}},"GenericOkResult":{"type":"object","properties":{"ok":{"type":"boolean"},"id":{"type":"string"},"error":{"type":"string"},"result":{"type":"object"}}},"EnergyReport":{"type":"object","properties":{"ok":{"type":"boolean"},"device":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"period":{"type":"string","enum":["day","month"]},"price_per_kwh":{"type":"number"},"buckets":{"type":"array","items":{"type":"object","properties":{"bucket":{"type":"string"},"kwh":{"type":"number"},"cost":{"type":"number"}}}},"total_kwh":{"type":"number"},"total_cost":{"type":"number"}}},"HotelOverview":{"type":"object","properties":{"summary":{"type":"object","properties":{"rooms_total":{"type":"integer"},"occupied":{"type":"integer"},"free":{"type":"integer"},"cleaning":{"type":"integer"},"maintenance":{"type":"integer"},"occupancy_pct":{"type":"integer"},"revenue_today":{"type":"number"},"checkout_due":{"type":"integer"}}},"rooms":{"type":"array","items":{"type":"object"}},"revenue_by_category":{"type":"array","items":{"type":"object"}}}},"HotelDayFolio":{"type":"object","properties":{"ok":{"type":"boolean"},"date":{"type":"string"},"room":{"type":"object"},"entries":{"type":"array","items":{"type":"object"}},"by_category":{"type":"array","items":{"type":"object","properties":{"category":{"type":"string"},"label":{"type":"string"},"count":{"type":"integer"},"amount":{"type":"number"}}}},"total":{"type":"number"},"count":{"type":"integer"}}},"HotelChargeRequest":{"type":"object","required":["category"],"properties":{"category":{"type":"string","enum":["room","restaurant","spa","minibar","pay_tv","excursion","taxi","laundry","phone","other"]},"description":{"type":"string"},"qty":{"type":"number"},"unit_price":{"type":"number"},"amount":{"type":"number"},"date":{"type":"string"},"vat_rate":{"type":"number"}}},"ChargeResult":{"type":"object","properties":{"ok":{"type":"boolean"},"charge":{"type":"object"},"error":{"type":"string"}}},"ArbitrageOverview":{"type":"object","description":"Kennzahlen des Arbitrage-Hubs."},"ArbitrageProposals":{"type":"object","properties":{"proposals":{"type":"array","items":{"type":"object"}}}},"ArbitrageGenerateRequest":{"type":"object","properties":{"min_margin_pct":{"type":"number"},"limit":{"type":"integer"},"channel":{"type":"string"},"category":{"type":"string"}}},"ArbitrageActionRequest":{"type":"object","properties":{"note":{"type":"string"},"approved_by":{"type":"string"},"dry_run":{"type":"boolean"}}},"ArbitrageActionResult":{"type":"object","properties":{"ok":{"type":"boolean"},"proposal":{"type":"object"},"error":{"type":"string"}}},"AccountingOverview":{"type":"object","properties":{"month":{"type":"string"},"ar":{"type":"object"},"ap":{"type":"object"},"month_summary":{"type":"object"},"calendar":{"type":"object"},"sections":{"type":"array","items":{"type":"object"}}}},"BillyInsights":{"type":"object","properties":{"insights":{"type":"array","items":{"type":"object"}}}}}},"security":[{"oauth2":[]}],"paths":{"/api/platform/token":{"post":{"summary":"OAuth2 Token-Endpoint (client_credentials, authorization_code, refresh_token)","tags":["oauth"],"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenRequest"}}}},"responses":{"200":{"description":"Access-Token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}},"400":{"description":"unsupported_grant_type / invalid_scope / invalid_grant (inkl. pkce_failed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"invalid_client","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited (Token-Endpoint-Limit je Client)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/platform/authorize":{"get":{"summary":"Consent-Seite des Authorization-Code-Flows (PKCE)","tags":["oauth"],"security":[],"parameters":[{"name":"response_type","in":"query","required":true,"schema":{"type":"string"}},{"name":"client_id","in":"query","required":true,"schema":{"type":"string"}},{"name":"redirect_uri","in":"query","required":true,"schema":{"type":"string"}},{"name":"scope","in":"query","required":false,"schema":{"type":"string"}},{"name":"state","in":"query","required":false,"schema":{"type":"string"}},{"name":"code_challenge","in":"query","required":true,"schema":{"type":"string"}},{"name":"code_challenge_method","in":"query","required":true,"schema":{"type":"string","enum":["S256"]}}],"responses":{"200":{"description":"HTML-Consent"},"302":{"description":"Redirect zu redirect_uri mit code+state"},"401":{"description":"Nutzer nicht angemeldet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/platform/catalog":{"get":{"summary":"Discovery: Scopes, Units, Konventionen","tags":["oauth"],"security":[],"responses":{"200":{"description":"Katalog"}}}},"/api/platform/openapi.json":{"get":{"summary":"Dieses OpenAPI-Dokument","tags":["oauth"],"security":[],"responses":{"200":{"description":"OpenAPI 3.1"}}}},"/api/ext/v1/billy/insights":{"get":{"summary":"Aktuelle Vorschläge/Warnungen (Insights).","tags":["core"],"operationId":"get_api_ext_v1_billy_insights","security":[{"oauth2":["billy:read"]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillyInsights"}}}},"401":{"description":"unauthorized / invalid_token / expired_token / revoked_client","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"insufficient_scope (nötig: billy:read) / module_disabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited — Limit je App überschritten (X-RateLimit-*, Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/ext/v1/iot/overview":{"get":{"summary":"Alle Messpunkte mit letztem Wert + Status.","tags":["iot"],"operationId":"get_api_ext_v1_iot_overview","security":[{"oauth2":["iot:read"]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IotOverview"}}}},"401":{"description":"unauthorized / invalid_token / expired_token / revoked_client","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"insufficient_scope (nötig: iot:read) / module_disabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited — Limit je App überschritten (X-RateLimit-*, Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/ext/v1/iot/devices/{id}":{"get":{"summary":"Ein Messpunkt inkl. Verlauf.","tags":["iot"],"operationId":"get_api_ext_v1_iot_devices_id","security":[{"oauth2":["iot:read"]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IotDeviceDetail"}}}},"401":{"description":"unauthorized / invalid_token / expired_token / revoked_client","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"insufficient_scope (nötig: iot:read) / module_disabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited — Limit je App überschritten (X-RateLimit-*, Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]}},"/api/ext/v1/iot/ingest":{"post":{"summary":"Wert einspielen: { device_id, value, ts? }. Unbekannte Geräte werden angelegt.","tags":["iot"],"operationId":"post_api_ext_v1_iot_ingest","security":[{"oauth2":["iot:write"]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IngestResult"}}}},"401":{"description":"unauthorized / invalid_token / expired_token / revoked_client","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"insufficient_scope (nötig: iot:write) / module_disabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited — Limit je App überschritten (X-RateLimit-*, Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IotIngestRequest"}}}}}},"/api/ext/v1/iot/devices/{id}/event":{"post":{"summary":"Status-/Tür-/Smart-Lock-/Gateway-Event schreiben: { status, event, ts? }.","tags":["iot"],"operationId":"post_api_ext_v1_iot_devices_id_event","security":[{"oauth2":["iot:write"]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenericOkResult"}}}},"401":{"description":"unauthorized / invalid_token / expired_token / revoked_client","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"insufficient_scope (nötig: iot:write) / module_disabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited — Limit je App überschritten (X-RateLimit-*, Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IotEventRequest"}}}}}},"/api/ext/v1/iot/devices/{id}/post-to-folio":{"post":{"summary":"Abrechenbares IoT-Event auf ein Hotel-Folio buchen.","tags":["iot"],"operationId":"post_api_ext_v1_iot_devices_id_post_to_folio","security":[{"oauth2":["iot:write"]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenericOkResult"}}}},"401":{"description":"unauthorized / invalid_token / expired_token / revoked_client","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"insufficient_scope (nötig: iot:write) / module_disabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited — Limit je App überschritten (X-RateLimit-*, Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IotFolioPostRequest"}}}}}},"/api/ext/v1/iot/energy":{"get":{"summary":"Energie-Aggregation (Tages-/Monats-kWh + Kosten); ?device=&period=day|month&price=.","tags":["iot"],"operationId":"get_api_ext_v1_iot_energy","security":[{"oauth2":["iot:read"]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnergyReport"}}}},"401":{"description":"unauthorized / invalid_token / expired_token / revoked_client","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"insufficient_scope (nötig: iot:read) / module_disabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited — Limit je App überschritten (X-RateLimit-*, Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/ext/v1/ocpp/{chargerId}/frame":{"post":{"summary":"OCPP 1.6J Frame einer Ladestation einspielen.","tags":["iot"],"operationId":"post_api_ext_v1_ocpp_chargerId_frame","security":[{"oauth2":["iot:write"]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenericOkResult"}}}},"401":{"description":"unauthorized / invalid_token / expired_token / revoked_client","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"insufficient_scope (nötig: iot:write) / module_disabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited — Limit je App überschritten (X-RateLimit-*, Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"chargerId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OcppFrameRequest"}}}}}},"/api/ext/v1/ocpp/{chargerId}/commands":{"get":{"summary":"Anstehende Server→Lader-Kommandos abholen (OCPP-CALL-Frames, z. B. SetChargingProfile).","tags":["iot"],"operationId":"get_api_ext_v1_ocpp_chargerId_commands","security":[{"oauth2":["iot:write"]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"unauthorized / invalid_token / expired_token / revoked_client","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"insufficient_scope (nötig: iot:write) / module_disabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited — Limit je App überschritten (X-RateLimit-*, Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"chargerId","in":"path","required":true,"schema":{"type":"string"}}]}},"/api/ext/v1/ocpp/sessions/{id}/post-to-folio":{"post":{"summary":"EV-Ladesession nach StopTransaction auf ein Hotel-Folio buchen.","tags":["iot"],"operationId":"post_api_ext_v1_ocpp_sessions_id_post_to_folio","security":[{"oauth2":["iot:write"]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenericOkResult"}}}},"401":{"description":"unauthorized / invalid_token / expired_token / revoked_client","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"insufficient_scope (nötig: iot:write) / module_disabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited — Limit je App überschritten (X-RateLimit-*, Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OcppFolioPostRequest"}}}}}},"/api/ext/v1/iot/prices":{"post":{"summary":"Strombörsen-Preisslots einspielen (Day-Ahead; slots:[{start,end,ct}] in ct/kWh).","tags":["iot"],"operationId":"post_api_ext_v1_iot_prices","security":[{"oauth2":["iot:write"]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"unauthorized / invalid_token / expired_token / revoked_client","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"insufficient_scope (nötig: iot:write) / module_disabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited — Limit je App überschritten (X-RateLimit-*, Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/ext/v1/hotel/overview":{"get":{"summary":"Zimmerstatus, Auslastung, Tagesumsatz.","tags":["hotel"],"operationId":"get_api_ext_v1_hotel_overview","security":[{"oauth2":["hotel:read"]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelOverview"}}}},"401":{"description":"unauthorized / invalid_token / expired_token / revoked_client","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"insufficient_scope (nötig: hotel:read) / module_disabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited — Limit je App überschritten (X-RateLimit-*, Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/ext/v1/hotel/rooms/{id}/day":{"get":{"summary":"Tagesblatt eines Zimmers.","tags":["hotel"],"operationId":"get_api_ext_v1_hotel_rooms_id_day","security":[{"oauth2":["hotel:read"]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelDayFolio"}}}},"401":{"description":"unauthorized / invalid_token / expired_token / revoked_client","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"insufficient_scope (nötig: hotel:read) / module_disabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited — Limit je App überschritten (X-RateLimit-*, Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}]}},"/api/ext/v1/hotel/rooms/{id}/charges":{"post":{"summary":"Umsatz buchen: { category, description, qty, unit_price }.","tags":["hotel"],"operationId":"post_api_ext_v1_hotel_rooms_id_charges","security":[{"oauth2":["hotel:write"]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChargeResult"}}}},"401":{"description":"unauthorized / invalid_token / expired_token / revoked_client","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"insufficient_scope (nötig: hotel:write) / module_disabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited — Limit je App überschritten (X-RateLimit-*, Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelChargeRequest"}}}}}},"/api/ext/v1/arbitrage/overview":{"get":{"summary":"Kennzahlen des Arbitrage-Hubs.","tags":["arbitrage"],"operationId":"get_api_ext_v1_arbitrage_overview","security":[{"oauth2":["arbitrage:read"]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArbitrageOverview"}}}},"401":{"description":"unauthorized / invalid_token / expired_token / revoked_client","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"insufficient_scope (nötig: arbitrage:read) / module_disabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited — Limit je App überschritten (X-RateLimit-*, Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/ext/v1/arbitrage/proposals":{"get":{"summary":"Offene Billy-Vorschläge.","tags":["arbitrage"],"operationId":"get_api_ext_v1_arbitrage_proposals","security":[{"oauth2":["arbitrage:read"]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArbitrageProposals"}}}},"401":{"description":"unauthorized / invalid_token / expired_token / revoked_client","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"insufficient_scope (nötig: arbitrage:read) / module_disabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited — Limit je App überschritten (X-RateLimit-*, Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/ext/v1/arbitrage/proposals/generate":{"post":{"summary":"Neue Billy-Vorschläge generieren.","tags":["arbitrage"],"operationId":"post_api_ext_v1_arbitrage_proposals_generate","security":[{"oauth2":["arbitrage:write"]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArbitrageProposals"}}}},"401":{"description":"unauthorized / invalid_token / expired_token / revoked_client","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"insufficient_scope (nötig: arbitrage:write) / module_disabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited — Limit je App überschritten (X-RateLimit-*, Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArbitrageGenerateRequest"}}}}}},"/api/ext/v1/arbitrage/proposals/{id}/confirm":{"post":{"summary":"Billy-Vorschlag bestätigen und veröffentlichen.","tags":["arbitrage"],"operationId":"post_api_ext_v1_arbitrage_proposals_id_confirm","security":[{"oauth2":["arbitrage:write"]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArbitrageActionResult"}}}},"401":{"description":"unauthorized / invalid_token / expired_token / revoked_client","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"insufficient_scope (nötig: arbitrage:write) / module_disabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited — Limit je App überschritten (X-RateLimit-*, Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArbitrageActionRequest"}}}}}},"/api/ext/v1/arbitrage/proposals/{id}/reject":{"post":{"summary":"Billy-Vorschlag ablehnen.","tags":["arbitrage"],"operationId":"post_api_ext_v1_arbitrage_proposals_id_reject","security":[{"oauth2":["arbitrage:write"]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArbitrageActionResult"}}}},"401":{"description":"unauthorized / invalid_token / expired_token / revoked_client","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"insufficient_scope (nötig: arbitrage:write) / module_disabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited — Limit je App überschritten (X-RateLimit-*, Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArbitrageActionRequest"}}}}}},"/api/ext/v1/accounting/overview":{"get":{"summary":"AR/AP, Periode, Kalenderblatt.","tags":["accounting"],"operationId":"get_api_ext_v1_accounting_overview","security":[{"oauth2":["accounting:read"]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountingOverview"}}}},"401":{"description":"unauthorized / invalid_token / expired_token / revoked_client","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"insufficient_scope (nötig: accounting:read) / module_disabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rate_limited — Limit je App überschritten (X-RateLimit-*, Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}