-
Notifications
You must be signed in to change notification settings - Fork 17
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.
- Authentication
- Creating an API token
- Permissions (scopes)
- API description (OpenAPI)
- Endpoints
- Subscribing to the calendar in Thunderbird
- Error format and limits
The API uses personal access tokens. A token always starts with fwb_ and can be used in two ways:
-
Bearer header (recommended for scripts and integrations):
Authorization: Bearer fwb_... - 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.
- Sign in to FewohBee and open your profile.
- In the API access section, click Create API token.
- Choose a name, an expiry and the permissions.
- 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.
| 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 |
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 access → Download 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.yamlThe 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).
Base URL: https://your-installation/api/v1
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.
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.
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"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}}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}}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.
- In your profile, create an API token with the Read occupancy calendar (iCal) permission.
- In Thunderbird: New calendar → On the network → iCalendar (ICS).
- URL:
https://your-installation/api/v1/apartments/{id}/calendar.ics(the room ID can be found in the room settings). - When prompted for credentials: enter your FewohBee username and the token as the password.
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) |
Installation
Configuration
Usage
Integration & development
User manual