Skip to content

REST API

Alexander Elchlepp edited this page Aug 5, 2026 · 3 revisions

REST API

FewohBee provides a REST API to query data through a well-defined interface — e.g. for custom reports or for subscribing to the occupancy calendar from calendar applications.

Table of contents

Authentication

The API uses personal access tokens. A token always starts with fwb_ and can be used in two ways:

  1. Bearer header (recommended for scripts and integrations):
    Authorization: Bearer fwb_...
    
  2. Basic auth (for applications that only support username/password, e.g. calendar clients): username = your FewohBee username, password = the token. Your actual login password is never accepted by the API.

A token never grants more permissions than the user it belongs to. If a role is removed from the user or the user is deactivated, all of their tokens immediately lose access as well.

Creating an API token

  1. Sign in to FewohBee and open your profile.
  2. In the API access section, click Create API token.
  3. Choose a name, an expiry and the permissions.
  4. The token is displayed only once — copy it now and store it securely. It cannot be viewed again afterwards.

A token can be revoked (deleted) in the profile at any time.

Permissions (scopes)

Scope Description Required user role
reservations:read Read reservations ROLE_RESERVATIONS_RO
calendar:read Read calendars: iCal export, public holidays, custom calendars ROLE_RESERVATIONS_RO
statistics:read Read statistics ROLE_STATISTICS
invoices:read Read invoices ROLE_INVOICES

API description (OpenAPI)

The API ships with a machine-readable OpenAPI 3.1 description. It is not public — it is available to authenticated users in two ways:

  • In the browser: profile → API accessDownload API description. No token needed, your normal login is enough.
  • With a token: GET /api/v1/openapi.yaml. Any valid token works; no particular scope is required.
curl -H "Authorization: Bearer fwb_..." "https://your-installation/api/v1/openapi.yaml" -o openapi.yaml

The file can be imported into API clients such as Postman or Insomnia, opened at editor.swagger.io, or used to generate a client with openapi-generator. In the document, set the baseUrl server variable to your installation (including a subdirectory if the app is not served from the domain root).

Endpoints

Base URL: https://your-installation/api/v1

Query reservations

GET /api/v1/reservations

Requires the reservations:read scope.

Parameter Description Default
start Start of the date range (YYYY-MM-DD) today
end End of the date range (YYYY-MM-DD), at most 180 days after start = start
objectId Restrict to one property/object (ID) all
apartmentId Restrict to one room (ID) all
statusId Reservation status, repeatable (statusId=1,2 or statusId[]=1&statusId[]=2) all blocking statuses
type arrival, departure or inhouse — only arrivals, departures or in-house guests all

Example:

curl -H "Authorization: Bearer fwb_..." \
  "https://your-installation/api/v1/reservations?start=2026-08-01&end=2026-08-31&type=arrival"

Response:

{
  "data": [
    {
      "uuid": "d3bd1487-734f-4875-8bc8-48fde331ac80",
      "bookingGroupUuid": null,
      "startDate": "2026-08-04",
      "endDate": "2026-08-06",
      "arrivalTime": null,
      "departureTime": null,
      "persons": 2,
      "guestCounts": {"1": 2},
      "status": {"id": 1, "name": "Confirmed"},
      "apartment": {"id": 22, "number": "18", "description": "Double room"},
      "object": {"id": 3, "name": "Hotel"},
      "origin": "Website",
      "bookerName": "Doe John",
      "remark": null,
      "optionDate": null,
      "reservationDate": "2026-07-30T15:21:08+02:00",
      "types": ["arrival", "inhouse"],
      "isImported": false,
      "invoices": [
        {"id": 12, "number": "111", "date": "2026-08-06", "status": {"id": 2, "code": "PAID"}}
      ]
    }
  ],
  "meta": {"start": "2026-08-01", "end": "2026-08-31", "count": 1}
}

invoices lists the invoices linked to the reservation as metadata only (no amounts). It is only filled when the token also carries the invoices:read scope; otherwise the field is null, so a caller can tell "not permitted" apart from "no invoices" ([]). Amounts and line items are available through the invoice endpoints.

