The Nowistay public REST API lets your own tools, scripts and partners read and update data from your Nowistay account in a secure way. You can list properties, fetch bookings, update direct reservations, create cleaning or check-in missions, manage custom booking fields and pull a feed of activity events. Everything is documented, browsable and testable from a single page at /public/docs.
This guide walks you through two paths: the fastest way to try the API (the interactive docs playground) and the standard OAuth flow you will use when you build your own integration.
external_status) and set their values per reservation.https://api.nowistay.com/public/v1/* require an OAuth2 access token, sent as Authorization: Bearer ....Open https://api.nowistay.com/public/docs in your browser. The page is a full Swagger UI of the API, with a built-in OAuth playground that auto-registers a client for you. No coding needed to test calls.
Cmd+Shift+R on Mac, Ctrl+F5 on Windows)./public/docs, already authenticated.GET /public/v1/properties, click Try it out, then Execute.[Screenshot to add: the /public/docs page with the green "OAuth playground ready" banner at the top and the Authorize button on the right]
From there you can try every endpoint with one click, see the exact JSON Nowistay returns and copy the cURL command for any call. This is the recommended way to explore the API before writing code.
Important. The playground client lives only in your browser. It is fine for testing, but for a real integration you should register your own OAuth client (see below).
The API uses standard OAuth 2.1 with the authorization code flow and PKCE. In one sentence: the user opens a Nowistay page, says yes, you receive a short code, you exchange it for an access token, and you call the API with that token. Tokens expire and can be refreshed without bothering the user again.
The three OAuth endpoints you will use:
/public/oauth/register — POST/public/oauth/authorize — GET (browser redirect)/public/oauth/token — POSTYou can also fetch the OAuth metadata at /.well-known/oauth-authorization-server/public-api, which lists every endpoint and supported feature.
Ask only for the scopes your app actually needs. The fewer scopes you request, the faster the consent screen reads.
properties.read: List properties and their basic info.bookings.read: List bookings and read a booking detail (no guest contact info).bookings.sensitive.read: Adds guest email, phone, address and smart-lock code to the booking detail.bookings.write: Update direct bookings (dates, amounts, guest info).booking_parameters.read: List your custom booking fields.booking_parameters.write: Create custom booking fields and set values on bookings.missions.read: List missions on your properties.missions.write: Create or update missions, assign team members.activity.read: Read the activity feed for a property.team_members.read: List team members and their property assignments.webhooks.read: List your webhook subscriptions and read their config.webhooks.write: Create, update, pause and delete webhook subscriptions.Call POST /public/oauth/register once. Save the client_id (and client_secret if you set token_endpoint_auth_method to something other than none).
curl -X POST https://api.nowistay.com/public/oauth/register \
-H "Content-Type: application/json" \
-d '{
"clientName": "My PMS Integration",
"redirectUris": ["https://my-app.example.com/oauth/callback"],
"grantTypes": ["authorization_code", "refresh_token"],
"responseTypes": ["code"],
"tokenEndpointAuthMethod": "client_secret_post",
"scope": "properties.read bookings.read missions.read"
}'Response (shortened):
{
"client_id": "client_2Yw4Rk9pQp",
"client_secret": "client_secret_example",
"redirect_uris": ["https://my-app.example.com/oauth/callback"],
"scope": "properties.read bookings.read missions.read"
}Tip: if you are building a single-page app or a CLI without a backend, use "tokenEndpointAuthMethod": "none" and rely on PKCE only (no client secret). The Nowistay docs playground does exactly that.
Generate a PKCE pair (code verifier and S256 code challenge) and a random state value. Then redirect the user to:
https://api.nowistay.com/public/oauth/authorize
?response_type=code
&client_id=client_2Yw4Rk9pQp
&redirect_uri=https://my-app.example.com/oauth/callback
&scope=properties.read%20bookings.read%20missions.read
&state=RANDOM_STATE
&code_challenge=BASE64URL_S256_HASH
&code_challenge_method=S256The user lands on the Nowistay consent page, signs in if needed, sees the scopes you requested, and approves or denies. On approval, Nowistay redirects back to your redirect_uri with a one-time code and the same state you sent.
https://my-app.example.com/oauth/callback?code=auth_code_123&state=RANDOM_STATEAlways check that state matches what you sent. If it does not, abort.
curl -X POST https://api.nowistay.com/public/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "code=auth_code_123" \
-d "redirect_uri=https://my-app.example.com/oauth/callback" \
-d "client_id=client_2Yw4Rk9pQp" \
-d "client_secret=client_secret_example" \
-d "code_verifier=ORIGINAL_PKCE_VERIFIER"Response:
{
"access_token": "nis_access_token_example",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "nis_refresh_token_example",
"scope": "properties.read bookings.read missions.read",
"resource": "https://api.nowistay.com/public/v1"
}Store the access_token and the refresh_token securely on your side (server side, encrypted at rest). The access token is valid for expires_in seconds (one hour by default).
Send the access token in the Authorization header.
curl https://api.nowistay.com/public/v1/properties \
-H "Authorization: Bearer nis_access_token_example"Sample response:
{
"data": [
{
"id": 101,
"name": "Lilas Apartment",
"propertyType": "apartment",
"capacity": 4,
"checkInTime": "16:00:00",
"checkOutTime": "11:00:00",
"address": {
"city": "Paris",
"country": "France",
"countryCode": "FR",
"timezone": "Europe/Paris"
}
}
],
"pagination": { "page": 1, "limit": 50, "totalItems": 1, "totalPages": 1 }
}When the API returns 401 token_expired, call the token endpoint again with the refresh token. You do not need to send the user back to the consent page.
curl -X POST https://api.nowistay.com/public/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token" \
-d "refresh_token=nis_refresh_token_example" \
-d "client_id=client_2Yw4Rk9pQp" \
-d "client_secret=client_secret_example"curl "https://api.nowistay.com/public/v1/bookings?propertyId=101&fromDate=2026-07-01&toDate=2026-07-31&status=confirmed" \
-H "Authorization: Bearer nis_access_token_example"Supported filters: propertyId, status, channel, fromDate, toDate. Pagination with limit (max 100) and offset.
Requires the extra bookings.sensitive.read scope. Without it, email, phone and the smart-lock code are omitted.
curl https://api.nowistay.com/public/v1/bookings/9001 \
-H "Authorization: Bearer nis_access_token_example"curl https://api.nowistay.com/public/v1/bookings/9001 \
 -H "Authorization: Bearer nis_access_token_example"
