# CMS API Endpoints TYPO3 serves to other systems: **MyE&P** (hotel and product content, calendar hotels) and the **chatbot** (content feed). They are answered by `EP\EpProducts\Middleware\ApiMiddleware` before the page resolver, so they do not depend on a page and are never page-cached. The other side of these contracts is documented in MyE&P's `docs/api-consumer-guide.md` (`EP/myep`). ## Authentication All endpoints need the shared key `cmsApiKey` from the `ep_products` extension configuration (Settings → Extension Configuration → ep_products, tab ContentFeed): - header `X-Api-Key: ` (preferred, query strings end up in access logs), or - query parameter `?key=`. A missing or wrong key answers `403 {"message": "Invalid or missing api key"}`. An empty `cmsApiKey` denies every request. MyE&P sends the key as `APP_CMS_API_KEY`; both sides must be changed together. ## `GET /api/hotel?hotel=` Content of one hotel, for MyE&P's group house pages and booking flow. - `hotel`: the TYPO3 hotel code or one of its matchcode aliases. In MyE&P this is the accommodation's **CMS code** (e.g. `DGSGRP`), which can differ from its calendar code. - `400` without `hotel`, `404 {"message": "Hotel not found"}` for an unknown code. ```json { "name": "…", "code": "…", "description": "…", "features": "…", "room_types": "…", "additional_information": "…", "address": "…", "images": {"original": [{"url": "…", "alt": "…", "title": "…"}], "resized": […]}, "icons": […], "region": {"name": "…", "latitude": …, "longitude": …, "webcam": "…", "ski_area": "…", "ski_area_extended": "…", "description": "…", "news": "…", "length": …, "altitude": …, "lifts": …, "images": {…}, "region_maps": [{"url": "…", "alt": "…", "title": "…"}]} } ``` ## `GET /api/product?product=[&hotel=]` Content of one product, optionally with a hotel. - `product`: the product code. `400` without it, `404 {"message": "Product not found"}` for an unknown code. - `hotel` (optional): adds `hotel` with the same content as `/api/hotel`. ```json { "product": {"name": "…", "code": "…", "images": {…}}, "country": {"name": "…"}, "region": {…}, "city": {"name": "…"}, "hotel": {…} } ``` ## `GET /api/calendar-hotels` The calendar hotel of every visible product: the hotel codes whose availability TYPO3 shows (iCal feed, availability calendar, price calendar). MyE&P syncs its contingent snapshot for these codes in addition to its accommodations, so hotels offered only in winter, which have no accommodation in MyE&P, have availability too. See [calendars.md](calendars.md). - Only products and hotels that are not hidden or deleted; hotels without a code are left out. - Sorted by code; products by uid. Names have line breaks collapsed. - `season`: `"s"` (summer), `"w"` (winter) or `null`. ```json [ {"code": "DKI", "name": "Sportclub Schwendi", "products": [ {"uid": 155, "name": "Sportclub Schwendi", "season": "w"}, {"uid": 362, "name": "Gruppenhaus Schwendi", "season": "s"} ]} ] ``` Every code in this list should be a **BusPro calendar code**. A group house's content record (`…GRP`, `…SO`) set as a product's calendar hotel puts its CMS code in here, which bpn-connect does not know (see [calendars.md](calendars.md#the-calendar-hotel-field)). ## `GET /api/content/manifest` and `GET /api/content/records?type=&uids=` The content feed for the chatbot (`EP\EpProducts\ContentFeed\ContentFeedApi`). - `manifest`: everything the feed publishes (pages of the configured sites and records), with a modification timestamp per item. Cached for 10 minutes. - `records`: up to 100 records of one type per request. `type` is one of `product`, `hotel`, `region`, `country`, `city`, `concept`, `news`; `uids` comma-separated. Uids that are no longer eligible are left out, not answered with an error (the consumer reads them as deleted). - `400` for an unknown type, no uids or more than 100 uids; `404` for other paths. - `503` with `Retry-After: 3600` when the feed is switched off (`contentFeedEnabled = 0`, the kill switch for when the host is struggling), `503` when `contentFeedSites` is empty. The consumer keeps what it has on a 503; nothing is deleted on the chatbot side. Configuration (extension configuration `ep_products`, tab ContentFeed): `contentFeedEnabled`, `contentFeedSites`, `contentFeedLanguages`, `contentFeedExcludedPages`, `contentFeedNewsDetailPages`.