Hospitality
Hospitality: room types let by the night (their rooms kept as capacity, so a night is never let twice), seasonal rates, what a stay costs, and stays from reservation through check-in to check-out, which drafts the stay's sales invoice.
| Operation | Summary | Scope | Idempotency-Key |
|---|---|---|---|
GET /v1/hotel-stays |
List stays | read |
|
POST /v1/hotel-stays |
Reserve a stay | draft |
required |
GET /v1/hotel-stays/{stay_id} |
Get a stay | read |
|
POST /v1/hotel-stays/{stay_id}/cancel |
Cancel a stay | draft |
|
POST /v1/hotel-stays/{stay_id}/check-in |
Check guests in | draft |
|
POST /v1/hotel-stays/{stay_id}/check-out |
Check guests out and draft the invoice | draft |
|
POST /v1/hotel-stays/{stay_id}/invoice |
Draft a stay's invoice again | draft |
|
DELETE /v1/room-rates/{rate_id} |
Remove a season | draft |
|
GET /v1/room-types |
List room types | read |
|
POST /v1/room-types |
Add a room type | draft |
required |
GET /v1/room-types/{product_id} |
Get a room type | read |
|
PATCH /v1/room-types/{product_id} |
Change a room type | draft |
|
GET /v1/room-types/{product_id}/quote |
Price a stay | read |
|
POST /v1/room-types/{product_id}/rates |
Add a season | draft |
required |
GET /v1/hotel-stays
List stays
The stays by arrival and number, with their room type, customer, guests, nights, status and invoice.
| Required scope | read (admin implies every scope) |
| Operation id | list_hotel_stays_v1_hotel_stays_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| query | entity |
string or null | Only this entity's (code or id). | |
| query | room_type |
string or null | Only this room type's (product code, with entity, or id). |
|
| query | status |
"cancelled" | "checked_in" | "checked_out" | "reserved" or null |
Only reserved, checked_in, checked_out or cancelled. |
|
| query | first_day |
string (date) or null | Only those that leave on or after this day. | |
| query | last_day |
string (date) or null | Only those that arrive on or before this day. | |
| query | limit |
integer | How many, by arrival and number. (min 1; max 5000; default 500) |
Example request
curl "$ERP_URL/v1/hotel-stays" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | array of StayOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
[
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"number": "string",
"product_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"room_type": "string",
"room_type_name": "string",
"customer": "string",
"customer_name": "string",
"guest_name": "string",
"reference": "string",
"arrival": "2026-09-21",
"departure": "2026-09-21",
"rooms": 1,
"guests": 1,
"room": "string",
"currency": "string",
"nights": [
{
"night": "2026-09-21",
"price": "string",
"season": null
}
],
"total": "string",
"status": "cancelled",
"booking": "string",
"invoice": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"number": null,
"status": "string",
"total": "string",
"currency": "string"
},
"created_by": {},
"created_at": "2026-09-21T09:30:00Z",
"checked_in_by": {},
"checked_in_at": "2026-09-21T09:30:00Z",
"checked_out_by": {},
"checked_out_at": "2026-09-21T09:30:00Z",
"cancelled_by": {},
"cancelled_at": "2026-09-21T09:30:00Z"
}
]
POST /v1/hotel-stays
Reserve a stay
Reserve rooms of a type from arrival to departure for a customer: the rooms are booked
for each night (409 capacity_full, with the first night short, when too few are free) and
each night's price is agreed now, in the customer's currency (422 room_no_price where a
night has none). Nothing is posted. Needs an Idempotency-Key.
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | reserve_stay_v1_hotel_stays_post |
Request body
application/json (required): StayInput
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | Code (or id) of the entity that lets the rooms. |
room_type |
string | yes | Product code (or id) of the room type. |
customer |
string | yes | Code (or id) of the customer who pays: the invoice goes to it, in its currency and at its prices. |
arrival |
string (date) | yes | The day the guests arrive: the first night. |
departure |
string (date) | yes | The day they leave, after the last night (at most a year after arrival). |
rooms |
integer | Rooms of the type; default 1. (min 1; max 1000; default 1) |
|
guests |
integer or null | How many guests; default one a room. At most the rooms times what a room sleeps. (min 1; max 50000) | |
guest_name |
string or null | The guest's name, on the invoice line. (max length 200) | |
reference |
string or null | The customer's reference, e.g. its order number: the invoice's (BT-13). (max length 100) |
Example request
curl -X POST "$ERP_URL/v1/hotel-stays" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"arrival": "2026-11-15",
"customer": "C-CH",
"departure": "2026-11-17",
"entity": "CH01",
"guest_name": "Anna Muster",
"room_type": "DBL"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 201 | Created | StayOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 409 | The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"number": "string",
"product_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"room_type": "string",
"room_type_name": "string",
"customer": "string",
"customer_name": "string",
"guest_name": "string",
"reference": "string",
"arrival": "2026-09-21",
"departure": "2026-09-21",
"rooms": 1,
"guests": 1,
"room": "string",
"currency": "string",
"nights": [
{
"night": "2026-09-21",
"price": "string",
"season": "string"
}
],
"total": "string",
"status": "cancelled",
"booking": "string",
"invoice": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"number": "string",
"status": "string",
"total": "string",
"currency": "string"
},
"created_by": {},
"created_at": "2026-09-21T09:30:00Z",
"checked_in_by": {},
"checked_in_at": "2026-09-21T09:30:00Z",
"checked_out_by": {},
"checked_out_at": "2026-09-21T09:30:00Z",
"cancelled_by": {},
"cancelled_at": "2026-09-21T09:30:00Z"
}
GET /v1/hotel-stays/{stay_id}
Get a stay
The stay with its nights, status and invoice.
| Required scope | read (admin implies every scope) |
| Operation id | get_hotel_stay_v1_hotel_stays__stay_id__get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | stay_id |
string (uuid) | yes | Id of the stay. |
Example request
curl "$ERP_URL/v1/hotel-stays/{stay_id}" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | StayOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"number": "string",
"product_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"room_type": "string",
"room_type_name": "string",
"customer": "string",
"customer_name": "string",
"guest_name": "string",
"reference": "string",
"arrival": "2026-09-21",
"departure": "2026-09-21",
"rooms": 1,
"guests": 1,
"room": "string",
"currency": "string",
"nights": [
{
"night": "2026-09-21",
"price": "string",
"season": "string"
}
],
"total": "string",
"status": "cancelled",
"booking": "string",
"invoice": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"number": "string",
"status": "string",
"total": "string",
"currency": "string"
},
"created_by": {},
"created_at": "2026-09-21T09:30:00Z",
"checked_in_by": {},
"checked_in_at": "2026-09-21T09:30:00Z",
"checked_out_by": {},
"checked_out_at": "2026-09-21T09:30:00Z",
"cancelled_by": {},
"cancelled_at": "2026-09-21T09:30:00Z"
}
POST /v1/hotel-stays/{stay_id}/cancel
Cancel a stay
Cancel a reserved stay (a no-show too), which frees its rooms. 409 (stay_status)
unless it is reserved.
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | cancel_stay_v1_hotel_stays__stay_id__cancel_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | stay_id |
string (uuid) | yes | Id of the stay. |
Example request
curl -X POST "$ERP_URL/v1/hotel-stays/{stay_id}/cancel" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | StayOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 409 | The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"number": "string",
"product_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"room_type": "string",
"room_type_name": "string",
"customer": "string",
"customer_name": "string",
"guest_name": "string",
"reference": "string",
"arrival": "2026-09-21",
"departure": "2026-09-21",
"rooms": 1,
"guests": 1,
"room": "string",
"currency": "string",
"nights": [
{
"night": "2026-09-21",
"price": "string",
"season": "string"
}
],
"total": "string",
"status": "cancelled",
"booking": "string",
"invoice": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"number": "string",
"status": "string",
"total": "string",
"currency": "string"
},
"created_by": {},
"created_at": "2026-09-21T09:30:00Z",
"checked_in_by": {},
"checked_in_at": "2026-09-21T09:30:00Z",
"checked_out_by": {},
"checked_out_at": "2026-09-21T09:30:00Z",
"cancelled_by": {},
"cancelled_at": "2026-09-21T09:30:00Z"
}
POST /v1/hotel-stays/{stay_id}/check-in
Check guests in
Record the guests' arrival and the rooms given. 409 (stay_not_arrived) before the
arrival day, (stay_status) unless it is reserved.
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | check_in_stay_v1_hotel_stays__stay_id__check_in_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | stay_id |
string (uuid) | yes | Id of the stay. |
Request body
application/json (required): CheckInInput
| Field | Type | Required | Description |
|---|---|---|---|
room |
string or null | The room numbers given, e.g. 204, 205. (max length 100) |
Example request
curl -X POST "$ERP_URL/v1/hotel-stays/{stay_id}/check-in" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Content-Type: application/json" \
-d '{
"room": "204"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | StayOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 409 | The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"number": "string",
"product_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"room_type": "string",
"room_type_name": "string",
"customer": "string",
"customer_name": "string",
"guest_name": "string",
"reference": "string",
"arrival": "2026-09-21",
"departure": "2026-09-21",
"rooms": 1,
"guests": 1,
"room": "string",
"currency": "string",
"nights": [
{
"night": "2026-09-21",
"price": "string",
"season": "string"
}
],
"total": "string",
"status": "cancelled",
"booking": "string",
"invoice": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"number": "string",
"status": "string",
"total": "string",
"currency": "string"
},
"created_by": {},
"created_at": "2026-09-21T09:30:00Z",
"checked_in_by": {},
"checked_in_at": "2026-09-21T09:30:00Z",
"checked_out_by": {},
"checked_out_at": "2026-09-21T09:30:00Z",
"cancelled_by": {},
"cancelled_at": "2026-09-21T09:30:00Z"
}
POST /v1/hotel-stays/{stay_id}/check-out
Check guests out and draft the invoice
Record the day the guests left (default: the reserved departure, or today when they leave
early; a later day books the extra nights if free) and draft the stay's sales invoice: its
nights at the agreed prices and the extras. 409 (stay_status) unless the guests are in.
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | check_out_stay_v1_hotel_stays__stay_id__check_out_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | stay_id |
string (uuid) | yes | Id of the stay. |
Request body
application/json (required): CheckOutInput
| Field | Type | Required | Description |
|---|---|---|---|
departure |
string (date) or null | The day the guests left. Omitted: the reserved departure, or today when they leave early. A day after the reserved one books the extra nights, if free; never after today, and after the arrival. | |
extras |
array of ExtraInput |
What else goes on the invoice: breakfast, the minibar, parking. (max items 100) |
Example request
curl -X POST "$ERP_URL/v1/hotel-stays/{stay_id}/check-out" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Content-Type: application/json" \
-d '{}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | StayOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 409 | The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"number": "string",
"product_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"room_type": "string",
"room_type_name": "string",
"customer": "string",
"customer_name": "string",
"guest_name": "string",
"reference": "string",
"arrival": "2026-09-21",
"departure": "2026-09-21",
"rooms": 1,
"guests": 1,
"room": "string",
"currency": "string",
"nights": [
{
"night": "2026-09-21",
"price": "string",
"season": "string"
}
],
"total": "string",
"status": "cancelled",
"booking": "string",
"invoice": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"number": "string",
"status": "string",
"total": "string",
"currency": "string"
},
"created_by": {},
"created_at": "2026-09-21T09:30:00Z",
"checked_in_by": {},
"checked_in_at": "2026-09-21T09:30:00Z",
"checked_out_by": {},
"checked_out_at": "2026-09-21T09:30:00Z",
"cancelled_by": {},
"cancelled_at": "2026-09-21T09:30:00Z"
}
POST /v1/hotel-stays/{stay_id}/invoice
Draft a stay's invoice again
Draft the invoice of a checked-out stay whose invoice was rejected. 409 (stay_invoiced)
while it has one, (stay_status) unless the guests are out.
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | invoice_stay_v1_hotel_stays__stay_id__invoice_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | stay_id |
string (uuid) | yes | Id of the stay. |
Example request
curl -X POST "$ERP_URL/v1/hotel-stays/{stay_id}/invoice" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | StayOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 409 | The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"number": "string",
"product_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"room_type": "string",
"room_type_name": "string",
"customer": "string",
"customer_name": "string",
"guest_name": "string",
"reference": "string",
"arrival": "2026-09-21",
"departure": "2026-09-21",
"rooms": 1,
"guests": 1,
"room": "string",
"currency": "string",
"nights": [
{
"night": "2026-09-21",
"price": "string",
"season": "string"
}
],
"total": "string",
"status": "cancelled",
"booking": "string",
"invoice": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"number": "string",
"status": "string",
"total": "string",
"currency": "string"
},
"created_by": {},
"created_at": "2026-09-21T09:30:00Z",
"checked_in_by": {},
"checked_in_at": "2026-09-21T09:30:00Z",
"checked_out_by": {},
"checked_out_at": "2026-09-21T09:30:00Z",
"cancelled_by": {},
"cancelled_at": "2026-09-21T09:30:00Z"
}
DELETE /v1/room-rates/{rate_id}
Remove a season
Remove a season (audited with its prices); stays already reserved keep their prices.
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | remove_room_rate_v1_room_rates__rate_id__delete |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | rate_id |
string (uuid) | yes | Id of the season (GET /v1/room-types). |
Example request
curl -X DELETE "$ERP_URL/v1/room-rates/{rate_id}" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | RoomRateOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 409 | The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "string",
"currency": "string",
"first_night": "2026-09-21",
"last_night": "2026-09-21",
"nightly_price": "string",
"created_by": {},
"created_at": "2026-09-21T09:30:00Z"
}
GET /v1/room-types
List room types
The room types, by entity and code: rooms, guests a room sleeps, the list price of a night today, the rooms free tonight and the seasons.
| Required scope | read (admin implies every scope) |
| Operation id | list_room_types_v1_room_types_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| query | entity |
string or null | Only this entity's (code or id). |
Example request
curl "$ERP_URL/v1/room-types" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | array of RoomTypeOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
[
{
"product_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"code": "string",
"name": "string",
"active": true,
"rooms": 1,
"max_guests": 1,
"rate_type": "string",
"currency": "string",
"nightly_price": "string",
"free_tonight": 1,
"rates": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "string",
"currency": "string",
"first_night": "2026-09-21",
"last_night": "2026-09-21",
"nightly_price": "string",
"created_by": {},
"created_at": "2026-09-21T09:30:00Z"
}
],
"created_by": {},
"created_at": "2026-09-21T09:30:00Z"
}
]
POST /v1/room-types
Add a room type
Let rooms by the night: a service product with its nightly list price (at the VAT rate type
of accommodation where the VAT area has one), its rooms kept as capacity so no night is let
twice, and how many guests a room sleeps. Nothing is posted. Needs an Idempotency-Key.
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | create_room_type_v1_room_types_post |
Request body
application/json (required): RoomTypeInput
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | Code (or id) of the entity that lets the rooms. |
code |
string or null | Product code of the room type, e.g. DBL; omitted: the next product number (P-00001). (min length 1; max length 40) |
|
name |
string | yes | What it is called on invoices: Double room. (min length 1; max length 200) |
rooms |
integer | yes | How many rooms of the type there are: the rooms free each night. (min 1; max 1000) |
max_guests |
integer | How many guests one room sleeps (1 to 50). Default 2. (min 1; max 50; default 2) |
|
nightly_price |
decimal string | yes | Net price of one room for one night outside any season: the product's list price. A season (POST /v1/room-types/{product_id}/rates) or the customer's price list prices a night instead. (min 0) |
currency |
string or null | ISO 4217 code of the price; omitted: the entity's functional currency. (pattern ^[A-Z]{3}$) |
|
valid_from |
string (date) or null | The first night the price applies; omitted: today. | |
rate_type |
"exempt" | "parking" | "reduced" | "special" | "standard" | "super_reduced" | "zero" or null |
The VAT rate type of a night; omitted: the one the entity's VAT area has for accommodation on the day (Switzerland special 3.8 %, Germany reduced 7 %), else standard. |
Example request
curl -X POST "$ERP_URL/v1/room-types" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"entity": "CH01",
"name": "Double room",
"nightly_price": "210.00",
"rooms": 12
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 201 | Created | RoomTypeOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 409 | The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"product_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"code": "string",
"name": "string",
"active": true,
"rooms": 1,
"max_guests": 1,
"rate_type": "string",
"currency": "string",
"nightly_price": "string",
"free_tonight": 1,
"rates": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "string",
"currency": "string",
"first_night": "2026-09-21",
"last_night": "2026-09-21",
"nightly_price": "string",
"created_by": {},
"created_at": "2026-09-21T09:30:00Z"
}
],
"created_by": {},
"created_at": "2026-09-21T09:30:00Z"
}
GET /v1/room-types/{product_id}
Get a room type
The room type with its rooms, price, rooms free tonight and seasons.
| Required scope | read (admin implies every scope) |
| Operation id | get_room_type_v1_room_types__product_id__get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | product_id |
string (uuid) | yes | Id of the room type's product (GET /v1/room-types). |
Example request
curl "$ERP_URL/v1/room-types/{product_id}" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | RoomTypeOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"product_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"code": "string",
"name": "string",
"active": true,
"rooms": 1,
"max_guests": 1,
"rate_type": "string",
"currency": "string",
"nightly_price": "string",
"free_tonight": 1,
"rates": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "string",
"currency": "string",
"first_night": "2026-09-21",
"last_night": "2026-09-21",
"nightly_price": "string",
"created_by": {},
"created_at": "2026-09-21T09:30:00Z"
}
],
"created_by": {},
"created_at": "2026-09-21T09:30:00Z"
}
PATCH /v1/room-types/{product_id}
Change a room type
Set its rooms or the guests a room sleeps. 409 (capacity_booked) when fewer rooms than
are booked on a night from today on. Its name and price are the product's.
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | not used |
| Operation id | update_room_type_v1_room_types__product_id__patch |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | product_id |
string (uuid) | yes | Id of the room type's product (GET /v1/room-types). |
Request body
application/json (required): RoomTypeUpdate
| Field | Type | Required | Description |
|---|---|---|---|
rooms |
integer or null | How many rooms of the type there are: the rooms free each night. Refused below what is booked on a night from today on. (min 1; max 1000) | |
max_guests |
integer or null | How many guests one room sleeps (1 to 50). (min 1; max 50) |
Example request
curl -X PATCH "$ERP_URL/v1/room-types/{product_id}" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Content-Type: application/json" \
-d '{
"rooms": 14
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | RoomTypeOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 409 | The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"product_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"entity": "string",
"code": "string",
"name": "string",
"active": true,
"rooms": 1,
"max_guests": 1,
"rate_type": "string",
"currency": "string",
"nightly_price": "string",
"free_tonight": 1,
"rates": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "string",
"currency": "string",
"first_night": "2026-09-21",
"last_night": "2026-09-21",
"nightly_price": "string",
"created_by": {},
"created_at": "2026-09-21T09:30:00Z"
}
],
"created_by": {},
"created_at": "2026-09-21T09:30:00Z"
}
GET /v1/room-types/{product_id}/quote
Price a stay
Each night's price of one room and the rooms free, the total, and whether every night has the rooms free. Nothing is booked.
| Required scope | read (admin implies every scope) |
| Operation id | quote_stay_v1_room_types__product_id__quote_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | product_id |
string (uuid) | yes | Id of the room type's product (GET /v1/room-types). |
| query | arrival |
string (date) | yes | Arrival day. |
| query | departure |
string (date) | yes | Departure day, after the last night. |
| query | rooms |
integer | Rooms; default 1. (min 1; max 1000; default 1) |
|
| query | customer |
string or null | Code (or id) of the customer: its currency and prices. |
Example request
curl "$ERP_URL/v1/room-types/{product_id}/quote?arrival=value&departure=value" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | QuoteOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"room_type": "string",
"customer": "string",
"arrival": "2026-09-21",
"departure": "2026-09-21",
"rooms": 1,
"currency": "string",
"nights": [
{
"night": "2026-09-21",
"price": "string",
"season": "string",
"free": 1
}
],
"total": "string",
"available": true
}
POST /v1/room-types/{product_id}/rates
Add a season
A season's nightly price from its first to its last night, in one currency. 409
(room_rate_overlaps) when it overlaps a season of the room type in that currency. Stays
already reserved keep their prices. Needs an Idempotency-Key.
| Required scope | draft (admin implies every scope) |
| Idempotency-Key | required |
| Operation id | add_room_rate_v1_room_types__product_id__rates_post |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | product_id |
string (uuid) | yes | Id of the room type's product (GET /v1/room-types). |
Request body
application/json (required): RoomRateInput
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | The season: Winter season. (min length 1; max length 100) |
first_night |
string (date) | yes | The first night of the season. |
last_night |
string (date) | yes | The last night of the season, included (at most a year after the first). |
nightly_price |
decimal string | yes | Net price of one room for one night. (min 0) |
currency |
string or null | ISO 4217 code; omitted: the entity's functional currency. The seasons of a room type in one currency never overlap. (pattern ^[A-Z]{3}$) |
Example request
curl -X POST "$ERP_URL/v1/room-types/{product_id}/rates" \
-H "Authorization: Bearer $ERP_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"first_night": "2026-12-19",
"last_night": "2027-03-14",
"name": "Winter season",
"nightly_price": "260.00"
}'
Responses
| Status | Meaning | Body |
|---|---|---|
| 201 | Created | RoomRateOut |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 409 | The request conflicts with the data: an Idempotency-Key reused with a different request, a closed period, or an integrity rule of the database (conflict, integrity_violation). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "string",
"currency": "string",
"first_night": "2026-09-21",
"last_night": "2026-09-21",
"nightly_price": "string",
"created_by": {},
"created_at": "2026-09-21T09:30:00Z"
}
Schemas
CheckInInput
| Field | Type | Required | Description |
|---|---|---|---|
room |
string or null | The room numbers given, e.g. 204, 205. (max length 100) |
CheckOutInput
| Field | Type | Required | Description |
|---|---|---|---|
departure |
string (date) or null | The day the guests left. Omitted: the reserved departure, or today when they leave early. A day after the reserved one books the extra nights, if free; never after today, and after the arrival. | |
extras |
array of ExtraInput |
What else goes on the invoice: breakfast, the minibar, parking. (max items 100) |
ExtraInput
| Field | Type | Required | Description |
|---|---|---|---|
product |
string | yes | Code (or id) of an active product: breakfast, minibar. |
quantity |
decimal string | Quantity; default 1. (greater than 0; default "1") |
|
unit_price |
decimal string or null | Net unit price; omitted: the customer's or list price. (min 0) | |
description |
string or null | Text; omitted: its name. (min length 1; max length 500) |
NightPrice
| Field | Type | Required | Description |
|---|---|---|---|
night |
string (date) | yes | The night. |
price |
string | yes | Net price of one room, agreed when reserved. |
season |
string or null | yes | The season that priced it; null: the list price. |
QuoteNight
| Field | Type | Required | Description |
|---|---|---|---|
night |
string (date) | yes | The night (the evening it starts). |
price |
string or null | yes | Net price of one room; null: no price is known. |
season |
string or null | yes | The season that prices it; null: the list price. |
free |
integer | yes | Rooms of the type still free that night. |
QuoteOut
| Field | Type | Required | Description |
|---|---|---|---|
room_type |
string | yes | Product code of the room type. |
customer |
string or null | yes | Customer code whose prices are used. |
arrival |
string (date) | yes | Arrival day. |
departure |
string (date) | yes | Departure day. |
rooms |
integer | yes | Rooms asked for. |
currency |
string | yes | ISO 4217 code of the prices. |
nights |
array of QuoteNight |
yes | Each night from arrival to departure. |
total |
string or null | yes | Net total of the stay (each night's price times the rooms); null when a night has no price. |
available |
boolean | yes | Whether every night has the rooms free. |
RoomRateInput
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | The season: Winter season. (min length 1; max length 100) |
first_night |
string (date) | yes | The first night of the season. |
last_night |
string (date) | yes | The last night of the season, included (at most a year after the first). |
nightly_price |
decimal string | yes | Net price of one room for one night. (min 0) |
currency |
string or null | ISO 4217 code; omitted: the entity's functional currency. The seasons of a room type in one currency never overlap. (pattern ^[A-Z]{3}$) |
RoomRateOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | Id of the season. |
name |
string | yes | The season's name. |
currency |
string | yes | ISO 4217 code. |
first_night |
string (date) | yes | First night. |
last_night |
string (date) | yes | Last night, included. |
nightly_price |
string | yes | Net price of one room for one night. |
created_by |
object | yes | Who added it. |
created_at |
string (date-time) | yes | When (UTC). |
RoomTypeInput
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | Code (or id) of the entity that lets the rooms. |
code |
string or null | Product code of the room type, e.g. DBL; omitted: the next product number (P-00001). (min length 1; max length 40) |
|
name |
string | yes | What it is called on invoices: Double room. (min length 1; max length 200) |
rooms |
integer | yes | How many rooms of the type there are: the rooms free each night. (min 1; max 1000) |
max_guests |
integer | How many guests one room sleeps (1 to 50). Default 2. (min 1; max 50; default 2) |
|
nightly_price |
decimal string | yes | Net price of one room for one night outside any season: the product's list price. A season (POST /v1/room-types/{product_id}/rates) or the customer's price list prices a night instead. (min 0) |
currency |
string or null | ISO 4217 code of the price; omitted: the entity's functional currency. (pattern ^[A-Z]{3}$) |
|
valid_from |
string (date) or null | The first night the price applies; omitted: today. | |
rate_type |
"exempt" | "parking" | "reduced" | "special" | "standard" | "super_reduced" | "zero" or null |
The VAT rate type of a night; omitted: the one the entity's VAT area has for accommodation on the day (Switzerland special 3.8 %, Germany reduced 7 %), else standard. |
RoomTypeOut
| Field | Type | Required | Description |
|---|---|---|---|
product_id |
string (uuid) | yes | Id of the room type's product. |
entity |
string | yes | Entity code. |
code |
string | yes | Product code. |
name |
string | yes | Product name. |
active |
boolean | yes | Whether the rooms are still let. |
rooms |
integer | yes | How many rooms of the type there are: the rooms free each night. |
max_guests |
integer | yes | How many guests one room sleeps (1 to 50). |
rate_type |
string | yes | The VAT rate type of a night (the product's). |
currency |
string | yes | The entity's functional currency, of nightly_price. |
nightly_price |
string or null | yes | The list price of one night today, outside any season; null when the product has none in the currency. |
free_tonight |
integer | yes | Rooms not booked for tonight. |
rates |
array of RoomRateOut |
yes | Its seasons, by currency and first night. |
created_by |
object | yes | Who added the room type. |
created_at |
string (date-time) | yes | When (UTC). |
RoomTypeUpdate
| Field | Type | Required | Description |
|---|---|---|---|
rooms |
integer or null | How many rooms of the type there are: the rooms free each night. Refused below what is booked on a night from today on. (min 1; max 1000) | |
max_guests |
integer or null | How many guests one room sleeps (1 to 50). (min 1; max 50) |
StayInput
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | Code (or id) of the entity that lets the rooms. |
room_type |
string | yes | Product code (or id) of the room type. |
customer |
string | yes | Code (or id) of the customer who pays: the invoice goes to it, in its currency and at its prices. |
arrival |
string (date) | yes | The day the guests arrive: the first night. |
departure |
string (date) | yes | The day they leave, after the last night (at most a year after arrival). |
rooms |
integer | Rooms of the type; default 1. (min 1; max 1000; default 1) |
|
guests |
integer or null | How many guests; default one a room. At most the rooms times what a room sleeps. (min 1; max 50000) | |
guest_name |
string or null | The guest's name, on the invoice line. (max length 200) | |
reference |
string or null | The customer's reference, e.g. its order number: the invoice's (BT-13). (max length 100) |
StayInvoice
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | Id of the sales invoice (GET /v1/documents/{id}). |
number |
string or null | yes | Its number; null while it is a draft. |
status |
string | yes | draft, pending_approval, posted or rejected. |
total |
string | yes | Its total in currency. |
currency |
string | yes | ISO 4217 code. |
StayOut
| Field | Type | Required | Description |
|---|---|---|---|
id |
string (uuid) | yes | Id of the stay. |
entity |
string | yes | Entity code. |
number |
string | yes | Stay number, gap-free per entity and year (ST-...). |
product_id |
string (uuid) | yes | Id of the room type's product. |
room_type |
string | yes | Product code of the room type. |
room_type_name |
string | yes | Its name. |
customer |
string | yes | Customer code. |
customer_name |
string | yes | Customer name. |
guest_name |
string or null | yes | The guest's name. |
reference |
string or null | yes | The customer's reference. |
arrival |
string (date) | yes | Arrival day. |
departure |
string (date) | yes | Departure day (the day they left, once out). |
rooms |
integer | yes | Rooms. |
guests |
integer | yes | Guests. |
room |
string or null | yes | The rooms given at check-in. |
currency |
string | yes | ISO 4217 code of the prices. |
nights |
array of NightPrice |
yes | Each night with its price. |
total |
string | yes | Net total of the nights (times the rooms). |
status |
"cancelled" | "checked_in" | "checked_out" | "reserved" |
yes | reserved, checked_in, checked_out or cancelled. |
booking |
string | yes | Number of the booking of its rooms (BK-...). |
invoice |
StayInvoice or null |
yes | The invoice of the stay, once out. |
created_by |
object | yes | Who reserved it. |
created_at |
string (date-time) | yes | When (UTC). |
checked_in_by |
object or null | yes | Who checked the guests in. |
checked_in_at |
string (date-time) or null | yes | When (UTC). |
checked_out_by |
object or null | yes | Who checked them out. |
checked_out_at |
string (date-time) or null | yes | When (UTC). |
cancelled_by |
object or null | yes | Who cancelled it. |
cancelled_at |
string (date-time) or null | yes | When (UTC). |