Only direct bookings (channel direct) you created in Nowistay can be edited. Reservations from Airbnb, Booking.com, VRBO and Expedia are read-only here, because the OTA owns them.
curl -X PATCH https://api.nowistay.com/public/v1/bookings/9001 \
-H "Authorization: Bearer nis_access_token_example" \
-H "Content-Type: application/json" \
-d '{
"arrival": "2026-07-14",
"departure": "2026-07-18",
"adults": 2,
"children": 1,
"amount": "620.00",
"currency": "EUR",
"notes": "Guest requested a baby cot.",
"silent": true
}'Set "silent": false if you want Nowistay to send the usual notifications (guest messages, calendar push) on the update. Default is silent.
Custom booking parameters are useful when you want to mirror data from your own system on each Nowistay reservation.
# Create the field once
curl -X POST https://api.nowistay.com/public/v1/booking-parameters \
-H "Authorization: Bearer nis_access_token_example" \
-H "Content-Type: application/json" \
-d '{"key": "external_status", "name": "External status"}'
# Set its value on a booking
curl -X PATCH https://api.nowistay.com/public/v1/bookings/9001 \
-H "Authorization: Bearer nis_access_token_example" \
-H "Content-Type: application/json" \
-d '{"customParameters": {"external_status": "ready"}}'Send null as the value to clear a custom field on a booking.
curl -X POST https://api.nowistay.com/public/v1/missions \
-H "Authorization: Bearer nis_access_token_example" \
-H "Content-Type: application/json" \
-d '{
"propertyId": 101,
"bookingId": 9001,
"missionType": "cleaning",
"title": "Checkout cleaning",
"scheduledAt": "2026-07-18T11:30:00Z",
"timezone": "Europe/Paris",
"assignedTeamMemberId": 301
}'curl https://api.nowistay.com/public/v1/properties/101/activity?limit=20 \
-H "Authorization: Bearer nis_access_token_example"Events are grouped into three categories: booking, mission and automation (guest messages, smart-lock events).
If you do not want to poll the API to detect changes, you can subscribe to webhooks. Nowistay will then call a URL you provide each time a booking is created or updated on the property, and send the same booking data that GET /public/v1/bookings/{id} returns.
Two events are supported today:
booking.created: a new reservation has been recorded on the property.booking.updated: an existing reservation has changed (dates, status, guest info, smart-lock code, etc.).Each webhook is tied to a single property and you can register up to three per property. Deliveries are signed (HMAC-SHA256) so you can verify they really come from Nowistay, retried with exponential backoff on failure, and the endpoint is auto-disabled if it keeps failing for more than 72 hours. Webhook payloads do not contain sensitive guest fields (email, phone, address, smart-lock code). When you need those, call the API with the bookings.sensitive.read scope.
Full setup and signature verification details are in the dedicated article: Public API: receive real-time booking webhooks.
When you hit the limit, the API returns 429 Rate limit exceeded. Back off and retry after a few seconds.
Every error follows the same shape, with a stable code, a human message and a requestId you can include in support requests.
{
"error": {
"code": "validation_error",
"message": "Parameter key must be lowercase snake_case, start with a letter, and be at most 40 characters",
"requestId": "req_01HYZV5R8G3W9Y2M7K4P6Q1N2A"
}
}silent flag on writes. Set to true when you are syncing data in bulk and do not want to trigger guest notifications.requestId in your logs. It makes support requests much faster to resolve./public/openapi.json.