Authentication
OAuth2 (Resource Owner Password Credentials): obtaining and refreshing an access token, its lifetime, and the authorization header for Hostme API requests.
The Hostme API uses OAuth2 with the Resource Owner Password Credentials scheme
(password flow). You obtain a token with the login/password of a service account and attach
it to every request in the Authorization: Bearer <token> header.
Step 1. Obtaining a token
The token is issued by POST /Token in application/x-www-form-urlencoded format:
curl -X POST https://api.hostmeapp.com/Token \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=password" \ -d "username=YOUR_USERNAME" \ -d "password=YOUR_PASSWORD"Partners do not need to pass
client_idorscope—grant_type,usernameandpasswordare enough. (Hostme’s internal Admin Panel additionally sendsclient_id=panel, but partner integrations don’t need it.)
Response:
{ "access_token": "eyJhbGciOiJ...", "token_type": "bearer", "expires_in": 86399}| Field | Description |
|---|---|
access_token | Access token. Attached to every API request. |
token_type | Token type — bearer. |
expires_in | Token lifetime in seconds. |
refresh_tokenis not returned in thePOST /Tokenresponse — onceexpires_inelapses, obtain a new token by repeating the request with the login/password.
Endpoint and environments
| Environment | Token endpoint |
|---|---|
| Production | https://api.hostmeapp.com/Token |
| QA / Test | https://api-qa.hostmeapp.com/Token |
About swagger. The spec declares OAuth2 with
tokenUrl: https://service.hostmeapp.com/tokenand scopeAll— that is the official authorization server. In practice the token is also accepted on the base API domain ({API_BASE}/Token) — this is how the production panel signs in, and this path is recommended for partner integrations. Sendingscopeis optional.
Marketplace app — client_credentials (recommended)
If your integration is published as an app in the “Integrations” section, the password grant above is neither needed nor suitable: the partner does not store the restaurant’s account login and password. Instead, the restaurant connects your app and Hostme issues it a Client ID / Client Secret pair — it is shown in the app settings, and the restaurant copies it into your dashboard.
The pair is issued per connection, that is per restaurant, not one per partner.
However many restaurants have connected the app — that’s how many pairs you have, and a token obtained
with a pair works only with that restaurant: its identifier is baked into the token itself, so there is
no need to pass restaurantId separately when requesting a token.
curl -X POST https://api.hostmeapp.com/api/pos/apps/{appId}/security/oauth/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=client_credentials" \ -d "client_id=YOUR_CLIENT_ID" \ -d "client_secret=YOUR_CLIENT_SECRET"{appId} is your app’s identifier in the Hostme catalog (for example airsales).
The response is the same as for the password grant:
{ "access_token": "eyJhbGciOiJ...", "token_type": "Bearer", "expires_in": 43199}| Environment | Token endpoint |
|---|---|
| Production | https://api.hostmeapp.com/api/pos/apps/{appId}/security/oauth/token |
| QA / Test | https://api-qa.hostmeapp.com/api/pos/apps/{appId}/security/oauth/token |
What to keep in mind
- Do not pass
scope. Permissions come from your app’s manifest, not from the request. Ascopesent along is ignored — includingscope=All, which used to causeinvalid_scope. - The lifetime is shorter than for the password grant and may differ between apps:
for example, for apps with real-time reservation access it is 12 hours (
expires_in: 43199). Readexpires_infrom the response, don’t hardcode a constant. - No
refresh_tokenis issued — come back for a new token with the same pair. - Wrong pair →
401. The secret is compared in constant time, and the response does not distinguish “no such client_id” from “wrong secret”. - When a restaurant disconnects the app, access is revoked instantly: the pair resolver only looks at active connections, so you won’t get a new token, and the one already issued stops working.
- The token comes encrypted (JWE, five parts): you cannot parse it on your side to read the claims — treat it as an opaque string.
How such a token behaves differently
Reservations created with this token are automatically tagged with your app
(vendorKey / vendorName in the reservation contract) and show up in the restaurant’s analytics as
your channel — nothing needs to be set manually. Their source field is web.
Permissions are limited by the manifest: endpoints not covered by the requested permissions respond
with 403, even if the token is valid. For instance, an app with access to reservations and
availability cannot see reservation requests (GET /api/rsv/admin/restaurants/{id}/requests).
Step 2. Using the token
Attach access_token to every request:
curl https://api.hostmeapp.com/api/core/admin/account/me \ -H "Authorization: Bearer eyJhbGciOiJ..."Without a valid token the API returns 401 Unauthorized.
Step 3. Which restaurant is available
GET /api/core/admin/account/me
After obtaining a token, find out which restaurants you have access to, and their restaurantId:
curl https://api.hostmeapp.com/api/core/admin/account/me \ -H "Authorization: Bearer <token>"The response is a UserInfoContract. Key fields:
| Field | Type | Description |
|---|---|---|
id | string | User/account identifier |
email | string | |
fullName | string | Name |
partnerId | string | Partner identifier (if the account is a partner account) |
roles[] | array | Roles by restaurant: restaurantId, restaurantName, name, type |
restaurants[] | array | Available restaurants (see below) |
A restaurants[] element (RestaurantSummaryContract):
| Field | Type | Description |
|---|---|---|
id | integer | restaurantId — use it in /restaurants/{restaurantId}/... paths |
name | string | Name |
address, city, country, countryShort | string | Address |
timeZone | string | Restaurant time zone |
isVerified | boolean | Verified |
isSuspended | boolean | Suspended |
isOnline | boolean | Online reservations are enabled |
role | string | Your role in the restaurant |
Token types and API behavior
The behavior of some endpoints depends on the token type the request is made with:
| Token type | Who it is | How records are created |
|---|---|---|
| Hostme | A host/manager in the Admin Panel | On behalf of the current user |
| Partner | A partner account | On behalf of the current user (host/manager) |
| Vendor | An external app (POS, CRM) | On behalf of the external app; VendorAppId is set automatically |
Example: when creating a reservation (POST .../reservations) with a Vendor token, the reservation
is created on behalf of the external app, and when listing with scope App, VendorAppId is
substituted automatically and a date range is required (no more than 60 days).
See Reservations and Partner reservations for details.
Partner token
POST /api/core/admin/account/partners/token
For partner scenarios the platform supports issuing a partner token via
POST /api/core/admin/account/partners/token (body — PartnerTokenRequest). The exact
request schema is agreed individually when the partner is onboarded.
The exact format of the
PartnerTokenRequestbody and the conditions for issuing a partner token are provided by the Hostme team during partner onboarding — see App registration.
Handling authentication errors
| Code | Cause | What to do |
|---|---|---|
401 | Token is missing, expired or invalid | Obtain a new token; check the Authorization header |
403 | No permission for the restaurant/action | Check the account’s access to restaurantId |
More about errors — in Errors and response codes.