Loopon Public API
The Loopon API is a RESTful JSON API accessible through HTTPS, using OAuth 2 for authorisation with the Client Credentials grant type for authentication. If you are familiar with this type of APIs you should hopefully not find a lot of surprises here.
If you need any help at all, please write to support@loopon.com or chat with us at https://www.loopon.com/
If nothing else is explicitly mentioned, all data formats are as follows:
Type | Format | Example | Documentation |
---|---|---|---|
Content type | JSON | https://en.wikipedia.org/wiki/JSON | |
Text encoding | UTF-8 | https://en.wikipedia.org/wiki/UTF-8 | |
Date (& Time) | ISO 8601 in Zulu time | 2017-11-20T01:42:41Z | https://en.wikipedia.org/wiki/ISO_8601 |
Language | ISO 639-1 | sv for Swedish, nb for Norwegian Bokmål |
https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes |
Notes on request parameters / body parameters:
Through-out the Loopon API, the following convention is followed:
- Parameters which affect the behaviour of a query, such as the difference between listing all units or searching for a specific unit, are given as Request Parameters (i.e. in the query uri)
- Data provided to POST/PUT requests that will update contents in our database, such as the data about a specific stay when registering a guest stay, are provided as Body Parameters in JSON format
Loopon uses the industry standard OAuth 2 for authorization of API requests. In the naming convention of OAuth 2, the Loopon API acts as both a Resource Server and Authorization Server. For authentication we currently only support the Client Credentials
Grant Type.
The relevant endpoints you need to be aware of are:
Endpoint | URL |
---|---|
Token | https://api.loopon.com/oauth2/token |
API Base | https://api.loopon.com/public |
The possible scopes your API client might have access to are:
Scope | Explanation |
---|---|
basic | Basic access to API for a given unit, required to use API at all. |
read_scores | Possibility to read all/private scores of a unit. |
read_answers | Read detailed answers through the ‘get answer documents’ endpoint. |
manage_stays | Ability to register new guest stays and trigger guest communication. |
chat_sessions | Manage real-time chat session, registering callbacks and push messages to guests. |
read_public_reviews | Possibility to read public reviews for a unit, and units registered in competitive sets for unit. |
An example how to receive an access token would be:
$ curl -X POST -d "grant_type=client_credentials" -d "client_id=<CLIENT_ID>" --data-urlencode "client_secret=<SHARED_SECRET>" -d "scope=basic" "https://api.loopon.com/oauth2/token"
Which should return something like:
{"token_type":"Bearer","expires_in":3600,"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImEzMGQ0ZGM4OGY4MDIxY2Q5YWQ1OWE2MTVjZTk5MzM4MWViYmJmMWI3YjUzMjhiMjdhN2E1MTUzM2Y0NTQwOThkOWVkOGNhNjQzYjBkMzQzIn0.eyJhdWQiOiJsb29wb24tZGVtbyIsImp0aSI6ImEzMGQ0ZGM4OGY4MDIxY2Q5YWQ1OWE2MTVjZTk5MzM4MWViYmJmMWI3YjUzMjhiMjdhN2E1MTUzM2Y0NTQwOThkOWVkOGNhNjQzYjBkMzQzIiwiaWF0IjoxNTExMDEzODg2LCJuYmYiOjE1MTEwMTM4ODYsImV4cCI6MTUxMTAxNzQ4Niwic3ViIjoiIiwic2NvcGVzIjpbImJhc2ljIl19.kE9brbD3CQFYBzgGzicMNm3W0O5B61Ku7o1P35_LjyCdM_etGlkBdrXwsbqO7qYAeh4fYXcwp1Am9aqxTCSvLV8HU597BtPYxLneYdK3j33s8HGQjpny66JbEcb9rN0evr9_tSR_T9gBPqfZ1Fmb4a3IleOPv1X2nI1btlwgQEU"}
In order to use this access token, include a HTTP Header in the format Authorization: Bearer ACCESS_TOKEN
. For example:
curl -X GET -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImEzMGQ0ZGM4OGY4MDIxY2Q5YWQ1OWE2MTVjZTk5MzM4MWViYmJmMWI3YjUzMjhiMjdhN2E1MTUzM2Y0NTQwOThkOWVkOGNhNjQzYjBkMzQzIn0.eyJhdWQiOiJsb29wb24tZGVtbyIsImp0aSI6ImEzMGQ0ZGM4OGY4MDIxY2Q5YWQ1OWE2MTVjZTk5MzM4MWViYmJmMWI3YjUzMjhiMjdhN2E1MTUzM2Y0NTQwOThkOWVkOGNhNjQzYjBkMzQzIiwiaWF0IjoxNTExMDEzODg2LCJuYmYiOjE1MTEwMTM4ODYsImV4cCI6MTUxMTAxNzQ4Niwic3ViIjoiIiwic2NvcGVzIjpbImJhc2ljIl19.kE9brbD3CQFYBzgGzicMNm3W0O5B61Ku7o1P35_LjyCdM_etGlkBdrXwsbqO7qYAeh4fYXcwp1Am9aqxTCSvLV8HU597BtPYxLneYdK3j33s8HGQjpny66JbEcb9rN0evr9_tSR_T9gBPqfZ1Fmb4a3IleOPv1X2nI1btlwgQEU" "https://api.loopon.com/public/units"
Which would return all units you are authorized to access:
[{"unitId":1203,"propertyCode":"","name":"Sea Hotels"},{"unitId":1282,"propertyCode":null,"name":"Sea Hotel Gulf of Bothnia"},{"unitId":1204,"propertyCode":"","name":"Sea Hotel Kattegat"},{"unitId":1601,"propertyCode":"","name":"Kattegat Gourmet Restaurant"},{"unitId":1281,"propertyCode":null,"name":"Sea Hotel Skagerrak"}]
Please contact us at support@loopon.com to receive your client id & client secret.
Obtain access token using client credential grant
$ curl -X POST \
-d "grant_type=client_credentials" \
-d "client_id=<CLIENT_ID>" \
--data-urlencode "client_secret=<SHARED_SECRET>" \
-d "scope=basic" \
"https://api.loopon.com/oauth2/token"
{
"token_type":"Bearer",
"expires_in":3600,
"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImEzMGQ0ZGM4OGY4MDIxY2Q5YWQ1OWE2MTVjZTk5MzM4MWViYmJmMWI3YjUzMjhiMjdhN2E1MTUzM2Y0NTQwOThkOWVkOGNhNjQzYjBkMzQzIn0.eyJhdWQiOiJsb29wb24tZGVtbyIsImp0aSI6ImEzMGQ0ZGM4OGY4MDIxY2Q5YWQ1OWE2MTVjZTk5MzM4MWViYmJmMWI3YjUzMjhiMjdhN2E1MTUzM2Y0NTQwOThkOWVkOGNhNjQzYjBkMzQzIiwiaWF0IjoxNTExMDEzODg2LCJuYmYiOjE1MTEwMTM4ODYsImV4cCI6MTUxMTAxNzQ4Niwic3ViIjoiIiwic2NvcGVzIjpbImJhc2ljIl19.kE9brbD3CQFYBzgGzicMNm3W0O5B61Ku7o1P35_LjyCdM_etGlkBdrXwsbqO7qYAeh4fYXcwp1Am9aqxTCSvLV8HU597BtPYxLneYdK3j33s8HGQjpny66JbEcb9rN0evr9_tSR_T9gBPqfZ1Fmb4a3IleOPv1X2nI1btlwgQEU"
}
Specifies which version of the API your application expects to talk to. If the header is not provided at all, it will be assumed that you are talking to the latest stable version of the API. Versions are provided as integers. While we strive to maintain backwards compatibility, we recommend that you always specify an explicit version number that your application talks to.
Access Token as provided by the call to https://api.loopon.com/oauth2/token
Within Loopon we call a hotel, restaurant or cruise ship a Unit
. A single Unit might contain multiple Evaluation
s but usually there is a single Evaluation
which describes the questions asked to the guest at different stages of the guest journey.
{unitId}
Authentication
List all available units that your user has access to.
Request parameters
Search for unit with this specific property code, if specified.
Responses
Body
List all units
$ curl -X GET -H "Authorization: Bearer <token>" "https://api.loopon.com/public/units"
[
{
"unitId":1203,
"propertyCode":"",
"name":"Sea Hotels"
},
{
"unitId":1282,
"propertyCode":"X01",
"name":"Sea Hotel Gulf of Bothnia"
},
{
"unitId":1204,
"propertyCode":"X02",
"name":"Sea Hotel Kattegat"
},
{
"unitId":1601,
"propertyCode":"",
"name":"Kattegat Gourmet Restaurant"
},
{
"unitId":1281,
"propertyCode":"X03",
"name":"Sea Hotel Skagerrak"
}
]
Search for a unit based on property code
$ curl -X GET -H "Authorization: Bearer <token>" "https://api.loopon.com/public/units?propertyCode=X02"
[
{
"unitId":1204,
"propertyCode":"X02",
"name":"Sea Hotel Kattegat"
}
]
{unitId}
Authentication
Retrieve an explicitly identified unit.
Path variables
Unique id of the unit you want to retrieve
Responses
Body
You are not allowed to access the requested unit
Body
Description of a single hotel/restaurant/cruise ship.
Unique identifier for this unit.
Property code of this unit, as identified by the chain of which it is a member. Requires basic
scope for unit in order to be included (i.e. null
for Unit
objects provided in competitive sets)
Human readable name of this unit.
Unique identifier for the default evaluation active on this unit.
The API endpoints relating to Survey
refer to all data and anwers collected by Loopon directly from the guest. This is in contrast to the Online Reputation
endpoints that relate to information about the hotel collected online from public third party source.
{unitId}
/evaluations{unitId}
/evaluationsAuthentication
Retrieve all evaluations of the specified unit.
Path variables
Unique id of the unit for which you want to retrieve its evaluations.
Responses
Body
You are not allowed to access the requested unit
The simple survey operations consist of a few simple API calls that will most often be enough if your intention is to just collect data to a simple dashboard visualising your Loopon Results.
If you have more complex needs, and want to integrate Loopon with your CRM or retrieve complete data to your data warehouse including detailed information per guest, you should look at the Complete Survey Operations section.
{unitId}
/evaluation/scores/{semanticId}
{unitId}
/evaluation/scores/nps{unitId}
/evaluations/{evaluationId}
/scores/{semanticId}
Result of retrieving the score of a specific unit.
Title of the question.
Score ready for presentation, in the same scale as normal reports in Loopon.
Score on scale from 0 to 1.
How many answers from guests were used to calculate the score.
Number of promoters. This property is only returned when retrieving the NPS score.
Number of passives. This property is only returned when retrieving the NPS score.
Number of detractors. This property is only returned when retrieving the NPS score.
{unitId}
/evaluation/scores/{semanticId}
Authentication
Retrieve the score of the specified unit corresponding to the question with the specified semantic id within an optional date range.
If the unit id corresponds to a chain, the returned score is the aggregated score of all the units in the chain.
If no date range is specified, the score within the latest 30 days is returned. Dates refer to the checkout date of the guest.
The score is calculated using only results from guests who have answered the survey sent by e-mail. Specifically, answers provided through iPad-terminals, public web links, or online review sites are ignored and not included in the result.
Path variables
Unique id of the unit for which you want to retrieve the score.
Semantic id of the question for which you want to retrieve the score.
Request parameters
Date from which to calculate the score. Defaults to the current date minus 30 days if the parameter is not specified
Date until which to calculate the score. Defaults to the current date if the parameter is not specified.
Comma-separated list of all sources you want to retrieve data from.
Include data from email (or sms, or other source where Loopon delivered the survey)
Include data provided through public web links or QR codes
Include data manually registered inside Loopon
Include data provided on public review sites
Include all data from above
Comma-separated list of reputation sources to include. If a reputation source is provided the source parameter will be set to source=reputation. By default all reputation sources will be selected if the source parameter includes reputation.
Responses
Body
You are not allowed to access the requested unit
Body
The specified date range is invalid.
Body
The specified semantic id is invalid.
Retrieve the NPS of the latest 30 days from all hotels in the ‘Sea Hotels’ chain
$ curl -X GET -H "Authorization: Bearer <token>" "https://api.loopon.com/public/units/1203/evaluation/scores/0"
{
"title": "How likely is it that you would recommend our hotel?",
"score": 65,
"normalizedScore": 0.8,
"answerCount": 100,
"promoterCount": 70,
"passiveCount": 25,
"detractorCount": 5
}
Retrieve the NPS of Sea Hotel Kattegat for 2017
$ curl -X GET -H "Authorization: Bearer <token>" "https://api.loopon.com/public/units/1204/evaluation/scores/0?startDate=2017-01-01&endDate=2017-12-31"
{
"title": "How likely is it that you would recommend our hotel?",
"score": 65,
"normalizedScore": 0.8,
"answerCount": 1000,
"promoterCount": 700,
"passiveCount": 205,
"detractorCount": 50
}
{unitId}
/evaluation/scores/npsAuthentication
Retrieve the NPS score of the specified unit within an optional date range.
If the unit id corresponds to a chain, the returned score is the aggregated score of all the units in the chain.
If no date range is specified, the score within the latest 30 days is returned. Dates refer to the checkout date of the guest.
The score is calculated using only results from guests who have answered the survey sent by e-mail. Specifically, answers provided through iPad-terminals, public web links, or online review sites are ignored and not included in the result.
Path variables
Unique id of the unit for which you want to retrieve the NPS score
Request parameters
Date from which to calculate the NPS score. Defaults to the current date minus 30 days if the parameter is not specified
Date until which to calculate the NPS score. Defaults to the current date if the parameter is not specified.
Comma-separated list of all sources you want to retrieve data from.
Include data from email (or sms, or other source where Loopon delivered the survey)
Include data provided through public web links or QR codes
Include data manually registered inside Loopon
Include data provided on public review sites
Include all data from above
Comma-separated list of reputation sources to include. If a reputation source is provided the source parameter will be set to source=reputation. By default all reputation sources will be selected if the source parameter includes reputation.
Responses
Body
You are not allowed to access the requested unit
Body
The specified date range is invalid.
Body
Retrieve the NPS of the latest 30 days from all hotels in the ‘Sea Hotels’ chain
$ curl -X GET -H "Authorization: Bearer <token>" "https://api.loopon.com/public/units/1203/evaluation/scores/nps"
{
"title": "How likely is it that you would recommend our hotel?",
"score": 65,
"normalizedScore": 0.8,
"answerCount": 100,
"promoterCount": 70,
"passiveCount": 25,
"detractorCount": 5
}
Retrieve the NPS of Sea Hotel Kattegat for 2017
$ curl -X GET -H "Authorization: Bearer <token>" "https://api.loopon.com/public/units/1204/evaluation/scores/nps?startDate=2017-01-01&endDate=2017-12-31"
{
"title": "How likely is it that you would recommend our hotel?",
"score": 65,
"normalizedScore": 0.8,
"answerCount": 1000,
"promoterCount": 700,
"passiveCount": 205,
"detractorCount": 50
}
{unitId}
/evaluations/{evaluationId}
/scores/{semanticId}
Authentication
Retrieve the score of the specified unit corresponding to the question with the specified semantic id in the specified evaluation within an optional date range.
If the unit id corresponds to a chain, the returned score is the aggregated score of all the units with compatible evaluations in the chain.
If no date range is specified, the score within the latest 30 days is returned. Dates refer to the checkout date of the guest.
The score is calculated using only results from guests who have answered the survey sent by e-mail. Specifically, answers provided through iPad-terminals, public web links, or online review sites are ignored and not included in the result.
Path variables
Unique id of the unit for which you want to retrieve the score.
Unique id of the evaluation for which you want to retrieve the score.
Semantic id of the question for which you want to retrieve the score.
Request parameters
Date from which to calculate the score. Defaults to the current date minus 30 days if the parameter is not specified
Date until which to calculate the score. Defaults to the current date if the parameter is not specified.
Comma-separated list of all sources you want to retrieve data from.
Include data from email (or sms, or other source where Loopon delivered the survey)
Include data provided through public web links or QR codes
Include data manually registered inside Loopon
Include data provided on public review sites
Include all data from above
Comma-separated list of reputation sources to include. If a reputation source is provided the source parameter will be set to source=reputation. By default all reputation sources will be selected if the source parameter includes reputation.
If you are trying to retrieve some basic data (such as NPS score for your property) you most likely want to look at the simplified Simple Survey Operations.
What you find here instead is complete access to all guest data collected by Loopon, including old versions of your survey(s) - at the cost of quite some complexity.
{unitId}
/evaluations/{evaluationId}
/answerDocuments{unitId}
/evaluations/{evaluationId}
/answerDocuments/{answerDocumentId}
{unitId}
/evaluations/{evaluationId}
/questionSet/{questionSetId}
{unitId}
/evaluations/{evaluationId}
/question/{questionId}
At its core, Loopon considers all types of feedback - irregardless of source (e-mail survey, QR-code feedback, 3rd party review site, …) as the same type of data; and this API refers to all these types of guest feedback.
When a guest provides feedback, the entire set of answers are saved in something called an AnswerDocument. Each individual answer to a specific question is exposed through the API as an Answer object.
In order to know what question an answer relates to, there’s the Question object. If the question is of a multiple choice type (radio or checkbox), it will also contain a list of QuestionOption.
So far everything is hopefully completely clear. One detail that complicates the above situation a little however, is that surveys are no static objects - you as a customer might often be asking us to do a change, or introduce changes yourself. You might add questions, remove questions, reformulate questions, etc.
In relation to the above an additional complication is that the AnswerDocument will also contain meta-data provided by your PMS; such as room number, rate plan, checkout date and other details. What this means in practice is that answers to a single AnswerDocument might be provided at different times - meta-data will be provided when we receive data from your PMS, the guest’s actual answers will be provided when the guest answers the survey. Finally, in certain circumstances, a guest’s answers might be modified afterwards if there has been some misunderstanding or the guest changes their mind after you have sorted out an issue the guest might have complained about.
In order to cope with all the above requirements, Loopon keeps track of something called a QuestionSet. This is a container that keeps track of which Questions were included in the survey at a given point in time.
Simply speaking, every time you make a modification to your survey - even if you just change a single question, a completely new QuestionSet is created which defines the complete contents of the survey at that point in time. When you are retreiving Answer objects they will specify which Question they relate to, which in turn is contained by a given QuestionSet. However, since (as explained above) a single AnswerDocument might contain answers provided at different points in time - also a single AnswerDocument can refer to multiple QuestionSets. While this is uncommon, it’s something your implementation must take into consideration.
Finally, how you retrieve your feedback. Within Loopon a single property (a hotel, cruise ship, restaurant, …) is called a Unit. Each Unit might have one or more Evaluations which is the container of the QuestionSets and AnswerDocuments.
There is a single straight-forward way to receive new answers from Loopon, which is to call the Get Answer Objects endpoint. This will automatically expand all relevant information you need (all included QuestionSets, Questions, QuestionOptions, AnswerDocument and Answers).
Note that for all API endpoints documented below as /units/{unitId}/evaluations/{evaluationId}/
you can also use the short-hand version /units/{unitId}/evaluation/
if you only want to operate on the default evaluation rather than explicitly stating which one you want to access. For the majority of Loopon API use cases, this is probably the version you want to use to avoid having to care about the evaluation ids.
{unitId}
/evaluations/{evaluationId}
/answerDocumentsAuthentication
This method retrieves all the answerDocuments that match the given parameters. By default if no parameters are provided we will return all new answers we have received in the last 24 hours.
Note that we will apply an AND rule between all the provided parameters, so for example:
GET /units/1/evaluation/1/answerDocuments?updatedMin=2019-01-01T00:00:00Z&checkoutMin=2019-05-01&checkoutMax=2019-05-02
would return all answers that we have received since 2019-01-01, but only if the checkout date is in the interval 2019-05-01 - 2019-05-02.
Use List Units to find the units you can access as well as their default evaluation id.
If the unitId
and evaluationId
refer to a chain, you will receive answer documents for all the compatible evaluations of the chain.
Note that there is no paging implemented for this call. If you are seeing “timeout” results, rather perform paging on your side by providing a narrower restriction of updatedMin
and updatedMax
.
Path variables
Identification of the property for which you want to retrieve answers
Identification of the evalution for which you want to retrieve answers
Request parameters
Earliest date when guest is allowed to have checked out to have their feedback included in the results.
Latest date when guest is allowed to have checked out to have their feedback included in the results
Only include new answers received since the given timestamp
Only include new answers received before the given timestamp
Comma-separated list of all sources you want to retrieve data from.
Include data from email (or sms, or other source where Loopon delivered the survey)
Include data provided through public web links or QR codes
Include data manually registered inside Loopon
Include data provided on public review sites
Include all data from above
Comma-separated list of reputation sources to include. If a reputation source is provided the source parameter will be set to source=reputation. By default all reputation sources will be selected if the source parameter includes reputation.
Responses
Body
All question sets referred to by answers included in the results
{unitId}
/evaluations/{evaluationId}
/answerDocuments/{answerDocumentId}
Authentication
Retrieve a specific answer document as identified by the given id
Path variables
Identification of the property for which you want to retrieve answers
Identification of the evalution for which you want to retrieve answers
Unique id of the specific answer document you want to retrieve
Responses
Body
{unitId}
/evaluations/{evaluationId}
/question/{questionId}
Authentication
Retrieve the definition of a question identified by a unique id
Path variables
The unique id of the unit from which you want to retrieve a question
The unique id of the evaluation from which you want to retrieve a question
The unique id of the question you want to retrieve
Responses
Body
A QuestionSet describes all the questions that were included in your survey at a specific point in time.
Unique id for this particular set of questions
Serial number for this question set for the evaluation that owns this question set
Timestamp when this particular question set was created
Timestamp when this particular question set was superseded by another question set
Array of sections of questions contained by this question set
Questions in QuestionSets are grouped within multiple “sections”. For example a typical case could be:
Section: To what extent do you agree to the following statements? Question 1: The reception staff did a good job Question 2: The breakfast gave me a good start of the day
Note however that it’s also possible to have sections without a title, so do not depend on it in your implementation.
Unique identifier of this section
Serial number of this section, within the question set
Unique id of the QuestionSet which contains this section
Title of the section
Array of questions contained within this section
Description of a specific question within a question set. Please note as per the explanation in Feedback Introduction that every change to a survey will result in a completely new QuestionSet.
This implies that when retreiving answers through Get Answer Documents you might get multiple guests who have answered for example the question “The breakfast gave me a good start of the day” but their answers refer to different questionId
s even though conceptually it’s the same question.
One way to deal with this on your side, is to look at the semanticId
field in the Question object. This is an id that will remain stable between different instances of the same question, as it refers to “the meaning” of the question.
Unique identifier of this specific question
Unique id of the QuestionSection which contains this question
Unique id of the QuestionSet that contains this question
Local id/serial number of the question within the QuestionSet
Title of the question as shown to the guest in the base language of the survey
Id that defines the “meaning” of the question, can be used to map results between different question sets.
For example:
0 = “How likely is it that you would recommend … to your friends or colleagues?”
1004 = “I am satisfied with the restaurant”
Code (provided by you) which associates this question to a specific department
Code (provided by you) which associates this question to a specific resource, such as a specific room type, upsell offer or activity/service
Identifier which associates this question with a specific facility type
Defines the type of question
Free text field
Multiple choice (where only a single of the options can be picked)
Multiple choice (where zero or more options can be picked)
Answer will be a decimal number
Array of available options if this question is a multiple choice (radio or checkbox) question
The QuestionOption object specifies a single answer option to either a multiple choice question (radio) or a checkbox selection.
Unique id of this answer option, as referred by Answer.answerOption
Serial number of this option within the Question
Title of this option as shown to the guest in the base language of the survey
Normalised score in range [0, 1] if this answer option should count as a rating
NPS segmentat if this answer option relates to “The Ultimate Question”
An AnswerDocument represents a single complete survey answer from a specific guest relating to a specific stay at a specific property.
Unique id that identifies this specific answer document
Unique id of the unit this feedback has been provided for
Unique id of the evaluation this feedback has been provided for
Property code for the unit this feedback has been provided for. (If you have provided us with your own internal property code(s))
The guest’s choice on how long you are allowed to keep personally identifiable information attached to the survey answer
The guest has chosen to respond anonymously
The guest has granted permission to save personally identifiable information for 30 days
The guest has granted permission to save personally identifiable information until further notice
Survey has been created but not answered yet
Survey has been answered and submitted
Survey was delivered by email, but the email bounced so the recipient will not have a chance to respond to it
Survey link has been clicked in mail/sms but no answers submitted
Survey has been created and a request to deliver it was made, but Loopon decided to not send it out due to internal anti-spam measures
Name of the guest
e-mail address of the guest
Reservation number for the stay this feedback relates to
Membership id in loyalty program of the guest
Source from where this feedback was obtained
Feedback obtained from post-stay e-mail/sms survey
Feedback obtained from pre-stay e-mail/sms survey
Feedback obtained from in-stay e-mail/sms survey
Feedback obtained from next-stay e-mail/sms survey
Feedback obtained from public web or QR-code survey
Feedback manually registered from paper survey
Online review retrieved from 3rd party source
If source=reputation
this field will specify which 3rd party site the review was retrieved from. The examples show a few available options, but do note that the list of available sources keep expanding so you should not add a constraint on your side to what possible values are for this field.
Date when the guest checked in to the property, in the local time zone of the property
Date when the guest checked out from the property, in the local time zone of the property
Timestamp when the survey was delievered to the guest
Defines the share of questions (in range [0.00, 1.00]) the guest has responded to, out of the questions that were visible to the guest.
Array of answers & meta data
Timestamp when an answer was last updated in this answer document.
An Answer object specifies a single answer to a single question (or a meta data field).
Only one of answerOption
, answerOptions
, answerText
will be non-null for a single Answer
.
Unique identifier of this particular answer
The question to which this is an answer
Timestamp when this answer was received
If the answer relates to a multiple choice question, this field provides the selected QuestionOption
If the answer related to a checkbox question, this field provides an array of all selected QuestionOptions (or an empty array if nothing was checked)
If the answer relates to a free text field, this field provides the actual answer given
Score in range [0, 1] if this answer represents a score
The API endpoints relating to Online Reputation
to information about the hotel collected online from public third party source, this is in contrast to the Survey
endpoints that refer to all data and anwers collected by Loopon directly from the guest.
{unitId}
/review-questions{unitId}
/reviews{unitId}
/reputation{unitId}
/review-questionsAuthentication
Retrieves the list of possible ReviewQuestions for which a Review can have ReviewRatings.
Path variables
Unique identifier of the unit for which to obtain review questions.
Responses
Body
You are not allowed to access the review questions for the specified unit.
{unitId}
/reviewsAuthentication
Retrieve all reviews for the given unit, in chronological reviewDate
order (most recent review comes last), that have been updated since the given from
date, up to the given to
date. If the to
date is not provided, it defaults to the current date. If the given unitId
refers to a chain of properties, all reviews for all sub-units will be included as well.
Path variables
Unique identifier of the unit for which to obtain reviews.
Request parameters
Include only reviews updated since given date, in format YYYY-MM-DD
. A review is considered updated since given date if either the reviewDate
or managementResponseDate
is later than or equal to the provided date.
Include only reviews updated up to given date, in format YYYY-MM-DD
. A review is considered updated up to given date if both the reviewDate
and managementResponseDate
are earlier or equal to the provided date. If this parameter is not provided it defaults to the current date.
Which page to retrieve. Will default to 0 if none is given. Request will return empty array if page is out of bounds.
Number of reviews to include per page.
Responses
Body
You are not allowed to access reviews for the requested unit.
The since
date has an invalid format.
{unitId}
/reputationAuthentication
Retrieve the online reputation for a specific unit for a given date. If the given unitId
represents a chain of properties, results for all the units within the chain will be returned.
Path variables
Unique identifier of the unit for which to retrieve online reputation.
Request parameters
Date for which to retrieve online reputation, in format YYYY-MM-DD
.
Name of the source from which to retrieve the online reputation. If this parameter is not provided, then the returned score is the average of all sources.
Responses
Body
Unit which this online reputation refers to.
Score/result for this specific unit.
Array of all competitive sets for this specific unit.
Unique identifier of this specific competitive set.
Name of this competitive set.
Array of units (& their results) contained in this competitive set. Might be of length 0 if this unit has no competitive sets.
Description of unit within competitive set.
Online reputation of unit within competitive set.
Representation of a single online review of a property.
Unique identifier of the issue which was created for this review.
Description of the unit for which this review was provided.
Date when review was provided in format YYYY-MM-DD
.
Date when stay review refers to occured in format YYYY-MM-DD
. Note that the resolution of this varies depending on review source, some review sites for example only provide the month in which case this date will be set to the 1st day of that month.
Identifier of the review channel on which this review was provided.
ISO 639-1 representation of language in which this review was provided.
Raw overall rating number as provided on the channel on which this review was provided.
Normalized overall rating on scale [0.0, 1.0] for this review.
True if this is a review it is possible to respond to.
Date in format YYYY-MM-DD
when (latest) management response was provided, or null if no response has been provided.
Description of the travel composition, if provided by the review channel.
A list of ReviewRating associated to this review.
Representation of the rating of a single aspect of a guest review.
The review question to which this rating applies.
The score using the original scale of the source.
This value is not null if the reviewQuestion
's answerType
is number
, null otherwise.
The score on a scale [0…1].
This value is not null if the reviewQuestion
's answerType
is number
, null otherwise.
The text answer given by the guest. This value is not null if the reviewQuestion
's answerType
is text
, null otherwise.
Representation of a reputation question for which there can be information in a Review.
Unique key identifying this question.
Description of this question.
The type of answer this question accepts.
If the answer to the question is a numerical rating.
If the answer to the question is a text from the guest.
If the answer to the question is a text from the list specified in answerValues
.
If the answerType
property is enum
, this property contains a list of valid text answers for this question. Otherwise this property is null.
Representation of a single unit’s online reputation score / result at (the end of) a specific day.
Date in format YYYY-MM-DD
for which this reputation score was calculated.
The rank of this property in the specified review channel source, or the average rank of this property over all the review channels where it has reviews if no channel source is specified. Please note that if no channel source is specified, several properties might have the same rank as it’s an average over multiple review channels. This value can be null
if there is no available data to calculate the rank.
The total number of properties against which this property is being compared in order to calculate the rank
for the specified review channel source, or all review channels if no channel source is specified. This value can be null
if there is no available data to calculate the rank.
The calculated Reputation Score for this specific date on scale [0.0, 10.0]. This value can be null
if there is no available data to calculate the score.
The normalized score for the specified date and source, or the average of all sources if no source is specified. This value can be null
if there is no available data to calculate the score.
A Stay
represents a single stay at a Unit
for a specific Guest
, throughout the entire guest journey.
{unitid}
/stays{unitId}
/stays/{stayId}
/events{unitid}
/staysAuthentication
This operation registers a guest stay at the given unit, and returns a unique identifier for the stay. There are a few things to consider:
- Almost all parameters are optional, except the
status
parameter. However, if no uniquely identifying information is provided such as booking reference or email address, a new anonymous stay will always be returned. - Even though this is a
POST
request which usually implies creation of a new resource, if the provided data (booking reference, email + checkin & checkout date, mobile + checkin & checkout date) matches an existing guest stay, the existing one will be returned. - If the stay existed previously, this call will update relevant fields (room, status, stayCallback)
Also note that this request specifically requires the manage_stays
scope. Additionally you also need the chat_sessions
scope if you want to be allowed to register the callback. Make sure to request it when requesting the access token:
$ curl -X POST -d "grant_type=client_credentials" -d "client_id=<CLIENT_ID>" -d "client_secret=<SHARED_SECRET>" -d "scope=basic manage_stays chat_sessions" "https://api.loopon.com/oauth2/token"
Finally, note that if you do not provide a stayCallback, any previously registered stayCallback will be discarded. There can at one time be max one stayCallback registered per GuestStay & API Client.
Path variables
Unique id of the hotel for which you want to register a stay
Request body
Full name of guest
E-mail address of the guest
ISO 639-1 language representation of the language the guest prefers to speak. If not provided will be defaulted to default language of the hotel.
Mobile phone number of guest in full international format.
Booking reference as provided by the PMS (Possibly shared by multiple guests)
Booking source/channel for this guest’s reservation
Market segment for this guest’s reservation
Rate plan for this guest’s reservation
Loyalty program type for this guest (if multiple loyalty programs available)
Loyalty number/identifier for this guest
Loyalty level for this guest
Date (locally at hotel) when stay was booked, expected in ISO 8601 date format: 2017-11-19
Checkin date (locally at hotel) for guest, expected in ISO 8601 date format: 2017-11-19
Checkout date (locally at hotel) for guest, expected in ISO 8601 date format: 2017-11-19
Room number guest is/will be/was staying in
Room category (code) the guest is/will be/was staying in
Human readable name of room category guest is/will be/was staying in
Current status of this guest’s journey
Guest has not yet checked in to the hotel
Guest is currently at the hotel
Guest has checked out from hotel
Guest is (hopefully) in the process of booking a stay at the hotel
Callback to which you want the Loopon API to POST
updates regarding this stay. All calls will be done with a LooponEvent in JSON format in the body of the request.
Responses
New guest stay was registered
Body
Returned if any required data is missing, or provided data is invalid
Body
Register a stay with all information provided
$ curl -X POST -H "Authorization: Bearer <ACCESS_TOKEN>" --data-binary \
"{ \
\"name\":\"Simon Finne\", \
\"bookingReference\":\"ExampleReference-001\", \
\"email\":\"simon.finne@loopon.com\", \
\"mobile\":\"+46730442480\", \
\"bookingDate\":\"2017-11-01\", \
\"checkinDate\":\"2017-11-18\", \
\"checkoutDate\":\"2017-11-29\", \
\"room\":\"123\", \
\"status\":\"instay\", \
\"stayCallback\": \
{ \
\"url\":\"https:\/\/www.wowsify.com\/api\/stayUpdate\", \
\"auth\":\"basic\", \
\"authToken\":\"ExampleToken\" \
} \
}" \
https://api.loopon.com/public/units/<unitId>/stays
{unitId}
/stays/{stayId}
/eventsAuthentication
Retrieve all events, in chronological order (most recent event comes last), that have occurred for the given stay. Each event will be represented in the form of a LooponEvent.
Paging is implemented by adding the pageId
and pageSize
parameter to the request, where pageId
starts from 0
and pageSize
defines the number of events you want per page. If less than pageSize
events are returned, it means the end of events was reached.
Path variables
Unique id for the hotel which you want to query
Unique id of the stay for which you want to retrieve events
Request parameters
Which page to retrieve. Will default to 0 if none is given. Request will return empty array if page is out of bounds.
Number of events to include per page.
Responses
Body
Representation of a single guest (person + unit). All contact details are nullable and will be null
in the case of an anonymous guest. For anonymous guests a new guestId
will always be generated, so you cannot use a non-anonymous guest’s guestId
from a chat to identify answers to the post-stay survey.
Unique identifier of a specific guest. Important GDPR/Data Privacy note: At the moment Loopon maintains unique and static guest ids & stay ids also for a guest that chooses to respond anonymously. On our side we will delete all PII associated with the guest id/stay id so the ids themselves do not constitute PII.
If you associate ids to a specific guest on your side, it is your responsibility to comply with GDPR & similar privacy laws by wiping/deassociating ids for guests who want to be anonymous.
Full name of guest
E-mail address to guest
Mobile phone number to guest in full international format
Representation of a guest’s specific stay at a specific hotel.
Unique id identifying this specific stay. Important GDPR/Data Privacy note: At the moment Loopon maintains unique and static guest ids & stay ids also for a guest that chooses to respond anonymously. On our side we will delete all PII associated with the guest id/stay id so the ids themselves do not constitute PII.
If you associate ids to a specific guest on your side, it is your responsibility to comply with GDPR & similar privacy laws by wiping/deassociating ids for guests who want to be anonymous.
Unique identifier of the hotel for which this is a stay.
The guest associated with this stay. Can be null for anonymous/un-identified stays.
Guest has not arrived yet
Guest is currently at the hotel
Guest has checked out from hotel
Guest is currently booking a new stay
Booking reference as provided by the PMS
Date when stay was booked given in ISO 8601 format (YYYY-MM-DD)
Arrival date in ISO 8601 format (YYYY-MM-DD)
Departure date in ISO 8601 format (YYYY-MM-DD)
Room number the guest is staying in
Chat Session associated with this stay. Note that this will only be returned if your access token is valid for the chat_sessions
scope.
ISO 639-1 representation of the language the guest prefers to speak
URL which can be passed on to guest to access the current stay at the current status. Note that a new URL will be created for each POST /units/:unitId/stays
. The URL will be valid for 30 days before expiring.
Specification of a callback to which you want the Loopon API to push updates regarding a guest stay.
URL to which LooponEvent’s should be POST
ed.
No authentication/authorization required (IP whitelist of 79.99.7.10
or URL contains secret)
Loopon will add a Authorization: Basic <token>
header to every request
Loopon will add a Authorization: Bearer <token>
header to every request
Authorization token to use with either basic
or bearer
authentication
In order to use the Loopon Chat API, we expect that you maintain your own backend for your own guest application and most likely your own frontend which is the guest application itself.
The picture below gives a quick overview of the different parts involved when using the Chat API:
A typical flow to setup a chat session, after which the guest writes a message to which the hotel responds, would look like this:
Note that for authorisation of the guest app itself, the unique identifier of the chat session is included in the WSS URL and is used as the authentication of the client. You do not need to pass any separate access token or other authentication info from the guest app.
In contrast to some systems, Loopon maintains a single chat session per guest for the entire guest journey rather than task/issue/topic-based chats. The main idea behind this design is simplicity from the guest’s point of view - he or she maintains a single conversation with the hotel, and the responsibility of delegation between tasks and departments rest solely at the hotel.
In order for your guest app to obtain access to the chat session, your backend needs to provide the Loopon API with a number of details about the guest so we can identify the guest and provide you with the associated chat session.
The request needs to be done from your backend rather than from the guest app itself, in order to eliminate the need to maintain shared secrets on the client side.
After having obtained access to the chat session, you just need to pass on the WSS URL to your guest app which can then maintain realtime communication through the WebSocket API.
The steps to obtain access to the chat session are the following:
- Register the Guest Stay Register Guest Stay
- Send chat session from your backend to guest app
- Connect to and maintain the chat session from your guest app Manage Realtime Chat (WebSocket JSON API)
The ChatSession
object describes all the information you need to pass on to your guest app in order to let it participate in a realtime conversation through the Loopon Chat Server WebSocket.
Unique identifier of the chat session
Fully qualified URL to the web socket your guest app needs to connect to. Note that this will always point to a secure WSS URL. Note that this URL will stay constant for the duration of the stay, so you can cache it in your own backend.
Identifier used to reference this chat session from 3rd party integrations such as Facebook Messenger.
From the guest app’s point of view, all communication with Loopon is done through a WebSocket. Messages sent both by the hotel and the guest will be received on the socket, and if the app wants to send messages to the hotel from the guest it is done by writing the message to the socket.
Note that when you register a GuestStay you will be given back a WSS URL in a format similar to:
wss://chat.loopon.com:2424/public/en/2f51f216ea9c411e87ce45059e2168de
While not required for the chat to work, it is highly recommended that you add a unique deviceId
parameter when connecting, such as:
wss://chat.loopon.com:2424/public/en/2f51f216ea9c411e87ce45059e2168de?deviceId=ab637dfeca7abcd
If you are developing under iOS a suitable deviceId
to use would be for example UIDevice.current.identifierForVendor!.uuidString
in Swift or [UIDevice currentDevice].identifierForVendor.UUIDString
for Objective C.
The purpose of the deviceId
is to keep track of which events a specific device has seen, in order for the chat server to know which events to replay on connect event. The deviceId
should not be an id that can be linked to any personally identifiable information.
When you want to deliver a chat message from the guest to the hotel, this is done by writing a LooponEvent in JSON format on the WebSocket to which your guest app is connected.
Note that if you provide any of the read only
fields in your message, they will be ignored.
After you have sent a LooponEvent on the WebSocket, if it was correctly received and processed in the backend it will be sent back to you on the same WebSocket but this time with the appropriate read only
fields filled in (such as created
and id
in case of for example a ChatMessage).
Example:
{
"type":"chatMessage",
"chatMessage":{
"localId":"FB634A49-074E-4A8F-A840-EF66B1D38A3C",'
"contentType":"text\/plain",
"content":"Please be careful when parking my brand new Tesla Roadster!"
}
}
All messages passed by Loopon on both the WebSocket connection as well as to the registered callback URL for the guest application backend will be of type LooponEvent
.
Unique identifier of the chat session this event is associated with.
Date & timestamp when the event was initially created, provided in ISO 8601 format including milliseconds.
Identifier of the type of event, which defines the rest of the content of the LooponEvent.
Either guest or hotel has written a message.
Either guest or hotel is currently composing a message (only sent on WebSocket).
Guest connected to chat server (only sent to backend callback).
Guest disconnected from chat server (only sent to backend callback).
Error report for event previously sent by client; mostly for debugging purposes
The chat message, if the type
is chatMessage
The error message, in case type
was error
The connection message, if type
is guestConnect
The disconnect message, if type
is guestDisconnect
The typing indicator message, if type
is typingIndicator
Describes a chat message written either by the guest or the hotel.
Unique identifier of this specific message. If your application already received this event in the past, you might want to check the updated
timestamp to verify if you need to update your local contents.
Timestamp when counterpart read the message (if written by guest, when did the hotel read it - if written by the hotel, when the guest read it). Provided in ISO 8601 format including milliseconds. If not read yet, it will be null.
Unique ID for this message specified by the interface where the message is generated. Can be used by guest app to keep track of correct delivery of the message.
Which side of the conversation wrote this message?
Message written by hotel staff
Message written by guest
Name of the author of this message
Fully qualified URL to image of the author
Describes the type of content included in this chat message.
Plain text message with no styling.
Rich text formatted with very limited subset of HTML. Loopon ensures whitelist of allowed tags in backend, safe to display as-is.
Image in PNG format. URL will be provided to actual image data.
Image in GIF format. URL will be provided to actual image data.
Image in JPEG format. URL will be provided to actual image data.
Fully qualified URL to content data (valid for image/png
, image/gif
and image/jpeg
)
Content of message (valid for text/plain
and text/html
)
Message was sent using sms
Message was sent using facebook messenger
Message was sent using Loopon’s doorbell (web chat)
Message was sent from app using the public api
Message was sent through e-mail
The TypingIndicator message acts as a notification that someone is currently typing a message. When your guest app is sending a TypingIndicator, you don’t even need to include the typingIndicator
field in the LooponEvent
message. Just keep sending a LooponEvent
of type typingIndicator
at around 5 second intervals, as long as the guest is still typing a message.
When you receive a LooponEvent
of type typingIndicator
you should show a typing indicator, optionally together with the authorName
field to signal who at the hotel is typing a message. You should make the indicator automatically disappear after timeout
seconds, unless you receive a new typingIndicator
LooponEvent
before that in which case you should reset the timeout to the new value - don’t add it!
Number of seconds this typing indicator should max be visible.
Identifier of who is currently typing a message.
Name of the author currently writing a message.
URL to an avatar of the author currently writing a message.