types describes the reservation relative to the requested date range: it contains arrival if the arrival day falls inside the range, departure if the departure day does, and inhouse if at least one day of the range lies strictly between arrival and departure. Over a multi-day range a reservation can therefore have several types at once (e.g. ["arrival", "inhouse"]). When querying a single day (start = end), exactly one type is returned — matching the front desk view. For a per-day breakdown, query day by day or derive it from startDate/endDate.

Invoices

Both invoice endpoints require the invoices:read scope and return the same object shape, including line items and the VAT breakdown.

Privacy note: Invoices contain the recipient's name and address. Payment credentials (IBAN, card number, card holder, mandate reference) are never returned by the API.

List invoices in a period:

GET /api/v1/invoices
Parameter Description Default
start First invoice date (YYYY-MM-DD) today
end Last invoice date (YYYY-MM-DD), range at most 366 days = start
status Invoice status IDs, repeatable (1 = open, 2 = paid, 3 = prepaid, 4 = canceled) all

Single invoice:

GET /api/v1/invoices/{id}
curl -H "Authorization: Bearer fwb_..." \
  "https://your-installation/api/v1/invoices?start=2026-08-01&end=2026-08-31&status=1,2"
{
  "data": [
    {
      "id": 63,
      "number": "151",
      "date": "2026-08-07",
      "status": {"id": 1, "code": "OPEN"},
      "paymentMeans": {"code": 48, "name": "CARD_PAYMENT"},
      "payment": null,
      "recipient": {
        "salutation": "Herr", "firstname": "Max", "lastname": "Mustermann", "company": null,
        "address": "Musterstr. 1", "zip": "12345", "city": "Musterhausen", "country": "DE",
        "email": "max@example.com", "phone": "987654321",
        "buyerReference": null, "buyerVatId": null
      },
      "remark": null,
      "totals": {"gross": 256.00, "vat": 30.96, "net": 225.04},
      "vatRates": [
        {"rate": 7.0, "gross": 90.00, "vat": 5.89, "net": 84.11},
        {"rate": 19.0, "gross": 157.00, "vat": 25.07, "net": 131.93}
      ],
      "apartments": [
        {"id": 41, "number": "1", "description": "Doppelzimmer", "startDate": "2026-08-03", "endDate": "2026-08-06",
         "beds": 2, "persons": 2, "amount": 3, "price": 90.00, "vat": 7.0,
         "includesVat": true, "isFlatPrice": false, "isPerRoom": true, "total": 270.00}
      ],
      "positions": [
        {"id": 135, "description": "Frühstück", "amount": 3, "price": 10.00, "vat": 19.0,
         "includesVat": true, "isFlatPrice": false, "positionGroup": "misc", "total": 30.00}
      ],
      "reservations": [{"id": 18, "uuid": "658cf1da-c324-4be3-83fd-7e6fcd41a138"}]
    }
  ],
  "meta": {"start": "2026-08-01", "end": "2026-08-31", "status": [1, 2], "count": 1}
}

Notes on the amounts: totals.gross is the invoice total, totals.vat the VAT contained in it, and totals.net = gross − vat. The same three values are broken down per VAT rate in vatRates. apartments holds the accommodation lines (a snapshot of room, period and price at the time of invoicing), positions the remaining items. reservations links back to the reservations the invoice was created for.

Occupancy calendar (iCal)

GET /api/v1/apartments/{id}/calendar.ics

Requires the calendar:read scope. Returns the occupancy calendar of a room in iCal format — even if the calendar is not shared publicly. The existing public calendar URL (with UUID) continues to work unchanged.

curl -u "username:fwb_..." "https://your-installation/api/v1/apartments/22/calendar.ics"

Public holidays

GET /api/v1/calendar/holidays

Requires the calendar:read scope. Returns the public holidays within a date range, computed for a country and optionally a subdivision (state).

