3RPMS General API Integration Knowledge Base
Welcome to the 3RPMS GraphQL API documentation. This API allows you to interact with the 3RPMS Property Management System.
3RPMS GraphQL API is based on GraphQL. Visit https://graphql.org/ to learn more about the protocol.
API Endpoints
Production server:
https://www.3rpms.de/graphql
Sandbox server:
https://demo.3rpms.de/graphql
Overview
Prerequisites
Please, read the 3RPMS® GENERAL API INTEGRATION GUIDE first!
GraphQL basics and tips
Although there is a lot more about GraphQL, we hereby list the very basics you should bear in mind when starting the integration.
All HTTP calls to the server must use POST method.
Access
Endpoint
Production server endpoint: https://www.3rpms.de/graphql (please make sure you use www.)
Sandbox server endpoint: https://demo.3rpms.de/graphql
Authentication
All requests require an API key in the header Authorization: Bearer <myapikey> where
API key
Create an API key in 3RPMS® under Settings -> Integrations access or ask the administrator of the hotel to do it if you do not have access.
Each hotel can have multiple API keys, each of which can have the following restrictions:
- API Key has
read-onlyaccess - API Key has
writeaccess - API Key has been disabled (when you want to restrict access temporary)
Contact support if you are not sure if an integration should have write or read-only access!
Changelog
Show changelog
This changelog lists integration-relevant changes, in chronological order.
2026-01-26
Improvements
- Add
Reservation.paymentTermsandQuery.paymentTermsfields.
query Reservations {
reservations {
edges {
node {
code
paymentTerms {
name
}
}
}
}
}
2026-01-06
Improvements
- Add
Reservation.billingClientandReservation.billingContactfields. When present, these client details will be used for invoices instead of reservation owner. - Add
UpdateReservationInput.clientId,UpdateReservationInput.contactId,UpdateReservationInput.billingClientIdandUpdateReservationInput.billingContactIdfields toupdateReservationmutation - Add simplified room availability API to query for category availability for each date. This API is similar to how rates and restrictions are already queried.
query Availability($dateRange:DateRangeInput!) {
settings {
categories {
edges {
node {
id
name
availability(filter:{date:$dateRange}) {
date
availability
}
}
}
}
}
}
2025-12-01
Improvements
- Add
updateReservationmutation- Only
Reservation.groupNamecan be updated
- Only
2025-09-15
Improvements
- Add
Client.newsletterSubscriptionDoubleOptInAtfield. A non-null value represents that the email has been verified and is known to be valid.
2025-07-24
Documentation
2025-07-23
Improvements
- Add
updateCategoryRestrictionsmutation
2025-06-11
Improvements
- Add
RoomSetup.countTowardsPerformancefield.- Some rooms may be used for purposes other than lodgings, for example, conference rooms or car parking spaces. In such cases, when this field is
false, the room should not be considered towards Key Performance Metrics.
- Some rooms may be used for purposes other than lodgings, for example, conference rooms or car parking spaces. In such cases, when this field is
2025-05-16
Improvements
- Added a new
RoomAccessQRRoom Access Key type. See Room Access Key API Guide for more details.
2025-05-06
Improvements
- Add
descriptionfield toCategorytype
{
settings {
categories {
edges {
node {
# By default, uses language set in `Accept-Language` header (by default, german)
description
# Or, can override to select a specific language. argument must be a 2-letter country code
enDescription: description(language: "en")
}
}
}
}
}
2025-02-04
Improvements
- Added
cancelledAtfield toReservationtype.
2025-02-03
Improvements
- Added daily rates breakdown fields to
RoomRatetype:packPriceGrosspackPriceNetlodgingGrosslodgingNet
Deprecations
- Deprecated
RoomRate.amountfield in favour ofRoomRate.packPriceGross
2025-01-20
Improvements
- Added
bookingChannelCodetoReservationFilter - Added logical
orclause to all filters. See documentation
2024-10-14
Improvements
- Added
Person.stayPreferencesandPerson.mealPreferencesas a replacement forPerson.preferences - Added
Company.stayPreferencesas a replacement forCompany.preferences
Deprecations
- Deprecated
Person.preferences,Company.preferences, andClient.preferences.
2024-09-11
Improvements
- Mutation
updateRoomSetupadded.- At the moment, the only field that can be updated is
cleaningStatus
- At the moment, the only field that can be updated is
2024-09-05
Improvements
- Add pagination arguments for
RoomStay.dailyRatesconnection
2024-07-17
Improvements
- Webhook
room_stay.updatednow includes aupdated_fieldsfield listing whichRoomStayfields have changed.
2024-03-25
Improvements
- Webhook
reservation.updatednow includes aupdated_fieldsfield listing whichReservationfields have changed.
2023-09-26
Improvements
- Webhook
category.availability.updatedadded. Sent when availability for a category is changed.
2023-05-16
Deprecations
- Deprecate
RoomSetup.bedLinenChangefield
2023-03-23
Breaking Changes
- graphql query errors no longer include
extensions.category=graphql
| Before | After |
|---|---|
|
|
There is no change for other category errors. There is no change for queries that dont result in an error.
2023-02-13
Improvements
- Adds External Sales API:
- Type
ExternalSalesProductadded - Mutation
createExternalSalesProductadded - Mutation
updateExternalSalesProductadded - Mutation
createExternalSaleadded - Query
externalSalesProductsadded
- Type
Documentation
- Added External Sales API Guide
2023-02-06
Improvements
- Added
check_inandcheck_outtoRoomStayFilter. Allows to, for example, retrieve all checked in rooms that have not yet checked out.
2023-02-01
Breaking Changes
- Union
RoomAccessKeyreplaced with an interface of the same name. No queries are expected to break, but tools inspecting the schema might be affected.
Improvements
- Query
roomAccessKeysadded to retrieve all room access keys your integration is allowed to grant/revoke to rooms - Mutation
createRoomAccessKeyadded to create a room access key - room pin or key storage (pin,compartment) pair - Mutation
addRoomAccessKeyadded to assign a room access key to a room - Mutation
removeRoomAccessKeyadded to remove room access key from a room - Mutation
deleteRoomAccessKeyadded to delete a room access key - Type
RoomAccessKeyStorageadded for reading (pin, compartment) pairs. A sibling type toRoomAccessPin - Added
configurationerror category, for errors that must not be visible to guests, but should be shown for hotel staff.
Documentation
- Added Room Access Key API Guide
- Updated graphql api errors documentation
2023-01-23
Improvements
- Field
Reservation.selfcheckinStatusadded- Returns an enum with values
DISABLED,ENABLED,AVAILABLE,COMPLETED
- Returns an enum with values
Deprecations
- Field
Reservation.selfcheckinEnableddeprecated in favour ofReservation.selfcheckinStatus
2022-12-19
Improvements
- Field
RoomStay.roomAccessKeyadded to allow retrieving room pin code for hotels with salto integration set up.
2022-11-14
Improvements
- Field
Reservation.totalAmountadded - Field
Reservation.openAmountadded
2022-10-27
Breaking Changes
Clienttype was split intoPersonandCompanytypes. Most existing queries are expected to continue working, this will affect your integration if__typenameis used.
Improvements
- Query
clientTitlesadded - a list of all active client titles - Query
communicationLanguagesadded - a list of available communication languages, ordered by hotel's preference. - Mutation
addRoomStayGuestadded - allows adding an existing client as a guest to a room - Mutation
removeRoomStayGuestadded - allows removing a client as a guest to a room - Mutation
createClientadded - allows creating newPersonclients. - Mutation
updateClientadded - allows updating any existing client - New fields to
Persontype added -clientTitle,street,zipcode,city,country,fax,preferences,nationality,passportNumber,idCardNumber,issuingAuthority - New fields to
Companytype added -street,zipcode,city,country,fax,preferences - Add a
@chaindirective to allow using a result from one operation as input for the next operation, within one request.
Deprecations
- Deprecate
Person.titlefield in favour ofPerson.clientTitle - Deprecate
Person.companyfield - value is always empty - Deprecate
Company.firstnamefield - value is always empty - Deprecate
Company.lastnamefield - value is always empty - Deprecate
Company.titlefield - value is always empty - Deprecate
Company.carPlateNumberfield - value is always empty - Deprecate
Company.birthdayfield - value is alwaysnull - Deprecate
Company.birthdayGreetingsEnabledfield - value is alwaysfalse - Deprecate
Client.title,Client.firstname,Client.lastname,Client.carPlateNumber,Client.birthday,Client.birthdayGreetingsEnabled- use the implementationPersontype - Deprecate
Client.company- use the implementationCompanytype.
< 2022-10-27
Changes before 2022-10-27 are not tracked.
GraphQL API
Why GraphQL?
Advantages of GraphQL
Here are several advantages of GraphQL protocol relevant to API clients integrating with a GraphQL endpoint.
1. Speed
GraphQL is faster than other communication APIs because it facilitates you to cut down your request query by choosing only the specific fields you want to query.
2. No over-fetching and under-fetching problems
The main advantage of GraphQl over REST is that REST responses contain too much data or sometimes not enough data, which creates the need for another request. GraphQL solves this problem by fetching only the exact and specific data in a single request.
3. Hierarchical Structure
GraphQL follows a hierarchical structure where relationships between objects are defined in a graphical structure. Here, every object type represents a component, and every related field from an object type to another object type represents a component wrapping another component.
4. Defines a data shape
When we request GraphQL queries to the server, the server returns the response in a simple, secure, and predictable shape. So, it facilitates you to write a specific query according to your requirement. This makes GraphQL really easy to learn and use.
Examples
Let's say, you want to get room stays with arrival, departure dates, and a list of arriving guest names
GraphQL
With GraphQL there would be just one request and it would look like this:
query ArrivingPrimaryGuests($date: Date!) {
room_stays(
filter: { reservation_from: { gt: $date } }
) {
edges {
node {
id
arrival: reservation_from
departure: reservation_to
first_guest {
firstname
lastname
}
}
}
}
}
In response you would get a structured resultset with the information you requested only:
{
"data": {
"room_stays": {
"edges": [
{
"node": {
"id": 1,
"arrival": "2022-02-21",
"departure": "2022-02-28",
"first_guest": {
"firstname": "Matt",
"lastname": "Luis"
}
}
},
{
"node": {
"id": 2,
"arrival": "2022-02-22",
"departure": "2022-02-26",
"guests": {
"firstname": "Linda",
"lastname": "Webber"
}
}
}
]
}
}
}
Rate Limiting
Rate Limits
There's a limit of 200 requests per minute per IP. Exceeding this limit will block further connections for a period of 20 minutes.
Optimisation
Reducing queries
When querying for multiple records by id at the same time, often the queries can be combined:
| Bad Example | Good Example |
|---|---|
|
|
Batching queries
See Query Batches
Filters
Fields with a filter argument allow, as the name implies, filtering the result-set items.
Operator reference
Some fields may support only a subset of operators.
| Operator | Description | Example |
|---|---|---|
lt |
Larger than | reservation_from: {lt: "2022-02-18"} |
le |
Larger or equal | reservation_from: {le: "2022-02-18"} |
eq |
Equal | reservation_from: {eq: "2022-02-18"} |
in |
Equals to any of the values | reservation_from: {in: ["2022-02-18", "2022-02-20"]} |
ge |
Greater or equal | reservation_from: {ge: "2022-02-18"} |
gt |
Greater than | reservation_from: {gt: "2022-02-18"} |
Logical Operators
In addition to comparisons, the following logical operators are also supported:
| Operator | Description | Example |
|---|---|---|
| not | Negates nested criteria | not: {reservation_from: {eq: "2022-02-18"}} |
| or | At least one of the nested criteria must match | or: [{reservation_from: {eq: "2022-02-18"}}}, {reservation_from: {eq: "2022-02-25"}}}] |
Examples
In these example we'll be using root room_stays field:
room_stays(filter: RoomStayFilter): RoomStayConnection!
Filter one field
{
room_stays(
filter: {
id: { in: ["nQZA3P1U1", "J0lZ3H4Ml"] }
}
) {
...
}
}
Here we look up specific RoomStay items by id
Filter multiple fields
When multiple fields are used, they all must match.
{
room_stays(
filter: {
reservation_from: { lt: "2022-02-28" }
reservation_to: { ge: "2022-02-18" }
}
) {
...
}
}
Here we look up RoomStay items that are staying between Feb 18th and Feb 28th.
Filter using multiple operators
Filters are not limited to using just one operator - any subset of the allowed operators can be used at the same time.
{
room_stays(
filter: {
reservation_from: {
le: "2022-02-27"
ge: "2022-02-21"
}
}
) {
...
}
}
Here we look up RoomStay items that arrived between Feb 21st and Feb 27th.
Negation
Negation is done by using the reserved not field.
{
room_stays(
filter: {
reservation_from: { gt: "2022-02-18" }
not: {
reservationStatus: { eq: CANCELLED }
}
}
) {
...
}
}
Here we find all RoomStay with arrival after Feb 18th that have not been cancelled.
Disjunction
Logical Disjunction can be done by using the reserved or field:
{
room_stays(
filter: {
or: [
{reservation_from: { eq: "2022-02-04" }},
{reservation_from: { eq: "2022-02-11" }},
{reservation_from: { eq: "2022-02-18" }},
{reservation_from: { eq: "2022-02-25" }},
]
}
) {
...
}
}
Here we find all RoomStay items that arrive on any friday in February 2022
Pagination
3RPMS implements relay-style connections for pagination. For detailed information see the GraphQL Cursor Connections Specification .
GraphQL allows you to fetch only the fields you need, with no unnecessary overhead. This helps keep network responses small and fast.
However, GraphQL doesn't automatically guarantee small responses. This is especially apparent when a field contains a list. To avoid enormous response from a seemingly small query, lists, containing unbounded maximum amount of items, use pagination:
query RoomStays($cursor: String = null) {
reservations(first:10 after:$cursor) {
edges {
node {
# reservation fields
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
Here, when no $cursor is provided, only the first 10 reservations are returned. To retrieve the next 10 reservations, the same query can be sent with $cursor set to value from endCursor field; and so on for the next 10 reservations. When there are no more results, hasNextPage will be set to false.
While the example uses reservations, it works the same for all connections.
The response to the previous example would look like this:
{
"data": {
"reservations": {
"edges": [
{"node": {...}},
{"node": {...}},
... 8 more results
],
"pageInfo": {
"hasNextPage": true,
"endCursor": "bWFkZSB5b3UgbG9vaw=="
}
}
}
}
Load only as many records you need. If you're using only the first result, then specify first:1; or any other value depending on how you use the result set.
Cursors are opaque strings, and must be passed back to the server as-is. Cursors should be stored only long enough to query the next page, and should not be stored long-term. Cursors may be different for each query depending on provided arguments or other scenarios.
Defaults
When no cursor arguments are provided, by default first:20 will be used, that is, only the first 20 items will be returned.
Unless otherwise specified on the field, maximum number of items a connection can return is 100.
Backward pagination
In addition to previously described forward pagination, 3RPMS also supports backwards pagination using last and before arguments, and startCursor and hasPreviousPage fields.
query RoomStays($cursor: String = null) {
reservations(last:10 before:$cursor) {
edges {
node {
# reservation fields
}
}
pageInfo {
hasPreviousPage
startCursor
}
}
}
Here the last 10 reservations are returned. Similarly to before, to retrieve the previous 10 reservations, same query can be sent with $cursor set to value from $startCursor. When there are no previous reservations, hasPreviousPage will be false
Errors
GraphQL requests cannot be fully executed in some cases - malformed query, insufficient access, or validation errors. For schema and query errors, refer to GraphQL's specification https://spec.graphql.org/June2018/#sec-Errors. This section will cover only errors not part of the graphql specification.
All mutations succeed, or none succeed. When sending multiple mutations in one request, and any one of them results in an error, all other mutations should be considered failed as well. In such cases, retrieved data is only informative and should be discarded.
Error Structure
In case of an error, a top-level error field will be returned by 3RPMS and will contain one or more errors. Each error will always have, at minimum, a message field.
In addition to graphql errors, 3RPMS will return the following errors:
- Configuration Errors - Errors that require hotel staff, or 3RPMS support intervention to resolve and will fail with any provided input. For example, some functionality has not been enabled for the hotel.
- Validation Errors - Errors that user is expected to be able to recover from by inputting different data. For example, value outside of expected range or some record not found.
- Auth Errors - Api token is not provided or is incorrect
Configuration
Configuration errors will have the extension.category field set to "configuration".
Configuration errors must not be shown to hotel guests, but may be shown to hotel staff and can be logged by your integration. These errors can be resolved only by hotel staff in 3RPMS system, or by contacting 3RPMS support. For example, some functionality has not been enabled for the hotel.
{
"errors": [
{
"message": "This hotel has not beet set up to support Room Access Key api",
"extensions": {
"category": "configuration"
},
}
]
}
Validation
Validation errors always will have the extensions.category field set to "validation".
Validation errors are meant to be resolvable by end-users, for example, by modifying the form in your application, and attempting again. These errors are safe to show to hotel guests, and usually can be recovered from.
Validation messages can change, and additional validations may be added at any time. These messages must not be used in comparisons.
Argument errors
For errors related to input arguments, extensions.argumentPath field will contain a path to the field that caused an error (similar to path in GraphQL schema errors).
Request
query AvailableRooms {
inventory(filter: {
period: {
start:"2022-09-08"
end:"2022-09-08"
}
categories:["a", "b", "c"]
}) {
# ...
}
}
In this example, filter has invalid data - a requested category ("c") does not exist.
Response
{
"errors": [
{
"message": "Sorry, Property Category not found",
"extensions": {
"argumentPath": [
"filter",
"categories",
2
],
"category": "validation"
},
"path": [
"inventory"
],
}
]
}
extensions.argumentPath can be followed to find the field that caused an error.
Non-argument errors
For validation errors that cannot be attributed to any specific input input argument, extensions.argumentPath will not be added
Request
In this example, provided data is correct, but room cannot be updated for other reasons.
mutation CheckIn {
updateRoomStay(input:{id:"119157" check_in:"2022-09-08T16:00:00+00:00"}) {
roomStay {
check_in
check_out
}
}
}
Response
{
"errors": [
{
"message": "This reservation was cancelled",
"extensions": {
"category": "validation"
},
"path": [
"updateRoomStay"
]
}
]
}
Validation message language
By default, validation error messages are in German. To see errors in English, add a Accept-Language header to each graphql request:
Accept-Language: en
Besides german de and english en no other language is supported at the moment.
Auth
"category": "auth" errors will be returned in the following cases:
- Missing api token (
Authorization: Bearer api_...header) - Api token incorrect
- Token might have been disabled, or rotated
- Running a mutation with read-only permissions.
This error must not be shown to end-guests, but might be relevant for hotel staff.
Response
{
"errors": [
{
"message": "Operation \"query\" not allowed",
"extensions": {
"category": "auth"
}
}
]
}
GraphQL Error
When a malformed query, non-existent field or other graphql-specific error is returned, there will be no extensions.category field:
{
“errors”: [
{
“message”: “Syntax Error”
}
]
}
GraphQL errors must not be shown to end-guests or hotel staff. These errors may require a change in your integration.
Extra features
Query Fragments
You can encapsulate common data structures in Fragments and reuse them.
Without Fragments
In the following example, you will immediately notice that the data structure of guests and first_guest are identical due to the fact they both return objects of type Person. Scroll to the With Fragments section to see how this can be optimized.
query RoomStays {
room_stays(filter: { date: "2022-10-27" }, first: 2) {
edges {
node {
id
room_setup {
name
}
guests {
clientTitle {
name
}
firstname
lastname
language
email
telephone
mobile
carPlateNumber
}
first_guest {
clientTitle {
name
}
firstname
lastname
language
email
telephone
mobile
carPlateNumber
}
}
}
}
}
With Fragments
The previously redundant guests and first_guest data structure blocks now have been encapsulated in a reusable Fragment called Guest, so instead of repeating the complete list of attributes, it is enough to reuse the fragment by specifying it as ...Guest
query RoomStays {
room_stays(filter: { date: "2022-10-27" }, first: 2) {
edges {
node {
id
room_setup {
name
}
guests {
...Guest
}
first_guest {
...Guest
}
}
}
}
}
fragment Guest on Person {
clientTitle {
name
}
firstname
lastname
language
email
telephone
mobile
carPlateNumber
}
Query variables
A query without variables
In the following example, you will notice that a static value "2022-02-18" is provided for the date attribute:
query RoomStays {
room_stays(
filter: {
date: "2022-02-18"
}
) {
edges {
cursor
node {
id
reservation {
code
}
room_setup {
expectedCleaningStatus(date: "2022-02-18")
}
}
}
}
}
A query with variables
This example demonstrates how the previously static value can be turned into a $date variable:
query RoomStays($date: Date!) {
room_stays(
filter: {
date: $date
}
) {
edges {
cursor
node {
id
reservation {
code
}
room_setup {
expectedCleaningStatus(date: $date)
}
}
}
}
}
Now you can pass the value via $date variable and reuse it in the query if necessary.
Multiple queries within one request
Using aliases
You can perform multiple queries within one request using GrapQL aliases. In the following example, we will call performanceStatistics query twice but with different input arguments which very much makes sense if you want to get data for today and tomorrow, for example
Variables
{
"today": "2022-03-01",
"tomorrow": "2022-03-02"
}
Query
query PerformanceStatistics($today: Date!, $tomorrow: Date!) {
today: performanceStatistics(filter: { date: $today }) {
...PerformanceStatistics
}
tomorrow: performanceStatistics(filter: { date: $tomorrow }) {
...PerformanceStatistics
}
}
fragment PerformanceStatistics on PerformanceStatistics {
occupancy
adr
revPAR
}
Using batches
You can execute multiple GraphQL queries within one GraphQL request, like this:
Query
[
{
"query":"query PerformanceStatistics($date: Date!) { performanceStatistics(filter: { date: $date }) { occupancy } }",
"variables": {
"date": "2022-02-28"
}
},
{
"query":"query PerformanceStatistics($date: Date!) { performanceStatistics(filter: { date: $date }) { occupancy } }",
"variables": {
"date": "2022-02-27"
}
}
]
Chained Operations
The @chain(variable: [const]String!) directive enables passing data from one operation to the next, when sending batch operations. In the subsequent operation, the value is provided as a variable with the name specified in the directive.
This helps in cases, when a mutation depends on data returned by a previous mutation - using a @chain directive both mutations can be sent in one request instead of two
Example
An example, to create a guest and immediately reference it in the next mutation to add it to a room:
POST https://www.3rpms.de/graphql
Content-Type: application/json
Authorization: <your api token>
[
{
"query": "mutation CreateGuest($createGuest: CreatePersonInput!) { createClient(input: {person: $createGuest}) { client { id @chain(variable:\"clientId\") } } }",
"variables": {
"createGuest": {
"lastname": "Přemyslid",
"country": "de",
"language": "de"
}
}
},
{
"query": "mutation AddGuestToRoom($roomStayId:ID! $clientId:ID!) { addRoomStayGuest(input:{roomStayId:$roomStayId clientId:$clientId}) { roomStay { guests { id } } } }",
"variables": {
"roomStayId": "<room id>"
}
}
]
Notice how the 2nd mutation AddGuestToRoom uses a variable $clientId that it does not provide - the variable comes from the previous mutation. However, if there were more operations, $clientId variable would not be available in the 3rd (or subsequent) operations - @chain directive provides variables only for the one subsequent operation.
According to GraphQL specification, queries and mutation selections can be executed in any order. To avoid determinism issues, there's the following restrictions on using @chain:
@chaincannot be used on a field that is a descendant of a list[Type].- Same variable name cannot be declared more than once.
In addition, to reduce accidental variable overwriting, there's also the following restrictions:
@chainvariable is not allowed to override a variable already sent in the request
Queries
room_stays
Description
Get a list of reserved rooms. By default, rooms from cancelled reservation are not returned.
If a previously requested room's id is no longer returned, assume it has been moved to a different day, deleted or reservation cancelled altogether.
If present, returns rooms after $after cursor and at most $first entries.
Similarly, returns rooms before $before cursor, and at most $last entries.
Response
Returns a RoomStayConnection!
Arguments
| Name | Description |
|---|---|
filter - RoomStayFilter
|
|
first - Int
|
|
after - String
|
|
last - Int
|
|
before - String
|
Example
Query
query Room_stays(
$filter: RoomStayFilter,
$first: Int,
$after: String,
$last: Int,
$before: String
) {
room_stays(
filter: $filter,
first: $first,
after: $after,
last: $last,
before: $before
) {
edges {
...RoomStayEdgeFragment
}
pageInfo {
...PageInfoFragment
}
totalCount
}
}
Variables
{
"filter": RoomStayFilter,
"first": 987,
"after": "xyz789",
"last": 123,
"before": "xyz789"
}
Response
{
"data": {
"room_stays": {
"edges": [RoomStayEdge],
"pageInfo": PageInfo,
"totalCount": 987
}
}
}
reservations
Description
A list of all reservations.
If present, returns reservation after $after cursor and at most $first entries.
Similarly, returns reservation before $before cursor, and at most $last entries.
Response
Returns a ReservationConnection!
Arguments
| Name | Description |
|---|---|
filter - ReservationFilter
|
|
first - Int
|
|
after - String
|
|
last - Int
|
|
before - String
|
Example
Query
query Reservations(
$filter: ReservationFilter,
$first: Int,
$after: String,
$last: Int,
$before: String
) {
reservations(
filter: $filter,
first: $first,
after: $after,
last: $last,
before: $before
) {
edges {
...ReservationEdgeFragment
}
pageInfo {
...PageInfoFragment
}
totalCount
}
}
Variables
{
"filter": ReservationFilter,
"first": 987,
"after": "xyz789",
"last": 123,
"before": "abc123"
}
Response
{
"data": {
"reservations": {
"edges": [ReservationEdge],
"pageInfo": PageInfo,
"totalCount": 987
}
}
}
clients
Description
A list of all clients.
If present, returns clients after $after cursor and at most $first entries.
Similarly, returns clients before $before cursor, and at most $last entries.
Response
Returns a ClientConnection!
Arguments
| Name | Description |
|---|---|
filter - ClientFilter
|
|
first - Int
|
|
after - String
|
|
last - Int
|
|
before - String
|
Example
Query
query Clients(
$filter: ClientFilter,
$first: Int,
$after: String,
$last: Int,
$before: String
) {
clients(
filter: $filter,
first: $first,
after: $after,
last: $last,
before: $before
) {
edges {
...ClientEdgeFragment
}
pageInfo {
...PageInfoFragment
}
totalCount
}
}
Variables
{
"filter": ClientFilter,
"first": 123,
"after": "xyz789",
"last": 123,
"before": "abc123"
}
Response
{
"data": {
"clients": {
"edges": [ClientEdge],
"pageInfo": PageInfo,
"totalCount": 987
}
}
}
clientTitles
Description
A list of all client titles.
If present, returns client titles after $after cursor and at most $first entries.
Similarly, returns client titles before $before cursor, and at most $last entries.
Response
Returns a ClientTitleConnection!
Example
Query
query ClientTitles(
$first: Int,
$after: String,
$last: Int,
$before: String
) {
clientTitles(
first: $first,
after: $after,
last: $last,
before: $before
) {
edges {
...ClientTitleEdgeFragment
}
pageInfo {
...PageInfoFragment
}
totalCount
}
}
Variables
{
"first": 987,
"after": "xyz789",
"last": 987,
"before": "abc123"
}
Response
{
"data": {
"clientTitles": {
"edges": [ClientTitleEdge],
"pageInfo": PageInfo,
"totalCount": 987
}
}
}
communicationLanguages
Description
A list of supported client communication languages, ordered by preference. Emails, invoices, etc., content created by 3RPMS can be only in these languages.
If present, returns languages after $after cursor and at most $first entries.
Similarly, returns languages before $before cursor, and at most $last entries.
Response
Returns a LanguageConnection!
Example
Query
query CommunicationLanguages(
$first: Int,
$after: String,
$last: Int,
$before: String
) {
communicationLanguages(
first: $first,
after: $after,
last: $last,
before: $before
) {
edges {
...LanguageEdgeFragment
}
pageInfo {
...PageInfoFragment
}
totalCount
}
}
Variables
{
"first": 123,
"after": "xyz789",
"last": 987,
"before": "abc123"
}
Response
{
"data": {
"communicationLanguages": {
"edges": [LanguageEdge],
"pageInfo": PageInfo,
"totalCount": 123
}
}
}
webhookEndpoints
Description
A list of all webhook endpoints for requesting integration.
See https://www.3rpms.de/graphql/docs#webhooks-overview for documentation
If present, returns webhook endpoints after $after cursor and at most $first entries.
Similarly, returns webhook endpoints before $before cursor, and at most $last entries.
Response
Returns a WebhookEndpointConnection!
Arguments
| Name | Description |
|---|---|
filter - WebhookEndpointFilter
|
|
first - Int
|
|
after - String
|
|
last - Int
|
|
before - String
|
Example
Query
query WebhookEndpoints(
$filter: WebhookEndpointFilter,
$first: Int,
$after: String,
$last: Int,
$before: String
) {
webhookEndpoints(
filter: $filter,
first: $first,
after: $after,
last: $last,
before: $before
) {
edges {
...WebhookEndpointEdgeFragment
}
pageInfo {
...PageInfoFragment
}
totalCount
}
}
Variables
{
"filter": WebhookEndpointFilter,
"first": 123,
"after": "abc123",
"last": 987,
"before": "abc123"
}
Response
{
"data": {
"webhookEndpoints": {
"edges": [WebhookEndpointEdge],
"pageInfo": PageInfo,
"totalCount": 123
}
}
}
paymentMethods
Description
Payment methods that are available for requesting integration to create deposits with.
If present, returns payment methods after $after cursor and at most $first entries.
Similarly, returns payment methods before $before cursor, and at most $last entries.
Response
Returns a PaymentMethodConnection!
Arguments
| Name | Description |
|---|---|
filter - PaymentMethodFilter
|
|
first - Int
|
|
after - String
|
|
last - Int
|
|
before - String
|
Example
Query
query PaymentMethods(
$filter: PaymentMethodFilter,
$first: Int,
$after: String,
$last: Int,
$before: String
) {
paymentMethods(
filter: $filter,
first: $first,
after: $after,
last: $last,
before: $before
) {
edges {
...PaymentMethodEdgeFragment
}
pageInfo {
...PageInfoFragment
}
totalCount
}
}
Variables
{
"filter": PaymentMethodFilter,
"first": 123,
"after": "abc123",
"last": 123,
"before": "xyz789"
}
Response
{
"data": {
"paymentMethods": {
"edges": [PaymentMethodEdge],
"pageInfo": PageInfo,
"totalCount": 123
}
}
}
roomAccessKeys
Description
Room access keys your integration has control over - for deleting, assigning or revoking.
If present, returns room access keys after $after cursor and at most $first entries.
Similarly, returns room access keys before $before cursor, and at most $last entries.
Response
Returns a RoomAccessKeyConnection!
Arguments
| Name | Description |
|---|---|
filter - RoomAccessKeyFilter
|
|
first - Int
|
|
after - String
|
|
last - Int
|
|
before - String
|
Example
Query
query RoomAccessKeys(
$filter: RoomAccessKeyFilter,
$first: Int,
$after: String,
$last: Int,
$before: String
) {
roomAccessKeys(
filter: $filter,
first: $first,
after: $after,
last: $last,
before: $before
) {
edges {
...RoomAccessKeyEdgeFragment
}
pageInfo {
...PageInfoFragment
}
totalCount
}
}
Variables
{
"filter": RoomAccessKeyFilter,
"first": 987,
"after": "xyz789",
"last": 123,
"before": "abc123"
}
Response
{
"data": {
"roomAccessKeys": {
"edges": [RoomAccessKeyEdge],
"pageInfo": PageInfo,
"totalCount": 987
}
}
}
externalSalesProducts
Description
Products your integration can use to add external sales
If present, returns products after $after cursor and at most $first entries.
Similarly, returns products before $before cursor, and at most $last entries.
Response
Returns an ExternalSalesProductConnection!
Example
Query
query ExternalSalesProducts(
$first: Int,
$after: String,
$last: Int,
$before: String
) {
externalSalesProducts(
first: $first,
after: $after,
last: $last,
before: $before
) {
edges {
...ExternalSalesProductEdgeFragment
}
pageInfo {
...PageInfoFragment
}
totalCount
}
}
Variables
{
"first": 987,
"after": "xyz789",
"last": 123,
"before": "abc123"
}
Response
{
"data": {
"externalSalesProducts": {
"edges": [ExternalSalesProductEdge],
"pageInfo": PageInfo,
"totalCount": 123
}
}
}
paymentTerms
Description
A list of all payment terms.
If present, returns payment terms after $after cursor and at most $first entries.
Similarly, returns payment terms before $before cursor, and at most $last entries.
Response
Returns a PaymentTermsConnection!
Example
Query
query PaymentTerms(
$first: Int,
$after: String,
$last: Int,
$before: String
) {
paymentTerms(
first: $first,
after: $after,
last: $last,
before: $before
) {
edges {
...PaymentTermsEdgeFragment
}
pageInfo {
...PageInfoFragment
}
totalCount
}
}
Variables
{
"first": 987,
"after": "xyz789",
"last": 987,
"before": "xyz789"
}
Response
{
"data": {
"paymentTerms": {
"edges": [PaymentTermsEdge],
"pageInfo": PageInfo,
"totalCount": 123
}
}
}
settings
Response
Returns a Settings!
Example
Query
query Settings {
settings {
categories {
...CategoryConnectionFragment
}
roomSetups {
...RoomSetupConnectionFragment
}
}
}
Response
{
"data": {
"settings": {
"categories": CategoryConnection,
"roomSetups": RoomSetupConnection
}
}
}
performanceStatistics
Response
Returns a PerformanceStatistics!
Arguments
| Name | Description |
|---|---|
filter - StatisticsFilter!
|
Example
Query
query PerformanceStatistics($filter: StatisticsFilter!) {
performanceStatistics(filter: $filter) {
occupancy
adr
revPAR
}
}
Variables
{"filter": StatisticsFilter}
Response
{
"data": {
"performanceStatistics": {
"occupancy": 123.45,
"adr": Decimal,
"revPAR": Decimal
}
}
}
inventory
Response
Returns an Inventory!
Arguments
| Name | Description |
|---|---|
filter - InventoryFilter!
|
Example
Query
query Inventory($filter: InventoryFilter!) {
inventory(filter: $filter) {
available {
...InventoryRoomConnectionFragment
}
occupied {
...InventoryRoomConnectionFragment
}
booked {
...InventoryRoomConnectionFragment
}
}
}
Variables
{"filter": InventoryFilter}
Response
{
"data": {
"inventory": {
"available": InventoryRoomConnection,
"occupied": InventoryRoomConnection,
"booked": InventoryRoomConnection
}
}
}
Mutations
updateCategoryPrices
Description
Update default rates for categories. Changes are pushed out to booking portals immediately, if they're connected.
Avoid sending multiple updates for the same (date,category,occupancy) pair in one request. We provide no guarantee on the order in which they're processed.
Response
Returns an UpdateCategoryPricesPayload!
Arguments
| Name | Description |
|---|---|
input - UpdateCategoryPricesInput!
|
Example
Query
mutation UpdateCategoryPrices($input: UpdateCategoryPricesInput!) {
updateCategoryPrices(input: $input) {
updatedCategoryPrices {
...CategoryPriceConnectionFragment
}
}
}
Variables
{"input": UpdateCategoryPricesInput}
Response
{
"data": {
"updateCategoryPrices": {
"updatedCategoryPrices": CategoryPriceConnection
}
}
}
updateCategoryRestrictions
Description
Update restrictions for categories. Changes are pushed out to booking portals immediately, if they're connected.
Avoid sending multiple updates for the same (date,category) pair in one request. We provide no guarantee on the order in which they're processed.
Response
Returns an UpdateCategoryRestrictionsPayload!
Arguments
| Name | Description |
|---|---|
input - UpdateCategoryRestrictionsInput!
|
Example
Query
mutation UpdateCategoryRestrictions($input: UpdateCategoryRestrictionsInput!) {
updateCategoryRestrictions(input: $input) {
updatedCategoryRestrictions {
...CategoryRestrictionConnectionFragment
}
}
}
Variables
{"input": UpdateCategoryRestrictionsInput}
Response
{
"data": {
"updateCategoryRestrictions": {
"updatedCategoryRestrictions": CategoryRestrictionConnection
}
}
}
updateReservation
Response
Returns an UpdateReservationPayload!
Arguments
| Name | Description |
|---|---|
input - UpdateReservationInput!
|
Example
Query
mutation UpdateReservation($input: UpdateReservationInput!) {
updateReservation(input: $input) {
reservation {
...ReservationFragment
}
}
}
Variables
{"input": UpdateReservationInput}
Response
{
"data": {
"updateReservation": {"reservation": Reservation}
}
}
updateRoomStay
Response
Returns an UpdateRoomStayPayload!
Arguments
| Name | Description |
|---|---|
input - UpdateRoomStayInput!
|
Example
Query
mutation UpdateRoomStay($input: UpdateRoomStayInput!) {
updateRoomStay(input: $input) {
roomStay {
...RoomStayFragment
}
errors {
...ValidationErrorFragment
}
}
}
Variables
{"input": UpdateRoomStayInput}
Response
{
"data": {
"updateRoomStay": {
"roomStay": RoomStay,
"errors": [ValidationError]
}
}
}
updateRoomSetup
Response
Returns an UpdateRoomSetupPayload!
Arguments
| Name | Description |
|---|---|
input - UpdateRoomSetupInput!
|
Example
Query
mutation UpdateRoomSetup($input: UpdateRoomSetupInput!) {
updateRoomSetup(input: $input) {
roomSetup {
...RoomSetupFragment
}
}
}
Variables
{"input": UpdateRoomSetupInput}
Response
{"data": {"updateRoomSetup": {"roomSetup": RoomSetup}}}
createWebhookEndpoint
Response
Returns a CreateWebhookEndpointPayload!
Arguments
| Name | Description |
|---|---|
input - CreateWebhookEndpointInput!
|
Example
Query
mutation CreateWebhookEndpoint($input: CreateWebhookEndpointInput!) {
createWebhookEndpoint(input: $input) {
webhookEndpoint {
...WebhookEndpointFragment
}
secret
errors {
...ValidationErrorFragment
}
}
}
Variables
{"input": CreateWebhookEndpointInput}
Response
{
"data": {
"createWebhookEndpoint": {
"webhookEndpoint": WebhookEndpoint,
"secret": "xyz789",
"errors": [ValidationError]
}
}
}
updateWebhookEndpoint
Response
Returns an UpdateWebhookEndpointPayload!
Arguments
| Name | Description |
|---|---|
input - UpdateWebhookEndpointInput!
|
Example
Query
mutation UpdateWebhookEndpoint($input: UpdateWebhookEndpointInput!) {
updateWebhookEndpoint(input: $input) {
webhookEndpoint {
...WebhookEndpointFragment
}
errors {
...ValidationErrorFragment
}
}
}
Variables
{"input": UpdateWebhookEndpointInput}
Response
{
"data": {
"updateWebhookEndpoint": {
"webhookEndpoint": WebhookEndpoint,
"errors": [ValidationError]
}
}
}
deleteWebhookEndpoint
Response
Returns a DeleteWebhookEndpointPayload!
Arguments
| Name | Description |
|---|---|
input - DeleteWebhookEndpointInput!
|
Example
Query
mutation DeleteWebhookEndpoint($input: DeleteWebhookEndpointInput!) {
deleteWebhookEndpoint(input: $input) {
deleted
errors {
...ValidationErrorFragment
}
}
}
Variables
{"input": DeleteWebhookEndpointInput}
Response
{
"data": {
"deleteWebhookEndpoint": {
"deleted": true,
"errors": [ValidationError]
}
}
}
createPaymentMethod
Description
At the moment, there is a limit of only one payment method per integration per hotel
Response
Returns a CreatePaymentMethodPayload!
Arguments
| Name | Description |
|---|---|
input - CreatePaymentMethodInput!
|
Example
Query
mutation CreatePaymentMethod($input: CreatePaymentMethodInput!) {
createPaymentMethod(input: $input) {
paymentMethod {
...PaymentMethodFragment
}
}
}
Variables
{"input": CreatePaymentMethodInput}
Response
{
"data": {
"createPaymentMethod": {
"paymentMethod": PaymentMethod
}
}
}
updatePaymentMethod
Response
Returns an UpdatePaymentMethodPayload!
Arguments
| Name | Description |
|---|---|
input - UpdatePaymentMethodInput!
|
Example
Query
mutation UpdatePaymentMethod($input: UpdatePaymentMethodInput!) {
updatePaymentMethod(input: $input) {
paymentMethod {
...PaymentMethodFragment
}
}
}
Variables
{"input": UpdatePaymentMethodInput}
Response
{
"data": {
"updatePaymentMethod": {
"paymentMethod": PaymentMethod
}
}
}
deletePaymentMethod
Response
Returns a DeletePaymentMethodPayload!
Arguments
| Name | Description |
|---|---|
input - DeletePaymentMethodInput!
|
Example
Query
mutation DeletePaymentMethod($input: DeletePaymentMethodInput!) {
deletePaymentMethod(input: $input) {
deleted
}
}
Variables
{"input": DeletePaymentMethodInput}
Response
{"data": {"deletePaymentMethod": {"deleted": false}}}
addRoomStayGuest
Description
Adds a guest to a room stay. Guest will be added at the end of existing guest list
Response
Returns an AddRoomStayGuestPayload!
Arguments
| Name | Description |
|---|---|
input - AddRoomStayGuestInput!
|
Example
Query
mutation AddRoomStayGuest($input: AddRoomStayGuestInput!) {
addRoomStayGuest(input: $input) {
roomStay {
...RoomStayFragment
}
}
}
Variables
{"input": AddRoomStayGuestInput}
Response
{"data": {"addRoomStayGuest": {"roomStay": RoomStay}}}
removeRoomStayGuest
Response
Returns a RemoveRoomStayGuestPayload!
Arguments
| Name | Description |
|---|---|
input - RemoveRoomStayGuestInput!
|
Example
Query
mutation RemoveRoomStayGuest($input: RemoveRoomStayGuestInput!) {
removeRoomStayGuest(input: $input) {
roomStay {
...RoomStayFragment
}
}
}
Variables
{"input": RemoveRoomStayGuestInput}
Response
{"data": {"removeRoomStayGuest": {"roomStay": RoomStay}}}
createClient
Response
Returns a CreateClientPayload!
Arguments
| Name | Description |
|---|---|
input - CreateClientInput!
|
Example
Query
mutation CreateClient($input: CreateClientInput!) {
createClient(input: $input) {
client {
...ClientFragment
}
}
}
Variables
{"input": CreateClientInput}
Response
{"data": {"createClient": {"client": Client}}}
updateClient
Response
Returns an UpdateClientPayload!
Arguments
| Name | Description |
|---|---|
input - UpdateClientInput!
|
Example
Query
mutation UpdateClient($input: UpdateClientInput!) {
updateClient(input: $input) {
client {
...ClientFragment
}
}
}
Variables
{"input": UpdateClientInput}
Response
{"data": {"updateClient": {"client": Client}}}
createDeposit
Response
Returns a CreateDepositPayload!
Arguments
| Name | Description |
|---|---|
input - CreateDepositInput!
|
Example
Query
mutation CreateDeposit($input: CreateDepositInput!) {
createDeposit(input: $input) {
incomingPayment {
...IncomingPaymentFragment
}
}
}
Variables
{"input": CreateDepositInput}
Response
{
"data": {
"createDeposit": {"incomingPayment": IncomingPayment}
}
}
createRoomAccessKey
Response
Returns a CreateRoomAccessKeyPayload!
Arguments
| Name | Description |
|---|---|
input - CreateRoomAccessKeyInput!
|
Example
Query
mutation CreateRoomAccessKey($input: CreateRoomAccessKeyInput!) {
createRoomAccessKey(input: $input) {
roomAccessKey {
...RoomAccessKeyFragment
}
}
}
Variables
{"input": CreateRoomAccessKeyInput}
Response
{
"data": {
"createRoomAccessKey": {
"roomAccessKey": RoomAccessKey
}
}
}
deleteRoomAccessKey
Response
Returns a DeleteRoomAccessKeyPayload!
Arguments
| Name | Description |
|---|---|
input - DeleteRoomAccessKeyInput!
|
Example
Query
mutation DeleteRoomAccessKey($input: DeleteRoomAccessKeyInput!) {
deleteRoomAccessKey(input: $input) {
deleted
}
}
Variables
{"input": DeleteRoomAccessKeyInput}
Response
{"data": {"deleteRoomAccessKey": {"deleted": false}}}
addRoomAccessKey
Response
Returns an AddRoomAccessKeyPayload!
Arguments
| Name | Description |
|---|---|
input - AddRoomAccessKeyInput!
|
Example
Query
mutation AddRoomAccessKey($input: AddRoomAccessKeyInput!) {
addRoomAccessKey(input: $input) {
roomStay {
...RoomStayFragment
}
roomAccessKey {
...RoomAccessKeyFragment
}
}
}
Variables
{"input": AddRoomAccessKeyInput}
Response
{
"data": {
"addRoomAccessKey": {
"roomStay": RoomStay,
"roomAccessKey": RoomAccessKey
}
}
}
removeRoomAccessKey
Response
Returns a RemoveRoomAccessKeyPayload!
Arguments
| Name | Description |
|---|---|
input - RemoveRoomAccessKeyInput!
|
Example
Query
mutation RemoveRoomAccessKey($input: RemoveRoomAccessKeyInput!) {
removeRoomAccessKey(input: $input) {
roomStay {
...RoomStayFragment
}
roomAccessKey {
...RoomAccessKeyFragment
}
}
}
Variables
{"input": RemoveRoomAccessKeyInput}
Response
{
"data": {
"removeRoomAccessKey": {
"roomStay": RoomStay,
"roomAccessKey": RoomAccessKey
}
}
}
createExternalSalesProduct
Description
Each integration can create only one external sales product
Response
Returns a CreateExternalSalesProductPayload!
Arguments
| Name | Description |
|---|---|
input - CreateExternalSalesProductInput!
|
Example
Query
mutation CreateExternalSalesProduct($input: CreateExternalSalesProductInput!) {
createExternalSalesProduct(input: $input) {
product {
...ExternalSalesProductFragment
}
}
}
Variables
{"input": CreateExternalSalesProductInput}
Response
{
"data": {
"createExternalSalesProduct": {
"product": ExternalSalesProduct
}
}
}
updateExternalSalesProduct
Response
Returns an UpdateExternalSalesProductPayload!
Arguments
| Name | Description |
|---|---|
input - UpdateExternalSalesProductInput!
|
Example
Query
mutation UpdateExternalSalesProduct($input: UpdateExternalSalesProductInput!) {
updateExternalSalesProduct(input: $input) {
product {
...ExternalSalesProductFragment
}
}
}
Variables
{"input": UpdateExternalSalesProductInput}
Response
{
"data": {
"updateExternalSalesProduct": {
"product": ExternalSalesProduct
}
}
}
createExternalSale
Response
Returns a CreateExternalSalePayload!
Arguments
| Name | Description |
|---|---|
input - CreateExternalSaleInput!
|
Example
Query
mutation CreateExternalSale($input: CreateExternalSaleInput!) {
createExternalSale(input: $input) {
created
}
}
Variables
{"input": CreateExternalSaleInput}
Response
{"data": {"createExternalSale": {"created": true}}}
Objects
UpdateCategoryPricesPayload
Fields
| Field Name | Description |
|---|---|
updatedCategoryPrices - CategoryPriceConnection!
|
A list of updated rates. If present, returns rates after Similarly, returns rates before |
Example
{"updatedCategoryPrices": CategoryPriceConnection}
UpdateCategoryRestrictionsPayload
Fields
| Field Name | Description |
|---|---|
updatedCategoryRestrictions - CategoryRestrictionConnection!
|
A list of updated restrictions. If present, returns rates after Similarly, returns rates before |
Example
{
"updatedCategoryRestrictions": CategoryRestrictionConnection
}
UpdateRoomStayPayload
Fields
| Field Name | Description |
|---|---|
roomStay - RoomStay
|
Example
{"roomStay": RoomStay}
UpdateRoomSetupPayload
Fields
| Field Name | Description |
|---|---|
roomSetup - RoomSetup
|
Example
{"roomSetup": RoomSetup}
PageInfo
Fields
| Field Name | Description |
|---|---|
startCursor - String
|
Corresponds to the first node's cursors in edges For empty lists value will be |
endCursor - String
|
Corresponds to the last node's cursors in edges For empty lists value will be |
hasNextPage - Boolean!
|
If first/after is used, then then server returns true if further edges exist, otherwise false |
hasPreviousPage - Boolean!
|
If last/before is used, then then server returns true if prior edges exist, otherwise false |
Example
{
"startCursor": "xyz789",
"endCursor": "abc123",
"hasNextPage": false,
"hasPreviousPage": true
}
RoomStayConnection
Description
Paginated reserved rooms.
Default page size is 20, if not provided. Maximum page size is 100.
Pagination implements Relay Connection spec. For detailed information follow https://relay.dev/graphql/connections.htm
Fields
| Field Name | Description |
|---|---|
edges - [RoomStayEdge!]!
|
List of paginated items |
pageInfo - PageInfo!
|
|
totalCount - Int!
|
Total number of items matching $filter |
Example
{
"edges": [RoomStayEdge],
"pageInfo": PageInfo,
"totalCount": 123
}
RoomStayEdge
RoomStay
Fields
| Field Name | Description |
|---|---|
id - ID!
|
Uniquely identifies the room stay. Doesnt mean anything to guests |
reservation - Reservation!
|
Reservation details |
room_setup - RoomSetup
|
Physical room details |
roomName - String
|
Physical room's name or number. This can be different from room_setup.name if the room has been renamed. Value is null when no physical room has been assigned. |
category - Category
|
Category from which rate and other ammenities are taken from. This is not the physical category the guest is staying in, not to be confused with room_setup.category |
guests - [Person!]!
|
List of all guests staying in the room |
first_guest - Person
|
First guest is the person that room is booked on, the other guests are co-travellers. For most integrations we recommend using first_guest |
gross - Decimal!
|
Pack price |
dailyRates - RoomRateConnection!
|
Contains daily rates for current room stay, with dates in interval [reservation_from,reservation_to). Not guaranteed to be ordered by date. If present, returns daily rates after Similarly, returns daily rates before |
lodgingsGross - Decimal!
|
Gross without included products |
additionalSales - Decimal!
|
Additional products or services charged to this room. Not included in the price |
reservation_from - Date!
|
Arrival date |
reservation_to - Date!
|
Departure date |
check_in - Datetime
|
Exact date and time of when guests arrived |
check_out - Datetime
|
Exact date and time when guests departed - can assume they no longer have access to the room if field is present not null |
selfcheckout_enabled - Boolean
|
If room has selfcheckout enabled. If true, you may direct the guest to selfcheckout_url |
selfcheckout_url - String
|
Absolute url to where the guest can pay for the room. May be present even if selfcheckout_enabled is false |
mealNotes - String!
|
Notes for allergies, food preferences and such |
maidNotes - String!
|
Notes for arrival that should be follow when preparing the room, for example, allergic free pillows |
guestMessage - String!
|
A message that should be delivered to arriving guests in a particular room of a reservation. |
ageGroups - AgeGroups
|
|
createdAt - Datetime!
|
|
updatedAt - Datetime!
|
|
roomAccessKey - RoomAccessKey
|
|
Example
{
"id": "4",
"reservation": Reservation,
"room_setup": RoomSetup,
"roomName": "xyz789",
"category": Category,
"guests": [Person],
"first_guest": Person,
"gross": Decimal,
"dailyRates": RoomRateConnection,
"lodgingsGross": Decimal,
"additionalSales": Decimal,
"reservation_from": "2007-12-03",
"reservation_to": "2007-12-03",
"check_in": Datetime,
"check_out": Datetime,
"selfcheckout_enabled": true,
"selfcheckout_url": "abc123",
"mealNotes": "xyz789",
"maidNotes": "xyz789",
"guestMessage": "abc123",
"ageGroups": AgeGroups,
"createdAt": Datetime,
"updatedAt": Datetime,
"roomAccessKey": RoomAccessKey
}
RoomAccessKeyConnection
Description
Paginated room access keys.
Default page size is 20, if not provided. Maximum page size is 100.
Pagination implements Relay Connection spec. For detailed information follow https://relay.dev/graphql/connections.htm
Fields
| Field Name | Description |
|---|---|
edges - [RoomAccessKeyEdge!]!
|
List of paginated items |
pageInfo - PageInfo!
|
|
totalCount - Int!
|
Total number of items matching $filter |
Example
{
"edges": [RoomAccessKeyEdge],
"pageInfo": PageInfo,
"totalCount": 123
}
RoomAccessKeyEdge
Fields
| Field Name | Description |
|---|---|
cursor - String!
|
Unique id of node. Use it for pagination to get surrounding entries |
node - RoomAccessKey!
|
Pagination item |
Example
{
"cursor": "abc123",
"node": RoomAccessKey
}
RoomAccessPin
Description
Intended use is for when the room has a pin code instead of a physical key
Fields
| Field Name | Description |
|---|---|
id - ID!
|
|
pin - String!
|
PIN intended for guests. Up to 10 characters. |
roomStays - RoomStayConnection!
|
|
Arguments |
|
Example
{
"id": 4,
"pin": "abc123",
"roomStays": RoomStayConnection
}
RoomAccessKeyStorage
Description
Intended use is for when a physical key has to be retrieved from pin-protected storage outside of the room
Fields
| Field Name | Description |
|---|---|
id - ID!
|
|
pin - String!
|
PIN intended for guests. Up to 10 characters. |
compartment - String!
|
Relevant only for hotel staff to know where to place the key. Must not be shown to guests. |
roomStays - RoomStayConnection!
|
|
Arguments |
|
Example
{
"id": 4,
"pin": "xyz789",
"compartment": "xyz789",
"roomStays": RoomStayConnection
}
RoomAccessQR
Fields
| Field Name | Description |
|---|---|
id - ID!
|
|
qrData - String!
|
The data being used to generate QR code |
roomStays - RoomStayConnection!
|
|
Arguments |
|
Example
{
"id": "4",
"qrData": "xyz789",
"roomStays": RoomStayConnection
}
CreateRoomAccessKeyPayload
Fields
| Field Name | Description |
|---|---|
roomAccessKey - RoomAccessKey
|
Example
{"roomAccessKey": RoomAccessKey}
DeleteRoomAccessKeyPayload
Fields
| Field Name | Description |
|---|---|
deleted - Boolean!
|
Always true |
Example
{"deleted": true}
AddRoomAccessKeyPayload
Fields
| Field Name | Description |
|---|---|
roomStay - RoomStay
|
|
roomAccessKey - RoomAccessKey
|
Example
{
"roomStay": RoomStay,
"roomAccessKey": RoomAccessKey
}
RemoveRoomAccessKeyPayload
Fields
| Field Name | Description |
|---|---|
roomStay - RoomStay
|
|
roomAccessKey - RoomAccessKey
|
Example
{
"roomStay": RoomStay,
"roomAccessKey": RoomAccessKey
}
AgeGroups
RoomRateConnection
Description
Paginated room rates.
Default page size is 20, if not provided. Maximum page size is 100.
Pagination implements Relay Connection spec. For detailed information follow https://relay.dev/graphql/connections.htm
Fields
| Field Name | Description |
|---|---|
edges - [RoomRateEdge!]!
|
List of paginated items |
pageInfo - PageInfo!
|
|
totalCount - Int!
|
Total number of items matching $filter |
Example
{
"edges": [RoomRateEdge],
"pageInfo": PageInfo,
"totalCount": 123
}
RoomRateEdge
RoomRate
Example
{
"date": "2007-12-03",
"packPriceGross": Decimal,
"packPriceNet": Decimal,
"lodgingGross": Decimal,
"lodgingNet": Decimal
}
ClientConnection
Description
Paginated clients.
Default page size is 20, if not provided. Maximum page size is 100.
Pagination implements Relay Connection spec. For detailed information follow https://relay.dev/graphql/connections.htm
Fields
| Field Name | Description |
|---|---|
edges - [ClientEdge!]!
|
List of paginated items |
pageInfo - PageInfo!
|
|
totalCount - Int!
|
Total number of items matching $filter |
Example
{
"edges": [ClientEdge],
"pageInfo": PageInfo,
"totalCount": 987
}
ClientEdge
Person
Fields
| Field Name | Description |
|---|---|
id - ID!
|
|
clientTitle - ClientTitle
|
|
firstname - String
|
Will always have at least firstname or lastname |
lastname - String
|
Will always have at least firstname or lastname |
street - String
|
|
zipcode - String
|
|
city - String
|
|
country - String!
|
ISO 3166-1 alpha-2 country code |
language - String
|
Preferred communication language. ISO 639-1 2 character Code. See |
email - String
|
|
telephone - String
|
May be either a mobile phone or a landline. Some features might not be supported, like sms or whatsapp |
mobile - String
|
Despite the name, may be either a mobile phone or a landline. Some features might not be supported, like sms or whatsapp |
fax - String
|
|
stayPreferences - String
|
|
mealPreferences - String
|
|
nationality - String
|
|
passportNumber - String
|
|
idCardNumber - String
|
|
issuingAuthority - String
|
|
carPlateNumber - String
|
|
birthday - Date
|
|
newsletterSubscriptionEnabled - Boolean!
|
true when client agreed to recieve martketing newsletter
|
newsletterSubscriptionDoubleOptInAt - Datetime
|
Contains the date and time of when client provided consent to receive the newsletter via double opt-in. |
birthdayGreetingsEnabled - Boolean!
|
true when client agreed to receive birthday greeting
|
Example
{
"id": "4",
"clientTitle": ClientTitle,
"firstname": "abc123",
"lastname": "abc123",
"street": "abc123",
"zipcode": "abc123",
"city": "abc123",
"country": "abc123",
"language": "abc123",
"email": "abc123",
"telephone": "xyz789",
"mobile": "abc123",
"fax": "xyz789",
"stayPreferences": "xyz789",
"mealPreferences": "abc123",
"nationality": "abc123",
"passportNumber": "abc123",
"idCardNumber": "abc123",
"issuingAuthority": "xyz789",
"carPlateNumber": "abc123",
"birthday": "2007-12-03",
"newsletterSubscriptionEnabled": false,
"newsletterSubscriptionDoubleOptInAt": Datetime,
"birthdayGreetingsEnabled": true
}
Company
Fields
| Field Name | Description |
|---|---|
id - ID!
|
|
company - String!
|
|
companyAdditionalInformation - String
|
|
street - String
|
|
zipcode - String
|
|
city - String
|
|
country - String!
|
ISO 3166-1 alpha-2 country code |
language - String
|
Preferred communication language. ISO 639-1 2 character Code. See |
email - String
|
|
telephone - String
|
May be either a mobile phone or a landline. Some features might not be supported, like sms or whatsapp |
mobile - String
|
Despite the name, may be either a mobile phone or a landline. Some features might not be supported, like sms or whatsapp |
fax - String
|
|
stayPreferences - String
|
|
newsletterSubscriptionEnabled - Boolean!
|
true when client agreed to recieve martketing newsletter
|
newsletterSubscriptionDoubleOptInAt - Datetime
|
Contains the date and time of when client provided consent to receive the newsletter via double opt-in. |
Example
{
"id": 4,
"company": "abc123",
"companyAdditionalInformation": "xyz789",
"street": "xyz789",
"zipcode": "xyz789",
"city": "xyz789",
"country": "xyz789",
"language": "xyz789",
"email": "abc123",
"telephone": "abc123",
"mobile": "xyz789",
"fax": "abc123",
"stayPreferences": "abc123",
"newsletterSubscriptionEnabled": false,
"newsletterSubscriptionDoubleOptInAt": Datetime
}
ClientTitleConnection
Description
Paginated client titles.
Default page size is 20, if not provided. Maximum page size is 100.
Pagination implements Relay Connection spec. For detailed information follow https://relay.dev/graphql/connections.htm
Fields
| Field Name | Description |
|---|---|
edges - [ClientTitleEdge!]!
|
List of paginated items |
pageInfo - PageInfo!
|
|
totalCount - Int!
|
Total number of items matching $filter |
Example
{
"edges": [ClientTitleEdge],
"pageInfo": PageInfo,
"totalCount": 987
}
ClientTitleEdge
Fields
| Field Name | Description |
|---|---|
cursor - String!
|
Unique id of node. Use it for pagination to get surrounding entries |
node - ClientTitle!
|
Pagination item |
Example
{
"cursor": "xyz789",
"node": ClientTitle
}
ClientTitle
BookingSource
Fields
| Field Name | Description |
|---|---|
name - String!
|
Example
{"name": "xyz789"}
BookingType
Fields
| Field Name | Description |
|---|---|
name - String!
|
Example
{"name": "abc123"}
StayType
Fields
| Field Name | Description |
|---|---|
name - String!
|
Example
{"name": "abc123"}
PaymentTermsConnection
Description
Paginated payment terms.
Default page size is 20, if not provided. Maximum page size is 100.
Pagination implements Relay Connection spec. For detailed information follow https://relay.dev/graphql/connections.htm
Fields
| Field Name | Description |
|---|---|
edges - [PaymentTermsEdge!]!
|
List of paginated items |
pageInfo - PageInfo!
|
|
totalCount - Int!
|
Total number of payment terms |
Example
{
"edges": [PaymentTermsEdge],
"pageInfo": PageInfo,
"totalCount": 123
}
PaymentTermsEdge
Fields
| Field Name | Description |
|---|---|
cursor - String!
|
Unique id of node. Use it for pagination to get surrounding entries. |
node - PaymentTerms!
|
Pagination item |
Example
{
"cursor": "abc123",
"node": PaymentTerms
}
PaymentTerms
RoomSetupConnection
Description
Paginated physical rooms.
Default page size is 20, if not provided. Maximum page size is 100.
Pagination implements Relay Connection spec. For detailed information follow https://relay.dev/graphql/connections.htm
Fields
| Field Name | Description |
|---|---|
edges - [RoomSetupEdge!]!
|
List of paginated items |
pageInfo - PageInfo!
|
|
totalCount - Int!
|
Total number of items matching $filter |
Example
{
"edges": [RoomSetupEdge],
"pageInfo": PageInfo,
"totalCount": 123
}
RoomSetupEdge
Fields
| Field Name | Description |
|---|---|
cursor - String!
|
Unique id of node. Use it for pagination to get surrounding entries |
node - RoomSetup!
|
Pagination item |
Example
{
"cursor": "xyz789",
"node": RoomSetup
}
RoomSetup
Description
Physical room details
Fields
| Field Name | Description |
|---|---|
id - ID!
|
|
name - String!
|
Room number |
areaName - String!
|
|
cleaningStatus - CleaningStatus!
|
|
expectedCleaningStatus - CleaningStatus!
|
Rooms dont have to be cleaned everyday. Used to check initial cleaning status on $date. Only future dates can be passed. |
Arguments
|
|
category - Category!
|
Default category from which rate and other ammenities are taken from |
countTowardsPerformance - Boolean!
|
Some rooms may be used for purposes other than lodgings, for example, conference rooms or car parking spaces. In such cases, when this value is false, the room should not be considered towards Key Performance Metrics. |
roomStays - RoomStayConnection!
|
Get a list of reserved room stays for current room. By default, cancelled rooms are not returned. If a previously requested room's id is no longer returned, assume it has been moved to a different day, deleted or reservation cancelled altogether. If present, returns rooms after Similarly, returns rooms before |
Arguments |
|
Example
{
"id": 4,
"name": "abc123",
"areaName": "xyz789",
"cleaningStatus": "CLEAN",
"expectedCleaningStatus": "CLEAN",
"category": Category,
"countTowardsPerformance": false,
"roomStays": RoomStayConnection
}
CategoryConnection
Description
Paginated categories.
Default page size is 20, if not provided. Maximum page size is 100.
Pagination implements Relay Connection spec. For detailed information follow https://relay.dev/graphql/connections.htm
Fields
| Field Name | Description |
|---|---|
edges - [CategoryEdge!]!
|
List of paginated items |
pageInfo - PageInfo!
|
|
totalCount - Int!
|
Total number of items matching $filter |
Example
{
"edges": [CategoryEdge],
"pageInfo": PageInfo,
"totalCount": 987
}
CategoryEdge
Category
Description
Accommodations are divided into categories (e.g. double room superior) and rooms (e.g. room 312).
The properties of a room are determined by the category it belongs to, so each room must be assigned to a category.
The availability is determined via the rooms available at a certain point in time for each category.
Fields
| Field Name | Description |
|---|---|
id - ID!
|
|
name - String!
|
Name of category, e.g., double room or single room. Usually meant only for staff. If you're looking for a value to display to guests, consider using description |
description - String!
|
Category description meant for guests. This description is on invoices and emails sent by 3RPMS.
|
Arguments
|
|
standardOccupancy - Int!
|
Usually, this is the number of beds the category has |
minOccupancy - Int
|
Minimum occupancy a non-standard rate has been defined for. When Occupancy specific rates are defined for interval [minOccupancy, maxOccupancy] |
maxOccupancy - Int
|
Maximum occupancy a non-standard rate has been defined for. When Occupancy specific rates are defined for interval [minOccupancy, maxOccupancy] |
minimumRate - Decimal!
|
Minimum allowed rate for any date. Rate may still be 0.00 regardless of this value if no rate for the day has been sate. |
roomSetups - RoomSetupConnection!
|
Active physical rooms that use this category by default. If a room is no longer in the list, assume it has been moved to a different category, deleted or deactivated. If present, returns rooms after Similarly, returns rooms before |
defaultPrices - CategoryPriceConnection!
|
Rates used for this category If present, returns rates after Similarly, returns rates before |
Arguments |
|
restrictions - CategoryRestrictionConnection!
|
Restrictions set up for this category If present, returns restrictions after Similarly, returns restrictions before |
Arguments |
|
availability - [CategoryAvailability!]!
|
Room availability count, grouped by date. Even with no reservations, this might not be the same as total number of rooms, if a room is disabled for maintenance or other reasons. |
Arguments
|
|
Example
{
"id": "4",
"name": "xyz789",
"description": "xyz789",
"standardOccupancy": 123,
"minOccupancy": 123,
"maxOccupancy": 987,
"minimumRate": Decimal,
"roomSetups": RoomSetupConnection,
"defaultPrices": CategoryPriceConnection,
"restrictions": CategoryRestrictionConnection,
"availability": [CategoryAvailability]
}
CategoryPriceConnection
Description
Paginated category rates.
Default page size is 20, if not provided. Maximum page size is 100.
Pagination implements Relay Connection spec. For detailed information follow https://relay.dev/graphql/connections.htm
Fields
| Field Name | Description |
|---|---|
edges - [CategoryPriceEdge!]!
|
List of paginated items |
pageInfo - PageInfo!
|
|
totalCount - Int!
|
Total number of items matching $filter |
Example
{
"edges": [CategoryPriceEdge],
"pageInfo": PageInfo,
"totalCount": 987
}
CategoryPriceEdge
Fields
| Field Name | Description |
|---|---|
cursor - String!
|
Unique id of node. Use it for pagination to get surrounding entries |
node - CategoryPrice!
|
Pagination item |
Example
{
"cursor": "abc123",
"node": CategoryPrice
}
CategoryPrice
Fields
| Field Name | Description |
|---|---|
category - Category!
|
|
date - Date!
|
|
amount - Decimal!
|
Rate for standard occupancy |
occupancies - [CategoryOccupancyPrice!]!
|
Example
{
"category": Category,
"date": "2007-12-03",
"amount": Decimal,
"occupancies": [CategoryOccupancyPrice]
}
CategoryOccupancyPrice
CategoryAvailability
UpdateReservationPayload
Fields
| Field Name | Description |
|---|---|
reservation - Reservation
|
Example
{"reservation": Reservation}
ReservationConnection
Description
Paginated reservations.
Default page size is 20, if not provided. Maximum page size is 100.
Pagination implements Relay Connection spec. For detailed information follow https://relay.dev/graphql/connections.htm
Fields
| Field Name | Description |
|---|---|
edges - [ReservationEdge!]!
|
List of paginated items |
pageInfo - PageInfo!
|
|
totalCount - Int!
|
Total number of items matching $filter |
Example
{
"edges": [ReservationEdge],
"pageInfo": PageInfo,
"totalCount": 987
}
ReservationEdge
Fields
| Field Name | Description |
|---|---|
cursor - String!
|
Unique id of node. Use it for pagination to get surrounding entries |
node - Reservation!
|
Pagination item |
Example
{
"cursor": "abc123",
"node": Reservation
}
Reservation
Fields
| Field Name | Description |
|---|---|
id - ID!
|
|
code - String!
|
Uniquely identifies the reservation for this hotel. Guaranteed to never change. Guest may or may not know the reservation code. |
bookingChannelCode - String
|
External booking channel/portal provided reservation code. Not present for reservations created directly in pms. Usually known to guests. |
pmsUrl - String!
|
Full url to open the reservation in 3RPMS. Value may change at any point. |
status - ReservationStatus!
|
|
cancelledAt - Datetime
|
Contains the date and time of cancellation, when reservation's status is CANCELLED. |
groupName - String!
|
|
client - Client!
|
Client details on who made the reservation. Do not confuse this with room guests - they may or may not be the same |
contact - Person
|
If present, prefer communicating must be done through contact details |
selfcheckinStatus - SelfcheckinStatus!
|
When AVAILABLE, you may direct the guest to selfcheckinUrl |
selfcheckinUrl - String
|
Absolute url to where the guest can complete selfcheckin. May be present even if selfcheckinEnabled is false |
bookingSource - BookingSource
|
|
bookingType - BookingType
|
|
stayType - StayType
|
|
paymentTerms - PaymentTerms
|
|
rooms - RoomStayConnection!
|
Get a list of reserved rooms. If present, returns rooms after Similarly, returns rooms before |
Arguments |
|
createdAt - Datetime!
|
|
updatedAt - Datetime!
|
|
notes - String!
|
Internal notes about a reservation. Must be visible only to hotel staff. Must not be visible to guests. |
deposits - IncomingPaymentConnection!
|
A list of non-cancelled deposits for this reservation. If present, returns deposits after Similarly, returns deposits before |
totalAmount - Decimal!
|
Rooms' and products' price summed up. Invoiced items are summed as well. |
openAmount - Decimal!
|
Total amount, with already made deposits and invoiced items subtracted. If you intend to ask guests for a deposit, this is the amount to use to cover the entire reservation. |
billingClient - Client
|
This information will appear on the invoice. |
billingContact - Person
|
This information will be used as contact details for the invoice. |
Example
{
"id": 4,
"code": "abc123",
"bookingChannelCode": "abc123",
"pmsUrl": "abc123",
"status": "CANCELLED",
"cancelledAt": Datetime,
"groupName": "xyz789",
"client": Client,
"contact": Person,
"selfcheckinStatus": "DISABLED",
"selfcheckinUrl": "xyz789",
"bookingSource": BookingSource,
"bookingType": BookingType,
"stayType": StayType,
"paymentTerms": PaymentTerms,
"rooms": RoomStayConnection,
"createdAt": Datetime,
"updatedAt": Datetime,
"notes": "abc123",
"deposits": IncomingPaymentConnection,
"totalAmount": Decimal,
"openAmount": Decimal,
"billingClient": Client,
"billingContact": Person
}
Settings
Fields
| Field Name | Description |
|---|---|
categories - CategoryConnection!
|
If present, returns categories after Similarly, returns categories before |
Arguments |
|
roomSetups - RoomSetupConnection!
|
If present, returns rooms after Similarly, returns rooms before |
Arguments |
|
Example
{
"categories": CategoryConnection,
"roomSetups": RoomSetupConnection
}
PerformanceStatistics
Description
Performance statistics for the selected date
Example
{
"occupancy": 987.65,
"adr": Decimal,
"revPAR": Decimal
}
Inventory
Fields
| Field Name | Description |
|---|---|
available - InventoryRoomConnection!
|
List of available rooms for sale for the given period. Even with no reservations, this might not be the same as total number of rooms, if a rooms is disabled for maintence or other reasons. Default page size is 20, if not provided. Maximum page size is 100. Pagination implements Relay Connection spec. For detailed information follow https://relay.dev/graphql/connections.htm |
occupied - InventoryRoomConnection!
|
List of already booked or blocked rooms in the given period. Reservations placed in sandbox are not counted here. Default page size is 20, if not provided. Maximum page size is 100. Pagination implements Relay Connection spec. For detailed information follow https://relay.dev/graphql/connections.htm |
booked - InventoryRoomConnection!
|
List of rooms with one or more booking on it (in case of overbookings) in the given period. Blocked rooms do not appear in this list. Default page size is 20, if not provided. Maximum page size is 100. Pagination implements Relay Connection spec. For detailed information follow https://relay.dev/graphql/connections.htm |
Example
{
"available": InventoryRoomConnection,
"occupied": InventoryRoomConnection,
"booked": InventoryRoomConnection
}
InventoryRoomConnection
Description
Paginated inventory rooms
Default page size is 20, if not provided. Maximum page size is 100.
Pagination implements Relay Connection spec. For detailed information follow https://relay.dev/graphql/connections.htm
Fields
| Field Name | Description |
|---|---|
edges - [InventoryRoomEdge!]!
|
List of paginated items |
pageInfo - PageInfo!
|
|
totalCount - Int!
|
Total number of items matching $filter |
Example
{
"edges": [InventoryRoomEdge],
"pageInfo": PageInfo,
"totalCount": 987
}
InventoryRoomEdge
Fields
| Field Name | Description |
|---|---|
cursor - String!
|
Unique id of node. Use it for pagination to get surrounding entries |
node - RoomSetup!
|
Pagination item |
Example
{
"cursor": "xyz789",
"node": RoomSetup
}
CategoryRestrictionConnection
Description
Paginated category restrictions.
Default page size is 20, if not provided. Maximum page size is 100.
Pagination implements Relay Connection spec. For detailed information follow https://relay.dev/graphql/connections.htm
Fields
| Field Name | Description |
|---|---|
edges - [CategoryRestrictionEdge!]!
|
List of paginated items |
pageInfo - PageInfo!
|
|
totalCount - Int!
|
Total number of items matching $filter |
Example
{
"edges": [CategoryRestrictionEdge],
"pageInfo": PageInfo,
"totalCount": 123
}
CategoryRestrictionEdge
Fields
| Field Name | Description |
|---|---|
cursor - String!
|
Unique id of node. Use it for pagination to get surrounding entries |
node - CategoryRestriction!
|
Pagination item |
Example
{
"cursor": "abc123",
"node": CategoryRestriction
}
CategoryRestriction
Fields
| Field Name | Description |
|---|---|
date - Date!
|
|
stopSell - Boolean!
|
When active, category is not bookable |
minStay - Int!
|
Minimum Length of Stay |
maxStay - Int
|
Maximum Length of Stay. NULL represents a no limit on maximum stay |
closedToArrival - Boolean!
|
|
closedToDeparture - Boolean!
|
|
guarantee - Boolean!
|
Is room bookableable without the need of a guarantee |
cancellation - Int
|
The amount of days whilst possible to cancel prior to arrival without penalties. NULL is non cancellable, 0 is a standard policy as defined in booking portals. |
breakfastIncluded - Boolean!
|
Example
{
"date": "2007-12-03",
"stopSell": true,
"minStay": 987,
"maxStay": 123,
"closedToArrival": false,
"closedToDeparture": true,
"guarantee": false,
"cancellation": 123,
"breakfastIncluded": false
}
LanguageConnection
Description
Paginated communication languages
Default page size is 20, if not provided. Maximum page size is 100.
Pagination implements Relay Connection spec. For detailed information follow https://relay.dev/graphql/connections.htm
Fields
| Field Name | Description |
|---|---|
edges - [LanguageEdge!]!
|
List of paginated items |
pageInfo - PageInfo!
|
|
totalCount - Int!
|
Total number of products |
Example
{
"edges": [LanguageEdge],
"pageInfo": PageInfo,
"totalCount": 987
}
LanguageEdge
Language
WebhookEndpointConnection
Description
Paginated webhook endpoints
Default page size is 20, if not provided. Maximum page size is 100.
Pagination implements Relay Connection spec. For detailed information follow https://relay.dev/graphql/connections.htm
Fields
| Field Name | Description |
|---|---|
edges - [WebhookEndpointEdge!]!
|
List of paginated items |
pageInfo - PageInfo!
|
|
totalCount - Int!
|
Total number of products |
Example
{
"edges": [WebhookEndpointEdge],
"pageInfo": PageInfo,
"totalCount": 987
}
WebhookEndpointEdge
Fields
| Field Name | Description |
|---|---|
cursor - String!
|
Unique id of node. Use it for pagination to get surrounding entries |
node - WebhookEndpoint!
|
Pagination item |
Example
{
"cursor": "xyz789",
"node": WebhookEndpoint
}
WebhookEndpoint
Fields
| Field Name | Description |
|---|---|
id - ID!
|
|
url - String!
|
|
status - WebhookEndpointStatus!
|
|
events - [String!]!
|
|
createdAt - Datetime!
|
|
updatedAt - Datetime!
|
Example
{
"id": 4,
"url": "abc123",
"status": "ENABLED",
"events": ["xyz789"],
"createdAt": Datetime,
"updatedAt": Datetime
}
CreateWebhookEndpointPayload
Fields
| Field Name | Description |
|---|---|
webhookEndpoint - WebhookEndpoint
|
|
secret - String
|
Used for signing webhook payload. Secret can be retrieved only once on creation |
Example
{
"webhookEndpoint": WebhookEndpoint,
"secret": "abc123"
}
UpdateWebhookEndpointPayload
Fields
| Field Name | Description |
|---|---|
webhookEndpoint - WebhookEndpoint
|
Example
{"webhookEndpoint": WebhookEndpoint}
DeleteWebhookEndpointPayload
Fields
| Field Name | Description |
|---|---|
deleted - Boolean!
|
Always true |
Example
{"deleted": false}
CreatePaymentMethodPayload
Fields
| Field Name | Description |
|---|---|
paymentMethod - PaymentMethod
|
Example
{"paymentMethod": PaymentMethod}
UpdatePaymentMethodPayload
Fields
| Field Name | Description |
|---|---|
paymentMethod - PaymentMethod
|
Example
{"paymentMethod": PaymentMethod}
DeletePaymentMethodPayload
Fields
| Field Name | Description |
|---|---|
deleted - Boolean!
|
Always true |
Example
{"deleted": true}
PaymentMethodConnection
Description
Paginated payment methods.
Default page size is 20, if not provided. Maximum page size is 100.
Pagination implements Relay Connection spec. For detailed information follow https://relay.dev/graphql/connections.htm
Fields
| Field Name | Description |
|---|---|
edges - [PaymentMethodEdge!]!
|
List of paginated items |
pageInfo - PageInfo!
|
|
totalCount - Int!
|
Total number of items matching $filter |
Example
{
"edges": [PaymentMethodEdge],
"pageInfo": PageInfo,
"totalCount": 123
}
PaymentMethodEdge
Fields
| Field Name | Description |
|---|---|
cursor - String!
|
Unique id of node. Use it for pagination to get surrounding entries |
node - PaymentMethod!
|
Pagination item |
Example
{
"cursor": "xyz789",
"node": PaymentMethod
}
PaymentMethod
CreateDepositPayload
Fields
| Field Name | Description |
|---|---|
incomingPayment - IncomingPayment!
|
Example
{"incomingPayment": IncomingPayment}
IncomingPaymentConnection
Description
Paginated incoming payments.
Default page size is 20, if not provided. Maximum page size is 100.
Pagination implements Relay Connection spec. For detailed information follow https://relay.dev/graphql/connections.htm
Fields
| Field Name | Description |
|---|---|
edges - [IncomingPaymentEdge!]!
|
List of paginated items |
pageInfo - PageInfo!
|
|
totalCount - Int!
|
Total number of items matching $filter |
Example
{
"edges": [IncomingPaymentEdge],
"pageInfo": PageInfo,
"totalCount": 987
}
IncomingPaymentEdge
Fields
| Field Name | Description |
|---|---|
cursor - String!
|
Unique id of node. Use it for pagination to get surrounding entries |
node - IncomingPayment!
|
Pagination item |
Example
{
"cursor": "xyz789",
"node": IncomingPayment
}
IncomingPayment
Fields
| Field Name | Description |
|---|---|
id - ID!
|
|
paymentMethodName - String!
|
Payment name at the time the payment was created. Might be different from paymentMethod.paymentName if it has been changed. |
datetime - Datetime!
|
|
amount - Decimal!
|
|
paymentMethod - PaymentMethod!
|
|
receiptUrl - String
|
Value may be
|
Arguments
|
|
Example
{
"id": "4",
"paymentMethodName": "xyz789",
"datetime": Datetime,
"amount": Decimal,
"paymentMethod": PaymentMethod,
"receiptUrl": "abc123"
}
AddRoomStayGuestPayload
Fields
| Field Name | Description |
|---|---|
roomStay - RoomStay
|
Example
{"roomStay": RoomStay}
RemoveRoomStayGuestPayload
Fields
| Field Name | Description |
|---|---|
roomStay - RoomStay
|
Example
{"roomStay": RoomStay}
CreateClientPayload
Fields
| Field Name | Description |
|---|---|
client - Client
|
Example
{"client": Client}
UpdateClientPayload
Fields
| Field Name | Description |
|---|---|
client - Client
|
Example
{"client": Client}
CreateExternalSalesProductPayload
Fields
| Field Name | Description |
|---|---|
product - ExternalSalesProduct
|
Example
{"product": ExternalSalesProduct}
UpdateExternalSalesProductPayload
Fields
| Field Name | Description |
|---|---|
product - ExternalSalesProduct
|
Example
{"product": ExternalSalesProduct}
CreateExternalSalePayload
Fields
| Field Name | Description |
|---|---|
created - Boolean!
|
Always true |
Example
{"created": false}
ExternalSalesProductConnection
Description
Paginated products.
Default page size is 20, if not provided. Maximum page size is 100.
Pagination implements Relay Connection spec. For detailed information follow https://relay.dev/graphql/connections.htm
Fields
| Field Name | Description |
|---|---|
edges - [ExternalSalesProductEdge!]!
|
List of paginated items |
pageInfo - PageInfo!
|
|
totalCount - Int!
|
Total number of items matching $filter |
Example
{
"edges": [ExternalSalesProductEdge],
"pageInfo": PageInfo,
"totalCount": 123
}
ExternalSalesProductEdge
Fields
| Field Name | Description |
|---|---|
cursor - String!
|
Unique id of node. Use it for pagination to get surrounding entries |
node - ExternalSalesProduct!
|
Pagination item |
Example
{
"cursor": "abc123",
"node": ExternalSalesProduct
}
ExternalSalesProduct
Input objects
UpdateCategoryPricesInput
Fields
| Input Field | Description |
|---|---|
prices - [CategoryPriceInput!]!
|
Up to 1000 updates are allowed at a time |
Example
{"prices": [CategoryPriceInput]}
CategoryPriceInput
Fields
| Input Field | Description |
|---|---|
category - ID!
|
|
dateRange - DateRangeInput!
|
|
occupancy - Int
|
NULL for default occupancy
|
amount - Decimal
|
For occupancy rates NULL can be used to reset to default derived rate. |
Example
{
"category": 4,
"dateRange": DateRangeInput,
"occupancy": 987,
"amount": Decimal
}
UpdateCategoryRestrictionsInput
Fields
| Input Field | Description |
|---|---|
restrictions - [CategoryRestrictionInput!]!
|
Up to 1000 updates are allowed at a time |
Example
{"restrictions": [CategoryRestrictionInput]}
CategoryRestrictionInput
Fields
| Input Field | Description |
|---|---|
category - ID!
|
|
dateRange - DateRangeInput!
|
|
stopSell - Boolean
|
|
minStay - Int
|
Value must be between 1 and 99 |
maxStay - Int
|
NULL for no limit on maximum stay. When non-null, value must be larger or equal to minStay.
|
closedToArrival - Boolean
|
|
closedToDeparture - Boolean
|
|
guarantee - Boolean
|
|
cancellation - Int
|
Allowed values: NULL (non cancellable) , 0 (standard policy), 1, 2, 3, 7, 15, 30, 45, 90, |
breakfastIncluded - Boolean
|
Example
{
"category": "4",
"dateRange": DateRangeInput,
"stopSell": false,
"minStay": 123,
"maxStay": 123,
"closedToArrival": false,
"closedToDeparture": true,
"guarantee": false,
"cancellation": 123,
"breakfastIncluded": true
}
UpdateRoomStayInput
Example
{
"id": 4,
"check_in": Datetime,
"check_out": Datetime
}
UpdateRoomSetupInput
Fields
| Input Field | Description |
|---|---|
id - ID!
|
|
cleaningStatus - CleaningStatus
|
Example
{"id": "4", "cleaningStatus": "CLEAN"}
RoomAccessKeyFilter
Fields
| Input Field | Description |
|---|---|
id - IdOperators
|
|
not - RoomAccessKeyFilter
|
|
or - [RoomAccessKeyFilter!]
|
Example
{
"id": IdOperators,
"not": RoomAccessKeyFilter,
"or": [RoomAccessKeyFilter]
}
CreateRoomAccessKeyInput
Description
Not all hotels support Room Access Api. Requires one and only one to be set: roomAccessPin, roomAccessKeyStorage, roomAccessQR.
Fields
| Input Field | Description |
|---|---|
roomAccessPin - CreateRoomAccessPinInput
|
When provided, payload's roomAccessKey field will be of type RoomAccessPin |
roomAccessKeyStorage - CreateRoomAccessKeyStorageInput
|
When provided, payload's roomAccessKey field will be of type RoomAccessKeyStorage |
roomAccessQR - CreateRoomAccessQRInput
|
When provided, payload's roomAccessKey field will be of type RoomAccessQR |
Example
{
"roomAccessPin": CreateRoomAccessPinInput,
"roomAccessKeyStorage": CreateRoomAccessKeyStorageInput,
"roomAccessQR": CreateRoomAccessQRInput
}
CreateRoomAccessPinInput
Fields
| Input Field | Description |
|---|---|
pin - String!
|
Must match against ([A-Z0-9]|[a-z0-9]){1,10} (both lowercase and uppercase are allowed, but they should not be mixed) Your integration is responsible for ensuring the same pin is not used for multiple reservation rooms at the same time |
Example
{"pin": "abc123"}
CreateRoomAccessKeyStorageInput
CreateRoomAccessQRInput
Fields
| Input Field | Description |
|---|---|
qrData - String!
|
The data being used to generate QR code. At most 512 characters are allowed |
Example
{"qrData": "abc123"}
DeleteRoomAccessKeyInput
Fields
| Input Field | Description |
|---|---|
roomAccessKeyId - ID!
|
Restrictions when deleting an access key:
|
Example
{"roomAccessKeyId": 4}
AddRoomAccessKeyInput
RemoveRoomAccessKeyInput
RoomStayFilter
Fields
| Input Field | Description |
|---|---|
id - IdOperators
|
|
date - Date
|
All reservations that arrive or stayover on $date Alias for |
reservation_from - DateOperators
|
|
reservation_to - DateOperators
|
|
reservationStatus - ReservationStatusOperators
|
|
updatedAt - DatetimeOperators
|
|
check_in - DatetimeOperators
|
|
check_out - DatetimeOperators
|
|
not - RoomStayFilter
|
|
or - [RoomStayFilter!]
|
Example
{
"id": IdOperators,
"date": "2007-12-03",
"reservation_from": DateOperators,
"reservation_to": DateOperators,
"reservationStatus": ReservationStatusOperators,
"updatedAt": DatetimeOperators,
"check_in": DatetimeOperators,
"check_out": DatetimeOperators,
"not": RoomStayFilter,
"or": [RoomStayFilter]
}
ClientFilter
Fields
| Input Field | Description |
|---|---|
id - IdOperators
|
|
birthday - BirthdayOperators
|
|
not - ClientFilter
|
|
or - [ClientFilter!]
|
Example
{
"id": IdOperators,
"birthday": BirthdayOperators,
"not": ClientFilter,
"or": [ClientFilter]
}
CategoryPriceFilter
Fields
| Input Field | Description |
|---|---|
date - DateRangeInput!
|
Example
{"date": DateRangeInput}
CategoryRestrictionFilter
Fields
| Input Field | Description |
|---|---|
date - DateRangeInput!
|
Example
{"date": DateRangeInput}
CategoryAvailabilityFilter
Fields
| Input Field | Description |
|---|---|
date - DateRangeInput!
|
Date range can contain at most 1000 days |
Example
{"date": DateRangeInput}
UpdateReservationInput
ReservationFilter
Fields
| Input Field | Description |
|---|---|
id - IdOperators
|
|
code - StringOperators
|
|
bookingChannelCode - StringOperators
|
|
status - ReservationStatusOperators
|
|
updatedAt - DatetimeOperators
|
|
not - ReservationFilter
|
|
or - [ReservationFilter!]
|
Example
{
"id": IdOperators,
"code": StringOperators,
"bookingChannelCode": StringOperators,
"status": ReservationStatusOperators,
"updatedAt": DatetimeOperators,
"not": ReservationFilter,
"or": [ReservationFilter]
}
CategoryFilter
Fields
| Input Field | Description |
|---|---|
id - IdOperators
|
|
not - CategoryFilter
|
|
or - [CategoryFilter!]
|
Example
{
"id": IdOperators,
"not": CategoryFilter,
"or": [CategoryFilter]
}
RoomSetupFilter
Fields
| Input Field | Description |
|---|---|
id - IdOperators
|
|
not - RoomSetupFilter
|
|
or - [RoomSetupFilter!]
|
Example
{
"id": IdOperators,
"not": RoomSetupFilter,
"or": [RoomSetupFilter]
}
StatisticsFilter
Fields
| Input Field | Description |
|---|---|
date - Date!
|
Statistics for selected date |
Example
{"date": "2007-12-03"}
DateRangeInput
InventoryFilter
Fields
| Input Field | Description |
|---|---|
period - DateRangeInput!
|
|
categories - [String!]
|
List of category ids. If not provided, all categories will be used |
Example
{
"period": DateRangeInput,
"categories": ["abc123"]
}
ReservationStatusOperators
Fields
| Input Field | Description |
|---|---|
eq - ReservationStatus
|
field = x |
in - [ReservationStatus!]
|
field = x OR field = y OR ... |
Example
{"eq": "CANCELLED", "in": ["CANCELLED"]}
WebhookEndpointFilter
Fields
| Input Field | Description |
|---|---|
id - IdOperators
|
|
not - WebhookEndpointFilter
|
|
or - [WebhookEndpointFilter!]
|
Example
{
"id": IdOperators,
"not": WebhookEndpointFilter,
"or": [WebhookEndpointFilter]
}
CreateWebhookEndpointInput
Fields
| Input Field | Description |
|---|---|
url - String!
|
|
status - WebhookEndpointStatus!
|
|
events - [String!]!
|
Example
{
"url": "abc123",
"status": "ENABLED",
"events": ["abc123"]
}
UpdateWebhookEndpointInput
Fields
| Input Field | Description |
|---|---|
id - ID!
|
|
url - String
|
|
status - WebhookEndpointStatus
|
When disabling an endpoint, any currently pending webhooks may still be sent |
events - [String!]
|
When removing an event, any currently pending webhooks may still be sent |
Example
{
"id": "4",
"url": "abc123",
"status": "ENABLED",
"events": ["abc123"]
}
DeleteWebhookEndpointInput
Fields
| Input Field | Description |
|---|---|
id - ID!
|
Example
{"id": 4}
CreatePaymentMethodInput
Fields
| Input Field | Description |
|---|---|
name - String!
|
Example
{"name": "abc123"}
UpdatePaymentMethodInput
DeletePaymentMethodInput
Fields
| Input Field | Description |
|---|---|
id - ID!
|
Example
{"id": 4}
PaymentMethodFilter
Fields
| Input Field | Description |
|---|---|
id - IdOperators
|
|
not - PaymentMethodFilter
|
|
or - [PaymentMethodFilter!]
|
Example
{
"id": IdOperators,
"not": PaymentMethodFilter,
"or": [PaymentMethodFilter]
}
CreateDepositInput
ReceiptOptions
Fields
| Input Field | Description |
|---|---|
languageCode - String!
|
See communicationLanguages query for a list of allowed language codes. For invalid language codes, a hotel-defined fallback will be used |
Example
{"languageCode": "abc123"}
AddRoomStayGuestInput
RemoveRoomStayGuestInput
CreateClientInput
Fields
| Input Field | Description |
|---|---|
person - CreatePersonInput!
|
Example
{"person": CreatePersonInput}
CreatePersonInput
Fields
| Input Field | Description |
|---|---|
clientTitleId - ID
|
|
firstname - String
|
At least one of firstname or lastname is required. |
lastname - String
|
At least one of firstname or lastname is required. |
street - String
|
|
zipcode - String
|
|
city - String
|
|
country - String!
|
|
language - String!
|
|
email - String
|
|
telephone - String
|
|
mobile - String
|
|
fax - String
|
|
preferences - String
|
preferences cannot be used together with stayPreferences |
stayPreferences - String
|
|
mealPreferences - String
|
|
nationality - String
|
|
passportNumber - String
|
|
idCardNumber - String
|
|
issuingAuthority - String
|
|
birthday - Date
|
|
carPlateNumber - String
|
Example
{
"clientTitleId": 4,
"firstname": "abc123",
"lastname": "xyz789",
"street": "xyz789",
"zipcode": "abc123",
"city": "xyz789",
"country": "abc123",
"language": "abc123",
"email": "abc123",
"telephone": "xyz789",
"mobile": "xyz789",
"fax": "abc123",
"preferences": "xyz789",
"stayPreferences": "abc123",
"mealPreferences": "xyz789",
"nationality": "abc123",
"passportNumber": "xyz789",
"idCardNumber": "abc123",
"issuingAuthority": "xyz789",
"birthday": "2007-12-03",
"carPlateNumber": "abc123"
}
UpdateClientInput
Fields
| Input Field | Description |
|---|---|
id - ID!
|
|
person - UpdatePersonInput
|
One of person or company is required, but not both at the same time. |
company - UpdateCompanyInput
|
One of person or company is required, but not both at the same time. |
Example
{
"id": "4",
"person": UpdatePersonInput,
"company": UpdateCompanyInput
}
UpdatePersonInput
Fields
| Input Field | Description |
|---|---|
clientTitleId - ID
|
|
street - String
|
|
zipcode - String
|
|
city - String
|
|
country - String
|
|
language - String
|
|
email - String
|
|
telephone - String
|
|
mobile - String
|
|
fax - String
|
|
preferences - String
|
preferences cannot be used together with stayPreferences |
stayPreferences - String
|
|
mealPreferences - String
|
|
nationality - String
|
|
passportNumber - String
|
|
idCardNumber - String
|
|
issuingAuthority - String
|
|
birthday - Date
|
|
carPlateNumber - String
|
Example
{
"clientTitleId": 4,
"street": "xyz789",
"zipcode": "xyz789",
"city": "abc123",
"country": "abc123",
"language": "abc123",
"email": "xyz789",
"telephone": "xyz789",
"mobile": "xyz789",
"fax": "abc123",
"preferences": "abc123",
"stayPreferences": "abc123",
"mealPreferences": "abc123",
"nationality": "xyz789",
"passportNumber": "xyz789",
"idCardNumber": "xyz789",
"issuingAuthority": "xyz789",
"birthday": "2007-12-03",
"carPlateNumber": "xyz789"
}
UpdateCompanyInput
Example
{
"street": "abc123",
"zipcode": "abc123",
"city": "xyz789",
"country": "xyz789",
"language": "abc123",
"email": "xyz789",
"telephone": "xyz789",
"mobile": "xyz789",
"fax": "xyz789",
"preferences": "abc123",
"stayPreferences": "abc123"
}
CreateExternalSalesProductInput
Fields
| Input Field | Description |
|---|---|
name - String!
|
Name can be up to 20 characters |
Example
{"name": "xyz789"}
UpdateExternalSalesProductInput
CreateExternalSaleInput
Fields
| Input Field | Description |
|---|---|
productId - ID!
|
External Sale product to use, id of one created in createExternalSaleProduct mutation |
roomStayId - ID!
|
Room stay to which the product will be attached to. Reservation must not be cancelled |
amount - Decimal!
|
|
saleCreatedAt - Datetime!
|
|
receiptNumber - String!
|
|
receiptPdfUrl - String
|
Url to receipt pdf. Up to 200 characters. |
waiterName - String
|
|
tableName - String
|
Example
{
"productId": "4",
"roomStayId": 4,
"amount": Decimal,
"saleCreatedAt": Datetime,
"receiptNumber": "abc123",
"receiptPdfUrl": "xyz789",
"waiterName": "xyz789",
"tableName": "xyz789"
}
IdOperators
DateOperators
Example
{
"lt": "2007-12-03",
"le": "2007-12-03",
"eq": "2007-12-03",
"in": ["2007-12-03"],
"ge": "2007-12-03",
"gt": "2007-12-03"
}
DatetimeOperators
Example
{
"lt": Datetime,
"le": Datetime,
"eq": Datetime,
"in": [Datetime],
"ge": Datetime,
"gt": Datetime
}
StringOperators
BirthdayOperators
Description
Year is ignored for all operators, for example, {eq:"2022-01-01"} will also return results for year 2021.
Range constraints wrap around when end date is before start date, for example, {ge:"2021-11-01", le:"2021-01-31"} will return results for November, December and January; while {gt:"2021-01-31", lt:"2021-11-01"} will return results for the other 9 months.
Fields
| Input Field | Description |
|---|---|
lt - Date
|
birthday < x. Cannot be used together with le
|
le - Date
|
birthday <= x. Cannot be used together with lt
|
eq - Date
|
birthday = x
|
in - [Date!]
|
birthday = x OR birthday = y OR ...
|
ge - Date
|
birthday >= x. Cannot be used together with gt
|
gt - Date
|
birthday > x. Cannot be used together with ge
|
Example
{
"lt": "2007-12-03",
"le": "2007-12-03",
"eq": "2007-12-03",
"in": ["2007-12-03"],
"ge": "2007-12-03",
"gt": "2007-12-03"
}
Interfaces
RoomAccessKey
Description
Additional types could be added at any time, make sure your integration can safely continue when an unrecognised access key type is returned
Fields
| Field Name | Description |
|---|---|
id - ID!
|
|
roomStays - RoomStayConnection!
|
Reservation rooms that have this room access key assigned to. Even when your integration always assigns an access key to only one reservation room, in some cases the same access key can be assigned on other reservation rooms by 3RPMS, for example, when splitting a reservation room in two |
Arguments |
|
Possible Types
| RoomAccessKey Types |
|---|
Example
{"id": 4, "roomStays": RoomStayConnection}
Client
Description
Each client is either a Person or Company, after creation this type never changes.
see concrete type definitions for more fields.
Fields
| Field Name | Description |
|---|---|
id - ID!
|
|
title - String
|
Client's preferred salutation Use clientTitle from the concrete Person type instead
|
firstname - String
|
Use firstname from the concrete Person type instead
|
lastname - String
|
Use lastname from the concrete Person type instead
|
company - String
|
Use company from the concrete Company type instead
|
street - String
|
|
zipcode - String
|
|
city - String
|
|
country - String!
|
ISO 3166-1 alpha-2 country code |
language - String
|
Preferred communication language. ISO 639-1 2 character Code |
email - String
|
|
telephone - String
|
May be either a mobile phone or a landline. Some features might not be supported, like sms or whatsapp |
mobile - String
|
Despite the name, may be either a mobile phone or a landline. Some features might not be supported, like sms or whatsapp |
fax - String
|
|
preferences - String
|
Use stayPreferences
|
stayPreferences - String
|
|
carPlateNumber - String
|
Use carPlateNumber from the concrete Person type instead
|
birthday - Date
|
Use birthday from the concrete Person type instead
|
newsletterSubscriptionEnabled - Boolean!
|
true when client agreed to recieve martketing newsletter
|
newsletterSubscriptionDoubleOptInAt - Datetime
|
Contains the date and time of when client provided consent to receive the newsletter via double opt-in. |
birthdayGreetingsEnabled - Boolean!
|
Use birthdayGreetingsEnabled from the concrete Person type instead
|
Example
{
"id": 4,
"title": "xyz789",
"firstname": "abc123",
"lastname": "abc123",
"company": "xyz789",
"street": "abc123",
"zipcode": "abc123",
"city": "abc123",
"country": "abc123",
"language": "abc123",
"email": "abc123",
"telephone": "abc123",
"mobile": "xyz789",
"fax": "abc123",
"preferences": "abc123",
"stayPreferences": "abc123",
"carPlateNumber": "xyz789",
"birthday": "2007-12-03",
"newsletterSubscriptionEnabled": false,
"newsletterSubscriptionDoubleOptInAt": Datetime,
"birthdayGreetingsEnabled": true
}
Enums
CleaningStatus
Values
| Enum Value | Description |
|---|---|
|
|
|
|
|
Example
"CLEAN"
SelfcheckinStatus
Values
| Enum Value | Description |
|---|---|
|
|
Self checkin not enabled for this reservation, and likely wont be. |
|
|
Self checkin is enabled, but not yet available. Too early or too late to be completed. |
|
|
Self checkin available for completion. With this state the guest may be redirected to self checkin page. |
|
|
Already been completed. Final State. |
Example
"DISABLED"
ReservationStatus
Values
| Enum Value | Description |
|---|---|
|
|
No more actions with a reservation are expected. Status can change to Active. |
|
|
Nothing invoiced, or partially invoiced. Status might change to Invoiced or Cancelled. |
|
|
Fully invoiced. Status can change back to Active. |
Example
"CANCELLED"
WebhookEndpointStatus
Values
| Enum Value | Description |
|---|---|
|
|
|
|
|
Example
"ENABLED"
Scalars
Date
Description
Format YYYY-MM-DD
Example
"2007-12-03"
Datetime
Description
Format ISO8601
Example
Datetime
Decimal
Description
A decimal number separated by a dot. Arbitrary scale and precision. For example, "9999.99".
Decimals are always sent as strings, and, for input values, must be sent as strings.
Example
Decimal
Int
Description
The Int scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.
Example
123
ID
Description
The ID scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as "4") or integer (such as 4) input value will be accepted as an ID.
Example
4
Boolean
Description
The Boolean scalar type represents true or false.
Example
true
Float
Description
The Float scalar type represents signed double-precision fractional values as specified by IEEE 754.
Example
123.45
String
Description
The String scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.
Example
"abc123"
Directives
chain
Description
Allows the value of this field to be injected into a variable for use in subsequent operation within the same request.
This enables dependent queries to be executed in a single round-trip.
Locations: FIELD
Fields
| Field Name | Description |
|---|---|
variable - String!
|
Example
{"variable": "xyz789"}
include
Description
Directs the executor to include this field or fragment only when the if argument is true.
Locations: FIELD, FRAGMENT_SPREAD, INLINE_FRAGMENT
Fields
| Field Name | Description |
|---|---|
if - Boolean!
|
Included when true. |
Example
{"if": true}
skip
Description
Directs the executor to skip this field or fragment when the if argument is true.
Locations: FIELD, FRAGMENT_SPREAD, INLINE_FRAGMENT
Fields
| Field Name | Description |
|---|---|
if - Boolean!
|
Skipped when true. |
Example
{"if": false}
Webhooks
Overview
3RPMS uses webhooks to notify your application when an event happens in a hotel. Webhooks enable 3RPMS to push real-time notifications to your integration. Webhooks are sent as a HTTPS POST request with a JSON payload. You can then use these notifications to execute actions in your integration.
Terminology
- Webhook - an event notification
- Webhook endpoint - your server that receives event notifications (webhooks)
Reference
For all webhook types the payload will be a json object with the following top-level structure:
{
"id": "[A-Za-z0-9]{64}",
"type": "room_stay.updated",
"created_at": 2147483647,
"webhook_endpoint_id": "[A-Za-z0-9]{64}",
"data": ...
}
| Field | Type | Description |
|---|---|---|
id |
string |
Unique id of this event. Multiple endpoints receiving the same event will have the same id. |
type |
string |
name of the webhook |
created_at |
int |
timestamp of when the event happened. May not be the same as when your integration receives this. |
webhook_endpoint_id |
string |
id of the webhook endpoint that was created via graphql api.Can be used to identify what hotel this webhook belongs to. |
data |
any |
Main body of the webhook. See specific type's reference for more details. |
For forwards compatibility, allow unknown fields at any nesting level in the payload
If you want to get notifications about any additional event types not listed here, contact 3RPMS support at office@3rpms.de describing your use case.
reservation.created
Sent when a new Reservation is created.
Body shape
"object": {
"id": "[A-Za-z0-9-]{1,64}"
}
| Field | Type | Description |
|---|---|---|
object.id |
string |
Associated reservation's id. Use this to query for more data from the API |
reservation.updated
Sent when an existing Reservation is updated.
Body shape
"object": {
"id": "[A-Za-z0-9-]{1,64}"
},
"updated_fields": [...]
| Field | Type | Description |
|---|---|---|
object.id |
string |
Associated reservation's id. Use this to query for more data from the API |
updated_fields |
string[]|null |
list of Reservation field names (from graphql API) that have changed. When updated_fields is not present, assume that any field could have changed. * This will not include *Connection type fields. * Fields that contain their own ID (for example, client or contact) will only be included when a different record is referenced, for example, a change to client's first name will not trigger a reservation.updated webhook (use client.updated event for that), but changing reservation's referenced client to someone else will trigger a reservation.updated webhook. |
room_stay.created
Sent when a new RoomStay is created.
Body shape
"object": {
"id": "[A-Za-z0-9-]{1,64}"
}
| Field | Type | Description |
|---|---|---|
object.id |
string |
Associated room stay's id. Use this to query for more data from the API |
updated_fields |
string[]|null |
list of RoomStay field names (from graphql API) that have changed. When updated_fields is not present, assume that any field could have changed. This will not include *Connection type fields. |
room_stay.updated
Sent when an existing RoomStay is updated.
Body shape
"object": {
"id": "[A-Za-z0-9-]{1,64}"
},
"updated_fields": [...]
| Field | Type | Description |
|---|---|---|
object.id |
string |
Associated room stay's id. Use this to query for more data from the API |
updated_fields |
string[]|null |
list of RoomStay field names (from graphql API) that have changed. When updated_fields is not present, assume that any field could have changed. * This will not include *Connection type fields. * Fields that contain their own ID (for example, reservation or guests) will only be included when a different record is referenced; for example, a change to guest's first name will not trigger a room_stay.updated webhook (use client.updated event for that), but changing adding a new guest will trigger a room_stay.updated webhook. |
room_stay.deleted
Sent when an existing RoomStay is deleted.
Body shape
"object": {
"id": "[A-Za-z0-9-]{1,64}"
}
| Field | Type | Description |
|---|---|---|
object.id |
string |
Associated room stay's id. |
client.updated
Sent when an existing Client is updated.
Body shape
"object": {
"id": "[A-Za-z0-9-]{1,64}"
}
| Field | Type | Description |
|---|---|---|
object.id |
string |
Associated client's id. Use this to query for more data from the API |
client.deleted
Sent when an existing Client is deleted.
Body shape
"object": {
"id": "[A-Za-z0-9-]{1,64}"
}
| Field | Type | Description |
|---|---|---|
object.id |
string |
Associated client's id |
room_access_key.deleted
Sent when an existing RoomAccessKey is deleted.
Body shape
"object": {
"id": "[A-Za-z0-9-]{1,64}"
}
| Field | Type | Description |
|---|---|---|
object.id |
string |
Associated room access key's id |
category.availability.updated
Sent when room availability for a Category changes.
Body shape
"object": {
"id": "[A-Za-z0-9-]{1,64}"
},
"period": {
"start": "YYYY-MM-DD",
"end": "YYYY-MM-DD"
}
| Field | Type | Description |
|---|---|---|
object.id |
string |
Associated category's id |
period |
object|null |
Affected period. Will be null when all dates are affected. |
period.start |
date-string |
Start (inclusive) of the affected period. |
period.end |
date-string |
End (inclusive) of the affected period. |
Creating a webhook
- Identify the webhooks to receive
- Handle requests from 3RPMS by parsing each event object and returning 2xx response status codes.
- Register your endpoint with 3RPMS
There is a limit of 5 active webhook endpoints per hotel per integration
Step 1: Identify the webhooks to receive
See Supported events section. Each webhook endpoint can listen to multiple events.
Create endpoints with only events you need for better performance.
Step 2: Handle requests from 3RPMS
Webhooks will be sent as a POST request, with payload in the request's body
const express = require('express');
const crypto = require('crypto');
const scmp = require('scmp');
const app = express();
// If the same endpoint is used for multiple hotels, each one will have a different endpoint secret
const webhookEndpointSecret = '[your_webhook_endpoint_secret]';
app.post('/3rpms', express.raw({type: 'application/json'}), (request, response) => {
const payloadRaw = request.body;
const signatureHeader = request.header('3rpms-signature');
const payload = JSON.parse(payloadRaw);
if (!verifyHeader(signatureHeader, payloadRaw, webhookEndpointSecret)) {
// See Security section below for `verifySignature` definition
response.sendStatus(400);
return;
}
if (payload.type === 'room_stay.created') {
const roomStayId = payload.data.object.id;
// Execute your actions for `room_stay.created`
} else if (payload.type === 'room_stay.updated') {
const roomStayId = payload.data.object.id;
// Execute your actions for `room_stay.updated`
//} else if ... { }
} else {
console.log(`Unhandled event type ${payload.type}`);
}
// Return a response to acknowledge receipt of the event
response.sendStatus(204);
});
app.listen(8000, () => console.log('Running on port 8000'));
$payloadRaw = file_get_contents('php://input');
$signatureHeader = $_SERVER['HTTP_3RPMS_SIGNATURE'];
if (!verifySignature($signatureHeader, $payloadRaw, '[your_webhook_endpoint_secret]')) {
// See Security section below for `verifySignature` definition
// If the same endpoint is used for multiple hotels, each one will have a different endpoint secret
http_response_code(400);
exit();
}
$payload = json_decode($payloadRaw, true, 4, JSON_THROW_ON_ERROR);
if ($payload['type'] === 'room_stay.created') {
$roomStayId = $payload['data']['object']['id'];
// Execute your actions for `room_stay.created`
} elseif ($payload['type'] === 'room_stay.updated') {
$roomStayId = $payload['data']['object']['id'];
// Execute your actions for `room_stay.updated`
//} eseif (...)
} else {
echo 'Received unknown event type ' . $payload['type'];
}
http_response_code(200);
All endpoints are required to use https with a validate certificate
Duplicate events
Webhook endpoints might occasionally receive the same webhook more than once. We advise you to guard against duplicated webhooks by making your webhook processing idempotent. One way of doing this is logging the webhooks you've processed, and then not processing already-logged events.
3RPMS guarantees at least once delivery for webhooks
Order of events
3RPMS does not guarantee delivery of webhooks in the order in which they are generated. For example, creating a room stay and immediately deleting it might generate the following webhooks:
room_stay.createdroom_stay.deleted
It's possible you'll receive a roomstay.deleted webhook first, for a room stay your integration doesn't know about. Your endpoint must not expect delivery of these webhooks in this order and should handle this accordingly. Use the API to fetch any missing objects (for example, you can fetch the reservation when receiving room_stay.created webhook).
Security
3RPMS signs all webhooks by including a signature in each request's 3rpms-signature header. This allows you to verify that the webhook was sent by 3RPMS, not by a third party.
To verify signatures, you'll need to save your endpoint's secret when registering your webhooks endpoint in step 3. It can be retrieved only once when first registering the endpoint.
3RPMS generates a unique secret key for each endpoint. Additionally, if you use multiple endpoints, you must obtain a secret for each one you want to verify signatures on.
Preventing replay attacks
A replay attack is when an attacker intercepts a valid payload and its signature, then re-transmits them. To mitigate such attacks, 3RPMS includes a timestamp in the 3rpms-signature header. Because this timestamp is part of the signed payload, it is also verified by the signature, so an attacker cannot change the timestamp without invalidating the signature. If the signature is valid but the timestamp is too old, you can have your application reject the payload. We recommend a tolerance of no more than five minutes between the timestamp and the current time.
3RPMS generates the timestamp and signature each time we send a webhook to your endpoint. If 3RPMS retries an webhook (for example, your endpoint previously replied with a non-2xx status code), then we generate a new signature and timestamp for the next delivery attempt.
Verify signature
The 3rpms-signature header included in each signed webhook contains a timestamp and one or more signatures. The timestamp is prefixed by t=, and each signature is prefixed by a signature=.
3rpms-signature:
t=1658997155,
signature=e889ddfe87e55422a1a6493b2db846548ff2a7f22268501b519f9f4f5f70e2ff
3RPMS generates signatures using a hash-based message authentication code (HMAC) with SHA-256. For forwards compatibility, your integration must support handling multiple signatures prefixed with signature=.
Step 1: Extract the timestamp and signatures from the header
Split the header, using the , character as the separator, to get a list of elements. Then split each element, using the = character as the separator, to get a (prefix, value) pair.
The value for the prefix t corresponds to the timestamp, and signature corresponds to the signature (or signatures). You can discard all other elements.
Step 2: Prepare the signed payload string
The signed_payload string is created by concatenating:
- The timestamp (as a string) from header
- The character
. - The full request POST body
Step 3: Determine the expected signature
Compute an HMAC with the SHA256 hash function. Use the endpoint's signing secret as the key, and use the signed_payload string as the message.
Step 4: Compare the signatures
Compare the signature (or signatures) in the header to the expected signature. For an equality match, compute the difference between the current timestamp and the received timestamp, then decide if the difference is within your tolerance.
To protect against timing attacks, use a constant-time string comparison to compare the expected signature to each of the received signatures.
Sample code
function verifyHeader(header, body, secret, tolerance = 5 * 60) {
// Header contains two parts, timestamp and signature, in format `t=123,signature=abc`
const {timestamp, signatures} = header.split(',').reduce((acc, pair) => {
const [key, value] = pair.split('=');
if (key === 't') acc.timestamp = value;
else if (key === 'signature') acc.signatures.push(value);
return acc;
}, {
timestamp: -1,
// For forwards compatibility, allow multiple `signatures` to be present `signature=agfdgfdgd,signature=gkdsgde`
signatures: [],
});
if (timestamp === -1) return false;
if (Math.abs(Math.floor(Date.now() / 1000) - timestamp) > tolerance) {
// Reject to prevent replay attacks.
return false;
}
const signedPayload = timestamp + '.' + body;
const expectedSignature = crypto.createHmac('sha256', secret).update(signedPayload, 'utf8').digest('hex');
for(const signature of signatures) {
if (scmp(Buffer.from(expectedSignature), Buffer.from(signature))) {
return true;
}
}
return false;
}
function verifySignature(string $header, string $payload, string $secret, int $tolerance = 5 * 60): bool
{
// Header contains two parts, timestamp and signatures, in format `t=123,signature=abc,signature=def`
$timestamp = null;
$signatures = [];
foreach (explode(',', $header) as $item) {
[$key, $value] = explode('=', $item, 2);
if ($key === 't') {
$timestamp = $value;
} elseif ($key === 'signature') {
$signatures[] = $value;
}
}
if (abs(time() - $timestamp) > $tolerance) {
// Difference between current time and sent time is too high.
// Reject to prevent replay attacks.
return false;
}
$signedPayload = $timestamp . '.' . $payload;
$expectedSignature = hash_hmac('sha256', $signedPayload, $secret);
// For forwards compatibility, support multiple `signatures` in the header. Any succeeding counts as verified
foreach ($signatures as $signature) {
if (hash_equals($expectedSignature, $signature)) {
return true;
}
}
return false;
}
Webhooks that take too long to deliver a response status code will be treated as failed and delivery will be attempted again.
Step 3: Register your endpoint with 3RPMS
See graphQL docs on how to set use our api
# Variables
# {
# "input": {
# "url":"https://example.com/3rpms-webhook",
# "status":"DISABLED",
# "events":["room_stay.created"]
# }
# }
mutation CreateWebhookEndpoint($input:CreateWebhookEndpointInput!) {
createWebhookEndpoint(input:$input) {
webhookEndpoint { id }
secret
}
}
Make sure to save response's webhookEndpoint.id as it may be needed to identify the hotel when receiving a webhook.
Create new webhook endpoints in a DISABLED state, and update it to ENABLED after persisting the signing secret. This will avoid signing errors for webhooks immediately after registering the webhook endpoint
Additional Data
A webhook notifies your integration that something happened and includes only the bare minimum data to identify the affected record, 3RPMS will not know what fields your integrations needs.
After receiving a webhook, its expected that your integration will query the API for the data it needs. For example, for a room_stay.updated webhook, can use the data.object.id value to query for the room's current stay period:
# Variables
# {
# "id": "<value from data.object.id>"
# }
query RoomStay($id:ID!) {
room_stays(filter:{id:{eq:$id}} first:1) {
edges {
node {
id
arrival:reservation_from
departure:reservation_to
# other fields needed
}
}
}
}
Retries
3RPMS webhooks have built-in retry methods for 3xx, 4xx, or 5xx response status codes. Any webhooks with a non-2xx respones status code will be attempted again for a few days or until a 2xx response status code is received
Webhook endpoint redirects will not be followed for security reasons. 3xx response status code is treated as failing and will be re-sent.
Endpoints will be disabled when all sent webhooks have been failing for a few days
Troubleshooting
Lost signing secret
Signing secret is retrievable only when first creating a webhook endpoint with 3RPMS. If you had not saved it or its compromised, a new webhook endpoint has to be created and the old one deleted.
Lost api key
If you want to disable a webhook endpoint, but have lost the api key, return any non-2xx response status code. After a few days, the endpoint will be disabled and 3RPMS will stop sending notifications
Stopped receiving webhooks
Endpoints that have been failing for a few days are disabled. You can check the endpoint's status via api:
query WebhookEndpointStatus($id:ID!) {
webhookEndpoints(filter:{id:{eq:$id}}) {
edges {
node { status }
}
}
}
If its DISABLED, and you've resolved the issues that caused the endpoint to be disabled in the first place, it can be enabled back via api:
# Variables:
# {
# "input": {
# "id": "your_webhook_endpoint_id",
# "status": "ENABLED"
# }
# }
mutation EnableWebhookEndpoint($input:UpdateWebhookEndpointInput!) {
updateWebhookEndpoint(input:$input) {
webhookEndpoint { status }
}
}
Guides
Using Room Access Keys
Room Access Key API is used to read or write room door pin numbers, key storage pin, or other type of room access types. Room Access Key is a generic name for anything that gives guest access to the room - pin, physical key or other types of access in the future. This API allows to:
- Reading the PIN to, for example, send the guest an sms with their room's number and PIN. We expect most integrations to care only about reading Room Access Keys.
- For door lock or key storage integrators, inform 3RPMS of what Room Access Key is being used for a reservation, for other integrations to use.
Reading Room Access Key
For most integrations only reading PIN will be relevant.
Currently two types of PINs can be present on a room:
RoomAccessPinis used for pins with direct access to the room and used for the entire duration of the stay, e.g., door locksRoomAccessKeyStorageis used for key storage that, after entering pin, will give the guest a physical key to use. This pin is meant to be used only once on arrival, and may not function after.RoomAccessQRis used for locks with a QR code scanner. This key type contains data to generate a QR code.
Example query
New Room Access Key types may be added at any time. Your integration must handle cases when roomAccessKey returns a type not queried for.
Given a reservation room's id, to retrieve a Room Access Key for a specific room:
# Variables:
# {
# "roomId": "<room id>"
# }
query RoomAccessKey($roomId:ID!) {
room_stays(filter:{id:{eq:$roomId}} first:1) {
edges {
node {
room_setup { name }
roomAccessKey {
... on RoomAccessPin { pin }
... on RoomAccessKeyStorage { pin }
... on RoomAccessQR { qrData }
}
}
}
}
}
A reservation may contain multiple rooms, and each room may have a different Room Access Key. To retrieve keys for all rooms inside a reservation, when you have the reservation's code (not to be confused with reservation's id):
# Variables:
# {
# "reservationCode": "<reservation code>",
# "roomCursor": null
# }
query ReservationRoomAccessKeys($reservationCode:String! $roomsCursor:String = null) {
reservations(filter:{code:{eq:$reservationCode}}) {
edges {
node {
rooms(first:20 after:$roomsCursor) {
edges {
node {
room_setup { name }
roomAccessKey {
... on RoomAccessPin { pin }
... on RoomAccessKeyStorage { pin }
... on RoomAccessQR { qrData }
}
}
}
pageInfo { hasNextPage endCursor }
}
}
}
}
}
In the response, roomAccessKey.pin can be an alpha-numerical string with length up to 10 characters, or qrData in case of a QR code Room Access Key type. RoomAccessKeyStorage will also contain a compartment field, but that must not be shown to end guests.
A reservation can contain more rooms than can be returned with one request. Make sure to use pageInfo.hasNextPage and pageInfo.endCursor to read subsequent pages.
Room Access Key Changes
Room access key can change at any time before arrival, or during stay. See webhooks guide, if your integration requires the latest room access keys.
Writing Room Access Key
This section is relevant only for door lock or key storage integrations. Writing room access keys only informs 3RPMS of how to access the room. If your integration does not control any keys, these writes wont do anything.
Basic Flow
flowchart TD
A["room_stays()"] --> B["createRoomAccessKey()"]
B --> C["addRoomAccessKey()"]
C --> D["removeRoomAccessKey()"]
D --> E["deleteRoomAccessKey()"]
room_stays()reads relevant Room Stay entries.createRoomAccessKey()creates a room access key. Until added to a room stay, this will not be visible to users or other integrations.addRoomAccessKey()assigns a room access key to a room stay. Recommended to do this at least a day before arrival, so that arrival instructions can be sent to guest beforehand.- Integrations can add only keys it has created with
createRoomAccessKey() - Same key can be assigned to multiple rooms at the same time
- Each room stay can have only one key assigned to it.
- Integrations can add only keys it has created with
removeRoomAccessKey()removes room access key from a room stay.- Integrations can remove only its own keys
deleteRoomAccessKey()deletes a room access key; it has to be removed from any room stays first. 3RPMS will delete keys 2 days after departure (or 2 days after not being assigned to any room).- Integrations can delete only keys its own keys
1. Retrieve Room Stays
Bookings could be made months or years in advance, yet only bookings a few days in advance are relevant. 3RPMS recommends to store data only for the upcoming 7 days, and:
- Do one initial data poll when integration is connected
- Nightly synchronization for new dates
For example, to query for room stays from January 30th + next 6 days:
# Variables
# {
# "start": "2026-01-30",
# "end": "2026-02-05"
# }
query RoomStays($start:Date! $end:Date! $cursor:String=null) {
room_stays(filter:{reservation_from:{le:$end} reservation_to:{gt:$start}} first:20 after:$cursor) {
edges {
node {
...RoomStaySubset
}
}
pageInfo {
endCursor
hasNextPage
}
}
}
fragment RoomStaySubset on RoomStay {
id
room_setup { id }
arrival: reservation_from
departure: reservation_to
# In some cases, a newly created room stay may use an already existing room access key,
# for example, when an existing room stay is split in two separate ones.
roomAccessKey { id }
}
New bookings could be created just before arrival. In addition Room Stay arrival, departure and physical room can change at any time. How to respond to these changes is described in Listen for Room Stay Changes section.
2. Create a Room Access Key
There are three types of room access keys - RoomAccessPin, RoomAccessKeyStorage and RoomAccessQR. Any one of them can be created with the same mutation, by providing different inputs:
mutation CreateRoomAccessKey($input:CreateRoomAccessKeyInput!) {
createRoomAccessKey(input:$input) {
roomAccessKey { id }
}
}
In response, you'll get an id that you need to save, to assign to a room stay and keep track of.
RoomAccessPin type's use case is when a pin unlocks a door directly. This type has one required field:
pinthat guest will use to open the door. Up to 10 alpha-numeric characters. Both uppercase and lowercase is allowed, but not both at the same time.
Variable structure to use for this type, with the previously mentioned mutation:
{
"input": {
"roomAccessPin": {
"pin": "1234567890"
}
}
}
RoomAccessKeyStorage use case is when a pin is used to on arrival get a physical key that is then used for the duration of the stay. This type has two required fields :
pinthat guest will use to access key storage. Up to 10 alpha-numeric characters. Both uppercase and lowercase is allowed, but not both at the same time.compartmentwhere hotel staff will place the key. Usually not relevant for guests. Up to 10 alphanumeric characters.
Variable structure to use for this type, with the previously mentioned mutation:
{
"input": {
"roomAccessKeyStorage": {
"pin": "1234567890",
"compartment": "3R"
}
}
}
RoomAccessQR use case is when a room's door can be opened by scanning a QR code. This type has one required field:
qrDataData being used to generate QR code. Up to 512 characters are allowed.
Variable structure to use for this type, with the previously mentioned mutation:
{
"input": {
"roomAccessQR": {
"qrData": "abcdefgh1234567890"
}
}
}
3. Assign a Room Access Key
A newly created room access key will not be used for anything. For hotel staff and other systems to be aware of a Room Access Key, it has to be assigned to a Room Stay. Assignment is done the same way for all Room Access Key types:
# Variables:
# {
# "addRoomAccessKey": {
# "roomStayId": "<room stay id>",
# "roomAccessKeyId": "<room access key id>"
# }
# }
mutation AddRoomAccessKey($addRoomAccessKey: AddRoomAccessKeyInput!) {
addRoomAccessKey(input:$addRoomAccessKey) {
roomStay {
room_setup { id name }
arrival: reservation_from
departure: reservation_to
roomAccessKey { id }
}
}
}
Room Access Key should be assigned to the room at least a day before arrival, to allow sending out arrival instructions.
4. Listen for Room Stay Changes
New bookings could be created just before arrival, or a reservation could be cancelled last minute. In addition Room Stay arrival, departure and physical room can change at any time. Your integration must respond to these changes to ensure the Room Access Key works when, for example, a Room Stay is moved up by a week.
Scenarios to handle:
- Arrival/Departure change
- Room Stay moved to a different physical room
- Room Stay deleted
- Reservation cancelled or re-activated
- Room Stay created
Most scenarios are applicable only when using door pin, but some are relevant for key storage or other key types as well. 3RPMS recommends setting up a webhook to react to these changes in realtime:
room_stay.created- Add Room Access Keys to last-minute bookings.room_stay.updated- Changes to arrival, departure or physical room may be of interest.room_stay.deleted- When a room stay that had your integration's key assigned, is deleted, your integration may need to remove some access on your side.reservation.updated- When a reservation that had your integration's key assigned, is cancelled, your integration may need to remove some access on your side. Similarly, when a reservation is activated.room_access_key.deleted
See webhooks guide on how to use them.
Upon receiving a webhook, retrieve the data your integration needs:
# Variables
# {
# "ids": ["<room stay id 1>", "<room stay id 2>"]
# }
query RoomStay($ids:[ID!]!) {
room_stays(filter:{id:{in:$ids}}) {
edges {
node {
...RoomStaySubset
}
}
}
}
fragment RoomStaySubset on RoomStay {
id
room_setup { id }
arrival: reservation_from
departure: reservation_to
# In some cases, a newly created room stay may use an already existing room access key,
# for example, when an existing room stay is split in two separate ones.
roomAccessKey { id }
}
5. Revoke a Room Access Key
A Room Stay can have only on Room Access Key assigned. To change pin, or assign a different Room Access Key, the previous one has to be revoked. This is the same for all Room Access Key types:
# Variables
# {
# "removeRoomAccessKey": {
# "roomStayId": "<room stay id>",
# "roomAccessKeyId": "<room access key id >"
# }
# }
mutation RemoveRoomAccessKey($removeRoomAccessKey:RemoveRoomAccessKeyInput!) {
removeRoomAccessKey(input:$removeRoomAccessKey) {
roomStay {
id
roomAccessKey {
id
}
}
}
}
6. Delete a Room Access Key
After departure, or when your integration knows the key is no longer needed, its recommended to delete the room access key. Only Room Access Keys that have not been assigned to any rooms can be deleted.
# Variables
# {
# "roomAccessKeyId": "abc"
# }
mutation DeleteRoomAccessKey($roomAccessKeyId:ID!) {
deleteRoomAccessKey(input:{roomAccessKeyId: $roomAccessKeyId}) {
deleted
}
}
3RPMS will revoke and delete Room Access Keys 2 days after room stay departure, or 2 days of not being assigned to any room. Such Room Access Keys are assumed to be no longer used.
Rate Limits
Handling Room Access Keys updates for many Room Stays at the same time can cause exceeding rate limits. To prevent this, batch your queries in as few requests as possible. For example:
- Request: Query for rooms within the stay period
- Request: Create all Room Access Keys
- Request: Assigning new Room Access keys, Revoking and Deleting old Room Access Keys
To reduce requests even further, 2nd and 3rd request can be combined by interlieving the queries and using Chained Operations: Create, Assign, Create, Assign, Revoke, Revoke, Delete, Delete
External Sales API
The External Sales API provides the ability to integrate your POS system with 3RPMS enabling sending sales' bills to the guest's room. This helps achieve a seamless experience for guests and improves the overall efficiency for sales performed through 3rd party systems.
External Sales API requires read/write permissions
Terminology:
- External Sale - A single sale attached to a room stay.
- External Sales Product - A sale's template. Each External Sale will reference an External Sales Product.
Set up external sales product
Before any External Sale can be added, an External Sales Product must be set up. This will need to be referenced when adding an External Sale. The product's name can be anything, but its recommended to be your integration's name - this name will be visible in reservation's item listing.
# Query variables:
# {
# "createExternalSalesProduct": {
# "name": "Retro-rant"
# }
# }
mutation CreateExternalSalesProduct($createExternalSalesProduct: CreateExternalSalesProductInput!) {
createExternalSalesProduct(input:$createExternalSalesProduct) {
product { id name }
}
}
Your integration should save the response's id to later use in when adding. an External Sale
There is a limit of 1 External Sales Product per integration
Update External Sales Product
The name of an External Sales Product can be changed at any time. This name will be used only for new External Sales created, any existing External Sales will remain unchanged.
# Query variables:
# {
# "updateExternalSalesProduct": {
# "id": "<id>",
# "name": "Retro-raunt"
# }
# }
mutation UpdateExternalSalesProduct($updateExternalSalesProduct: UpdateExternalSalesProductInput!) {
updateExternalSalesProduct(input:$updateExternalSalesProduct) {
product { id name }
}
}
List External Sales Products
In case state from createExternalSalesProduct was not saved or was lost, it can be retrieved with externalSalesProducts query:
query ExternalSalesProducts {
externalSalesProducts {
edges {
node { id name }
}
}
}
Retrieve active room list
Every new External Sale must be attached to a room stay. An example query to retrieve room stays that: (1) have checked in, and (2) have not checked out, and (3) reservation is not cancelled.
query CheckedInRoomStays($cursor: String = null) {
room_stays(first:20 after:$cursor filter:{ not:{ check_in:{ eq:null } } check_out:{ eq:null } }) {
edges {
node {
id
reservation { code }
roomName
first_guest { firstname lastname }
}
}
pageInfo { hasNextPage endCursor }
}
}
External Sales can be attached to any non-cancelled rooms, your integration may use a different filter if needed.
Add External Sale
In addition to external sales product and room stay id, there are 3 requires values - amount, saleCreatedAt, receiptNumber. Quantity or discount cannot be specified - amount is the final total.
# Query variables:
# {
# "createExternalSale": {
# "productId": "<external sales product id>",
# "roomStayId": "<room stay id>",
# "amount": "12.34",
# "saleCreatedAt": "2023-02-20T11:13:00+01:00",
# "receiptNumber": "INVOICE-123"
# }
# }
mutation CreateExternalSale($createExternalSale:CreateExternalSaleInput!) {
createExternalSale(input: $createExternalSale) {
created
}
}
In addition, your integration can also provide optional receiptPdfUrl, waiterName and tableName values.
Cancel External Sale
External Sales cannot be updated or deleted by your integration, nor by hotel staff. Cancellation can be achieved by sending negative amount using the same mutation as above.
Rates & Restrictions API
This guide explains how to read and update category-level rates and restrictions.
Reading Rates and Restrictions
Rates and Restrictions are scoped to a category and a specific date. While category filter can be skipped, a date range for rates and restrictions is mandatory. Rates can be ready by using the Category.defaultPrices field, for example to read rates for all of July 2025:
# Variables
# {
# "dateRange": {
# "start": "2025-07-01",
# "end": "2025-07-31"
# }
# }
query RatesAndRestrictions($dateRange:DateRangeInput!) {
settings {
categories {
edges {
node {
...MyCategoryData
}
}
}
}
}
fragment MyCategoryData on Category {
id
defaultPrices(filter:{date:$dateRange} first:31) {
edges {
node {
date
amount
}
}
}
}
Similarly, restrictions can be read by using Category.restrictions field:
fragment MyCategoryData on Category {
id
restrictions(filter:{date:$dateRange} first:31) {
edges {
node {
date
stopSell
minStay
maxStay
closedToArrival
closedToDeparture
guarantee
cancellation
breakfastIncluded
}
}
}
}
defaultPrices and restrictions fields are *Connection types. Like all *Connection types, they support cursor pagination and are limited to at most 100 results per page. However, as each date appears only once, its more convenient to use multiple date ranges to read more data. instead of cursor arguments.
| Field | Description |
|---|---|
stopSell |
When active, category is not bookable |
minStay |
Minimum Length of Stay. Value is between 1 and 99 (inclusive) |
maxStay |
Maximum Length of Stay. NULL represents a no limit on maximum stay |
closedToArrival |
When active, arriving on this day is not allowed |
closedToDeparture |
When active, departing on this day is not allowed |
guarantee |
When active, a guarantee is required to stay on this date |
cancellation |
The amount of days whilst possible to cancel prior to arrival without penalties. NULL is non cancellable, 0 is a standard policy as defined in booking portals. |
breakfastIncluded |
Whether breakfast is included in price or not. |
Not all restrictions are supported by all Channel Managers. Some hotels might chose to not use all restrictions because of this.
Occupancy-Based Pricing
Some categories might have different rates based on how many people are staying in the room. For example, a standard rate might be set up for 2 people, with a cheaper rate if room is booked by 1 person. If your integration also wants to query occupancy based prices, the query can be augmented to include this data as well:
fragment MyCategoryData on Category {
id
standardOccupancy
defaultPrices(filter:{date:$dateRange} first:31) {
edges {
node {
date
amount
occupancies {
occupancy
amount
}
}
}
}
}
top-level amount refers to rate used by standard occupancy (standardOccupancy field), while occupancies.occupancy field refers to rate used by each occupancy other than standard occupancy.
occupancies field will be empty when a category does not use occupancy based pricing, so if your integration needs these rates, it's safe to include the fields for all categories.
Updating Rates and Restrictions
Updating rates and restrictions requires read/write permissions
Rates can be updated by using the updateCategoryPrices mutation. It requires:
- Category ID
- Date Range (inclusive)
- Amount
# Variables:
# {
# "updates": [
# {category: "<category id>", dateRange: {start: "2025-07-07", end: "2025-07-11"}, amount: "123.45"},
# {category: "<category id>", dateRange: {start: "2025-07-14", end: "2025-07-18"}, amount: "111.99"},
# ],
# }
#
mutation UpdatePrice($updates:[CategoryPriceInput!]!) {
updateCategoryPrices(input:{prices: $updates}) {
__typename
}
}
Similarly, restrictions can be updated by using updateCategoryRestrictions mutation. It requires:
- Category ID
- Date Range (inclusive)
- Changeset - any subset of restriction fields. Non-provided fields will not be updated.
# Variables:
# {
# "restrictions": [
# {
# "category": "<category id>",
# "dateRange": {
# "start": "2025-07-01",
# "end": "2025-07-31"
# },
# "stopSell": true,
# "minStay": 4,
# "maxStay": 5,
# "closedToArrival": true,
# "closedToDeparture": true,
# "guarantee": true,
# "cancellation": 2,
# "breakfastIncluded": true
# }
# ]
# }
mutation UpdateRestrictions($restrictions: [CategoryRestrictionInput!]!) {
updateCategoryRestrictions(input:{restrictions:$restrictions}) { __typename }
}
At most 1000 updates can be sent in one mutation. 1 update is a category and date pair. For example, updating 1 category for July is 31 updates. To update more than 1000 days, multiple requests must be sent.
Occupancy-Based Pricing
Occupancy-based pricing by default is derived from the standard rate. Updating these rates is optional. However, if the default behaviour is not suitable, your integration may still provide a different rate for each occupancy by providing an additional occupancy value:
{
"updates": [
{category: "<category id>", dateRange: {start: "2023-09-13", end: "2023-09-13"}, occupancy: 2, amount: "123.45"},
],
}
To reset back to using a derived rate for an occupancy, the amount value can be NULL:
{
"updates": [
{category: "<category id>", dateRange: {start: "2023-09-13", end: "2023-09-13"}, occupancy: 2, amount: null},
],
}
Avoid sending multiple updates for the same (date,category,?occupancy) tuple in one request. We provide no guarantee on the order in which updates processed.