Parameter Description Default
start Start date (YYYY-MM-DD) today
end End date (YYYY-MM-DD), range at most 731 days = start
country ISO 3166-1 country code (e.g. DE, AT, CH) DE
subdivision ISO 3166-2 subdivision code belonging to the country (e.g. DE-SN for Saxony) none — all of the country's holidays incl. all subdivisions
locale Language of the holiday names request language
curl -H "Authorization: Bearer fwb_..." \
  "https://your-installation/api/v1/calendar/holidays?start=2026-10-01&end=2026-10-31&country=DE&subdivision=DE-SN"
{"data": [{"date": "2026-10-03", "name": "Tag der Deutschen Einheit"}, {"date": "2026-10-31", "name": "Reformationstag"}], "meta": {"start": "2026-10-01", "end": "2026-10-31", "country": "DE", "subdivision": "DE-SN", "locale": "de", "count": 2}}

Custom calendars

Requires the calendar:read scope. Exposes the user-defined calendars (waste collection, vacations, events, …) and their entries.

List calendars:

GET /api/v1/calendars
{"data": [{"id": 1, "name": "Abfallkalender", "color": "#ffc107", "requiresConfirmation": true, "hasIcsSource": true, "lastSyncedAt": "2026-08-01T06:00:00+02:00"}], "meta": {"count": 1}}

Entries of one calendar:

GET /api/v1/calendars/{id}/entries
Parameter Description Default
start Start date (YYYY-MM-DD) today
end End date (YYYY-MM-DD), range at most 731 days = start
{"data": [{"id": 12, "date": "2026-05-10", "title": "Restmüll", "isManuallyCreated": false, "isConfirmed": true}], "meta": {"start": "2026-05-01", "end": "2026-05-31", "calendar": {"id": 1, "name": "Abfallkalender"}, "count": 1}}

Statistics

All statistics endpoints require the statistics:read scope. Unlike the chart endpoints used by the web UI, the responses are locale-independent (ISO month keys like 2026-08 instead of localized month names).

Bed utilization per month (percent):

GET /api/v1/statistics/utilization
Parameter Description Default
start First month (YYYY-MM) current month
end Last month (YYYY-MM), range at most 60 months = start
objectId Restrict to one property/object (ID) all
statusId Reservation status, repeatable all blocking statuses
{"data": [{"month": "2026-07", "utilization": 42.34}], "meta": {"start": "2026-07", "end": "2026-07", "objectId": null, "beds": 40, "count": 1}}

Reservations per booking origin/channel:

GET /api/v1/statistics/origins

Same parameters as utilization (start, end, objectId, statusId).

{"data": [{"id": 6, "name": "Website", "count": 33}], "meta": {"start": "2026-01", "end": "2026-12", "objectId": null, "count": 1}}

Turnover (gross, based on invoices):

GET /api/v1/statistics/turnover
Parameter Description Default
start First year (YYYY) current year
end Last year (YYYY), range at most 5 years = start
granularity year or month year
invoiceStatus Invoice status IDs, repeatable (1 = open, 2 = paid, 3 = prepaid, 4 = canceled) 1,2,3 (everything except canceled)
{"data": [{"year": 2026, "turnover": 10088.20}], "meta": {"start": 2026, "end": 2026, "granularity": "year", "invoiceStatus": [1, 2, 3], "count": 1}}

Note: The older session-based JSON endpoints under /statistics/* continue to work for the web UI, but they are considered internal: their format (chart-oriented, localized labels) may change without notice. New integrations should use the /api/v1/statistics/* endpoints described here.

Subscribing to the calendar in Thunderbird

  1. In your profile, create an API token with the Read occupancy calendar (iCal) permission.
  2. In Thunderbird: New calendar → On the network → iCalendar (ICS).
  3. URL: https://your-installation/api/v1/apartments/{id}/calendar.ics (the room ID can be found in the room settings).
  4. When prompted for credentials: enter your FewohBee username and the token as the password.

Error format and limits

Errors are returned as a consistent JSON structure:

{"error": {"code": 400, "message": "Invalid parameter 'start': expected format Y-m-d."}}
Code Meaning
400 Invalid parameters (e.g. wrong date format, date range > 180 days)
401 Missing or invalid token
403 Token lacks the required scope or the user lacks the role
429 Too many requests (rate limit: 300 requests / 5 minutes per token)

Clone this wiki locally