Utiliser l'API REST publique Nowistay

Tous les articles d'aide
>
Utiliser l'API REST publique Nowistay

L’API REST publique Nowistay connecte vos scripts et intégrations partenaires à votre compte. Consultez et modifiez les logements, calendriers, réservations et missions ; gérez les fiches voyageurs, conversations, connaissances privées et livrets d’accueil ; demandez des paiements et remboursements d’annulation ; consultez les options, achats et rapports de votre portefeuille. Les schémas détaillés sont disponibles sur /public/docs et /public/openapi.json.

Ce guide vous montre trois chemins : la façon la plus rapide d'essayer l'API (le playground interactif des docs), la façon la plus simple de faire tourner vos propres scripts (un identifiant API personnel créé dans l'application) et le flux OAuth standard que vous utiliserez pour construire une application destinée à d'autres hôtes.

Ce que vous pouvez faire avec l'API

  • Logements et calendriers : recherchez et consultez vos logements, modifiez leurs paramètres et gérez les disponibilités, tarifs et restrictions de séjour.
  • Réservations et paiements : créez des réservations directes, modifiez les réservations éligibles, demandez un paiement à la création, indiquez un remboursement d’annulation et consultez son statut.
  • Voyageurs : consultez votre base clients, créez des fiches et retrouvez leur historique de séjours.
  • Missions : créez, modifiez et assignez des missions, filtrez les interventions et consultez les détails et rapports de réalisation.
  • Conversations : retrouvez les conversations voyageurs, lisez les messages et envoyez une réponse hôte sur le canal choisi.
  • Connaissances privées : consultez, ajoutez et modifiez les questions-réponses d’un logement ou fusionnez-les dans d’autres logements.
  • Livrets d’accueil : modifiez les introductions et pages multilingues, changez leur position et importez des images.
  • Options (= Upsells /extras) et rapports : consultez le catalogue et les achats liés aux réservations, puis exportez réservations ou missions au format tableau.
  • Outils existants : les paramètres personnalisés, membres d’équipe, journaux d’activité et webhooks restent disponibles.

Avant de commencer

L’accès dépend de la propriété du logement et de son droit d’accès à l’assistant. Une connexion PMS active est requise pour les opérations qui en dépendent, notamment la gestion du calendrier et la création de réservations directes. Les autres ressources ne nécessitent pas une connexion PMS uniquement pour être consultées ou modifiées.

  • Il vous faut un abonnement AI Channel Manager actif sur les propriétés que vous voulez exposer. L'API ne renvoie que les propriétés où cet abonnement est actif.
  • Vous vous connectez en tant qu'hôte ou gestionnaire. L'API n'expose jamais de propriétés qui ne vous appartiennent pas.
  • L'URL de base est https://api.nowistay.com.
  • Tous les endpoints sous /public/v1/* exigent un token OAuth2, envoyé sous la forme Authorization: Bearer ....

La façon la plus rapide : tester depuis le playground des docs

Ouvrez https://api.nowistay.com/public/docs dans votre navigateur. La page est un Swagger UI complet de l'API, avec un playground OAuth intégré qui enregistre automatiquement un client pour vous. Pas besoin de coder pour tester.

  1. Rafraîchissez la page en mode dur (Cmd+Maj+R sur Mac, Ctrl+F5 sur Windows).
  2. Attendez que la petite bannière en haut affiche OAuth playground ready.
  3. Cliquez sur le bouton vert Authorize en haut à droite.
  4. Dans la fenêtre, à la section OAuth2, cliquez sur Authorize.
  5. Vous êtes redirigé vers la page de connexion et de consentement Nowistay.
  6. Connectez-vous avec un compte hôte ou gestionnaire qui dispose de l'abonnement AI Channel Manager.
  7. Vérifiez les autorisations demandées par le playground et approuvez-les.
  8. Vous revenez sur /public/docs, déjà authentifié.
  9. Ouvrez GET /public/v1/properties, cliquez sur Try it out puis sur Execute.
  10. Vos propriétés apparaissent dans la réponse.

À partir de là, vous pouvez essayer chaque endpoint en un clic, voir le JSON exact que Nowistay renvoie et copier la commande cURL correspondante. C'est la façon recommandée d'explorer l'API avant d'écrire du code.

Important. Le client du playground vit uniquement dans votre navigateur. C'est très bien pour tester, mais pour une vraie intégration il faut créer un identifiant API personnel ou enregistrer votre propre client OAuth (les deux sont expliqués plus bas).

Vos propres scripts : créez un identifiant API personnel

Si vous automatisez votre propre compte (un script de reporting, une synchronisation avec votre outil de comptabilité, un petit tableau de bord), vous n'avez pas besoin du flux OAuth complet. Créez un identifiant API personnel dans l'application et échangez-le contre un token en un seul appel. Pas d'écran de consentement, pas de refresh token à gérer.

  1. Dans l'application, ouvrez le menu puis Mon compte > Développeur, ou allez directement sur app.nowistay.com/hub/user/developer.
  2. Cliquez sur Nouvel identifiant.
  3. Donnez-lui un nom clair (par exemple Script de reporting) et cochez uniquement les autorisations nécessaires.
  4. Cliquez sur Créer l'identifiant. La clé secrète s'affiche une seule fois : copiez-la maintenant et rangez-la en lieu sûr. Vous ne pourrez plus la revoir ensuite.

Votre script échange ensuite l'identifiant contre un token d'accès (valable 1 heure) et appelle l'API :

curl -X POST https://api.nowistay.com/public/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "grant_type=client_credentials" \
  --data-urlencode "client_id=YOUR_CLIENT_ID" \
  --data-urlencode "client_secret=YOUR_CLIENT_SECRET"

Utilisez le champ access_token renvoyé comme jeton Bearer. L’exemple GET /public/v1/properties nécessite explicitement properties.read.

Quand le token expire, redemandez-en un de la même façon. Il n'y a pas de refresh token sur ce flux, et c'est voulu : deux lignes de code, rien à surveiller.

Gérer vos identifiants

  • Dernière utilisation indique quand chaque identifiant a appelé l'API pour la dernière fois. C'est le moyen le plus simple de repérer un vieux script qui tourne encore, ou un identifiant que vous pouvez supprimer sans risque.
  • Régénérer la clé génère une nouvelle clé secrète si l'actuelle a pu fuiter, ou dans le cadre d'une rotation périodique. La nouvelle clé s'affiche une seule fois ; l'ancienne reste valable 7 jours pour laisser le temps à votre script de migrer, puis elle s'arrête.
  • Supprimer retire un identifiant que vous n'utilisez plus. Tout ce qui l'utilise encore s'arrête immédiatement.

Vous pouvez créer jusqu'à 10 identifiants, chacun avec son nom et ses autorisations. Un identifiant ne lit et n'écrit que les données accessibles à votre propre compte, et il nécessite le même abonnement AI Channel Manager actif que le reste de l'API.

Comment ajouter des scopes à un token API existant

Les scopes d’un token ou d’un identifiant personnel existant ne sont pas modifiables. Créez un identifiant de remplacement avec toutes les permissions nécessaires à votre intégration. Les clients, tokens d’accès, codes d’autorisation et refresh tokens existants conservent leurs permissions initiales. Aucun scope n’est ajouté automatiquement.

Identifiants personnels : remplacer, tester, puis basculer

  1. Ouvrez Mon compte > Développeur, ou https://app.nowistay.com/hub/user/developer. Relevez les permissions utilisées par votre intégration actuelle.
  2. Cliquez sur Nouvel identifiant. Donnez un nom reconnaissable au remplacement et sélectionnez les permissions existantes encore nécessaires ainsi que les nouveaux scopes. Par exemple, modifier les tarifs du calendrier nécessite calendar.write, envoyer un message nécessite messages.write et demander un paiement nécessite bookings.write plus payments.write.
  3. Cliquez sur Créer l’identifiant et conservez de façon sécurisée l’Identifiant client et la Clé secrète, affichée une seule fois.
  4. Demandez un nouveau token à POST /public/oauth/token avec grant_type=client_credentials et les nouveaux identifiants. Vérifiez le champ scope de la réponse et testez un endpoint autorisé. GET /public/v1/properties nécessite properties.read.
  5. Remplacez les identifiants et le token d’accès en cache dans votre intégration. Vérifiez ses opérations existantes et la nouvelle opération.
  6. Quand tous les consommateurs ont basculé, cliquez sur Supprimer pour l’ancien identifiant. Sa suppression arrête immédiatement les intégrations qui l’utilisent encore.

Régénérer la clé n’ajoute aucun scope. Cette action remplace seulement le secret du même identifiant ; l’ancienne clé reste valable 7 jours. Renouveler un token ne peut pas étendre ses permissions initiales. Un scope d’écriture n’inclut pas automatiquement le scope de lecture.

Applications OAuth partenaires : nouvel enregistrement et consentement

  1. Appelez POST /public/oauth/register avec le jeu complet de scopes souhaité et les informations habituelles de l’application. Un enregistrement équivalent avec exactement les mêmes scopes réutilise le client correspondant ; des scopes différents créent un nouvel enregistrement. Conservez les identifiants renvoyés.
  2. Démarrez un nouveau flux authorization code avec PKCE pour cet enregistrement. Demandez à l’hôte d’approuver les permissions sur la page de consentement, puis échangez le nouveau code contre des tokens.
  3. Après vérification, basculez l’intégration vers ces nouveaux tokens. Les anciens refresh tokens et codes d’autorisation gardent leurs scopes initiaux et ne permettent pas d’en ajouter.

Dans le playground, actualisez la page puis autorisez à nouveau les scopes nécessaires. Les permissions affichent leur identifiant et un libellé lisible en anglais, français, espagnol, allemand, néerlandais et italien.

OAuth, en clair

L'API utilise OAuth 2.1 standard avec le flux authorization code et PKCE. En une phrase : l'utilisateur ouvre une page Nowistay, donne son accord, vous recevez un code court, vous l'échangez contre un token d'accès, puis vous appelez l'API avec ce token. Les tokens expirent et peuvent être renouvelés sans déranger l'utilisateur.

Les trois endpoints OAuth que vous allez utiliser :

  • Enregistrer votre app (une seule fois): /public/oauth/register (POST)
  • Envoyer l'utilisateur donner son accord: /public/oauth/authorize (GET, redirection navigateur)
  • Échanger le code contre des tokens: /public/oauth/token (POST)

Vous pouvez aussi récupérer les métadonnées OAuth à /.well-known/oauth-authorization-server/public-api, qui liste chaque endpoint et toutes les options supportées.

Les nouvelles applications commencent non vérifiées

N'importe qui peut enregistrer un client OAuth. Les hôtes ont donc besoin de distinguer une application toute neuve d'une intégration établie. C'est pourquoi toute application nouvellement enregistrée démarre non vérifiée :

  • L'écran de consentement affiche aux hôtes un avertissement clair indiquant que l'application n'a pas été vérifiée par Nowistay.
  • Une application non vérifiée peut être connectée à 3 comptes hôtes maximum.
  • Les clients enregistrés qui ne réalisent jamais la moindre connexion sont supprimés automatiquement au bout de 30 jours.

Vous construisez quelque chose pour plus de 3 comptes ? Écrivez au support avec le nom de votre application, son site web et votre cas d'usage. Une fois l'application vérifiée, l'avertissement disparaît et la limite de comptes est levée.

Les scopes disponibles

L’API REST prend en charge 25 scopes, dont dix permissions supplémentaires pour les logements, messages, livrets, connaissances, créations de voyageurs, options et paiements. Sélectionnez seulement celles nécessaires à votre intégration.

  • properties.read : consulter les logements et leurs paramètres.
  • properties.write : modifier les logements et leurs paramètres.
  • calendar.read : consulter les disponibilités, tarifs et restrictions.
  • calendar.write : modifier les disponibilités, tarifs et restrictions.
  • bookings.read : consulter les réservations, montants et statuts des demandes de paiement.
  • bookings.sensitive.read : inclure coordonnées, adresses postales et codes d’accès dans les réponses de réservation ; les lectures nécessitent aussi bookings.read.
  • bookings.write : créer et modifier des réservations directes. Les paiements et remboursements nécessitent aussi payments.write.
  • booking_parameters.read : consulter les définitions et valeurs des paramètres personnalisés.
  • booking_parameters.write : créer et modifier les définitions et valeurs des paramètres.
  • missions.read : consulter les missions et rapports de réalisation.
  • missions.write : créer, modifier et assigner les missions.
  • activity.read : consulter les journaux d’activité des logements.
  • team_members.read : consulter les membres d’équipe, coordonnées et logements assignés.
  • webhooks.read : consulter les abonnements webhook.
  • webhooks.write : créer, modifier, suspendre et supprimer des abonnements webhook.
  • customers.read : consulter les fiches voyageurs, coordonnées et historiques de séjour.
  • customers.write : créer des fiches voyageurs.
  • upsells.read : consulter le catalogue d’options et les achats liés aux réservations.
  • payments.write : demander un paiement à la création d’une réservation et indiquer un remboursement total ou partiel à l’annulation ; nécessite aussi bookings.write.
  • messages.read : lire les conversations et messages voyageurs.
  • messages.write : envoyer des messages aux voyageurs.
  • guides.read : consulter les livrets d’accueil et leurs pages.
  • guides.write : modifier les livrets, créer et modifier des pages, changer leur position et importer des images.
  • knowledge.read : consulter les questions-réponses privées des logements.
  • knowledge.write : créer et modifier les connaissances privées. La copie nécessite aussi knowledge.read.

Les huit scopes REST par défaut restent properties.read, calendar.read, bookings.read, booking_parameters.read, missions.read, activity.read, team_members.read et webhooks.read. Ces valeurs par défaut ne modifient pas les tokens existants.

Opérations nécessitant deux scopes

  • Demandes de paiement et remboursements explicites totaux ou partiels : bookings.write + payments.write. Une annulation sans remboursement utilise seulement bookings.write.
  • Historique de réservations d’un voyageur : customers.read + bookings.read.
  • Achats d’options d’une réservation : upsells.read + bookings.read. Le catalogue nécessite seulement upsells.read.
  • Copie des connaissances : knowledge.read + knowledge.write.
  • PATCH mêlant champs principaux et paramètres personnalisés : bookings.write + booking_parameters.write. Un PATCH limité aux paramètres conserve booking_parameters.write et fonctionne aussi sur les réservations OTA.

Les nouveaux endpoints d’écriture renvoient un résultat minimal sauf si le token possède aussi le scope de lecture correspondant. Le masquage existant des données de réservation reste en place. Les missions et conversations incluent leur contexte de réservation sous leurs propres scopes.

Pas à pas : votre propre intégration

Ces exemples utilisent des identifiants et valeurs fictifs à remplacer. Ce flux concerne les applications partenaires ; les identifiants personnels utilisent client_credentials, comme indiqué plus haut.

Étape 1. Enregistrez votre client OAuth

Appelez POST /public/oauth/register avec les permissions nécessaires. Conservez le client_id et le client_secret renvoyés de manière sécurisée. Cet exemple demande la lecture des logements, réservations et missions.

curl -X POST https://api.nowistay.com/public/oauth/register \
  -H "Content-Type: application/json" \
  -d '{"clientName":"My 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"}'

Pour un client public sans backend, utilisez "tokenEndpointAuthMethod": "none" et PKCE, sans secret client.

Étape 2. Envoyez l’utilisateur sur l’URL d’autorisation

Générez un vérificateur PKCE, son challenge S256 et un state aléatoire. Redirigez l’utilisateur vers l’endpoint d’autorisation. L’URL ci-dessous est sur une seule ligne ; encodez les valeurs des paramètres.

https://api.nowistay.com/public/oauth/authorize?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=https%3A%2F%2Fmy-app.example.com%2Foauth%2Fcallback&scope=properties.read%20bookings.read%20missions.read&state=RANDOM_STATE&code_challenge=BASE64URL_S256_HASH&code_challenge_method=S256

L’hôte se connecte, examine les permissions et accepte ou refuse l’accès. Après acceptation, le callback contient un code à usage unique et votre state. Rejetez un callback dont le state ne correspond pas.

https://my-app.example.com/oauth/callback?code=AUTHORIZATION_CODE&state=RANDOM_STATE

Étape 3. Échangez le code contre des tokens

curl -X POST https://api.nowistay.com/public/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "grant_type=authorization_code" \
  --data-urlencode "code=AUTHORIZATION_CODE" \
  --data-urlencode "redirect_uri=https://my-app.example.com/oauth/callback" \
  --data-urlencode "client_id=YOUR_CLIENT_ID" \
  --data-urlencode "client_secret=YOUR_CLIENT_SECRET" \
  --data-urlencode "code_verifier=ORIGINAL_PKCE_VERIFIER"

La réponse contient access_token, token_type, expires_in, refresh_token et scope. Conservez les tokens de façon sécurisée. Un token d’accès dure normalement une heure ; utilisez l’expiration renvoyée.

Étape 4. Appelez l’API

Cet exemple nécessite properties.read. Consultez les champs data et pagination dans la réponse.

curl "https://api.nowistay.com/public/v1/properties" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Étape 5. Renouvelez le token quand il expire

curl -X POST https://api.nowistay.com/public/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "grant_type=refresh_token" \
  --data-urlencode "refresh_token=YOUR_REFRESH_TOKEN" \
  --data-urlencode "client_id=YOUR_CLIENT_ID" \
  --data-urlencode "client_secret=YOUR_CLIENT_SECRET"

Conservez le nouveau refresh token renvoyé par la réponse. Si le renouvellement échoue, recommencez l’autorisation. Le renouvellement conserve les scopes déjà accordés. Les identifiants personnels n’ont pas de refresh token : demandez un nouveau token d’accès avec client_credentials.

Exemples par endpoint

Lire les disponibilités, tarifs et restrictions de séjour

GET /public/v1/properties/{property_id}/calendar nécessite calendar.read. Indiquez les dates inclusives from et to ; une requête peut couvrir jusqu'à 730 jours.

GET /public/v1/properties/101/calendar?from=2026-12-18&to=2026-12-20

Chaque journée peut renvoyer availability, rate, minStay, maxStay, stopSell, closedToArrival, closedToDeparture, restricted et restrictionReasons. availability correspond au stock encore vendable après déduction des réservations confirmées. Les champs de restriction expliquent pourquoi une date, une arrivée ou un départ est restreint.

Modifier le calendrier d'une propriété

PATCH /public/v1/properties/{property_id}/calendar nécessite calendar.write. La requête est partielle : envoyez uniquement les paramètres à modifier ; les champs omis restent inchangés.

{
 "from": "2026-12-18",
 "to": "2026-12-31",
 "availability": 1,
 "rate": 145.0,
 "minStay": 2,
 "maxStay": 14,
 "stopSell": false,
 "closedToArrival": false,
 "closedToDeparture": false,
 "daysOfWeek": [0, 1, 2, 3, 4]
}

daysOfWeek est facultatif : 0 correspond au lundi et 6 au dimanche. Omettez-le pour modifier toutes les dates de la plage. La réponse indique daysUpdated ainsi que la mise à jour normalisée qui a été appliquée. Les changements sont synchronisés via le PMS Nowistay.

Les opérations de calendrier nécessitent la propriété du logement, le droit d’accès à l’assistant et une gestion de calendrier Nowistay active. Pour ajouter les scopes de calendrier à une intégration, suivez la section Comment ajouter des scopes à un token API existant. Un renouvellement de token ou une régénération du secret ne suffit pas.

Lister les réservations d'une propriété sur une plage de dates

Les filtres comprennent propertyId, plusieurs propertyIds, status, channel, fromDate et toDate. La pagination JSON standard utilise limit (maximum 100) et offset. Les dates conservent la logique de chevauchement : departure >= fromDate et arrival <= toDate.

curl "https://api.nowistay.com/public/v1/bookings?propertyId=101&fromDate=2026-12-01&toDate=2026-12-31&status=confirmed" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Lire le détail d'une réservation avec les contacts voyageur

curl "https://api.nowistay.com/public/v1/bookings/9001" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Nécessite le scope supplémentaire bookings.sensitive.read. Sans lui, email, phone et le code de serrure ne sont pas inclus.

Mettre à jour une réservation directe

Les modifications des champs principaux nécessitent bookings.write et une réservation directe éligible. Les réservations OTA restent en lecture seule pour ces champs. Un PATCH limité à customParameters fonctionne aussi sur les réservations OTA et nécessite booking_parameters.write.

curl -X PATCH "https://api.nowistay.com/public/v1/bookings/9001" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "notes": "Guest requested a baby cot.",
  "reservationNumber": "DIRECT-9001",
  "silent": true
}'

Les PATCH de réservation existants utilisent silent=true par défaut. Mettez silent=false pour déclencher les notifications habituelles. À la création, la valeur par défaut est silent=false. Les champs omis restent inchangés et les valeurs null déjà prises en charge permettent toujours d’effacer des champs. La synchronisation des disponibilités reste intégrée à l’opération.

Ajouter un champ personnalisé à vos réservations

Les paramètres personnalisés de réservation sont utiles pour refléter sur chaque réservation Nowistay une donnée qui vient de votre propre système.

curl -X POST "https://api.nowistay.com/public/v1/booking-parameters" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "key": "external_status",
  "name": "External status"
}'

curl -X PATCH "https://api.nowistay.com/public/v1/bookings/9001" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "customParameters": {
    "external_status": "ready"
  }
}'

Les deux exemples nécessitent booking_parameters.write. Une modification mêlant champs principaux et paramètres nécessite les deux scopes d’écriture ; un échec de validation ne modifie ni les uns ni les autres.

Envoyez null comme valeur pour effacer un champ personnalisé sur une réservation.

Créer une mission de ménage et l'assigner à un membre d'équipe

Nécessite missions.write. Les workflows d’assignation et de notification s’appliquent aussi aux missions créées par l’API.

curl -X POST "https://api.nowistay.com/public/v1/missions" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "propertyId": 101,
  "bookingId": 9001,
  "missionType": "cleaning",
  "title": "Checkout cleaning",
  "scheduledAt": "2026-12-18T11:30:00Z",
  "timezone": "Europe/Paris",
  "assignedTeamMemberId": 301
}'

Lire le fil d'activité d'une propriété

Nécessite activity.read.

curl "https://api.nowistay.com/public/v1/properties/101/activity?limit=20" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Les événements sont regroupés en trois catégories : booking, mission et automation (messages voyageur, événements de serrure).

Endpoints supplémentaires et exemples

Les chemins de cette section sont relatifs à /public/v1. Remplacez les identifiants par des ressources appartenant au compte connecté. Le schéma OpenAPI détaille les champs, valeurs autorisées et formats de réponse.

Détails et paramètres des logements

Utilisez GET /properties/{id} avec properties.read et PATCH /properties/{id} avec properties.write. La liste accepte search. Les modifications prises en charge comprennent nom, adresse, capacité, type, liens de location, couverture, heures d’arrivée/départ, délais de réservation et paramètres de tarif et de séjour par défaut.

curl -X PATCH "https://api.nowistay.com/public/v1/properties/101" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "capacity": 4,
  "defaultMinStay": 2,
  "cutOffDays": 1
}'

Créer une réservation et demander un paiement

POST /bookings nécessite bookings.write, le droit d’accès à l’assistant et une connexion PMS active. Indiquez les dates, l’adresse email et les informations du voyageur, ainsi que les champs tarifaires et opérationnels souhaités. requestPayment=true nécessite aussi payments.write et crée la réservation en attente de paiement.

curl -X POST "https://api.nowistay.com/public/v1/bookings" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "propertyId": 101,
  "arrival": "2026-12-18",
  "departure": "2026-12-20",
  "guestFirstName": "Maya",
  "guestLastName": "Martin",
  "guestEmail": "maya@example.com",
  "adults": 2,
  "amount": "290.00",
  "currency": "EUR",
  "requestPayment": true
}'

La réservation peut être créée même si la génération du lien de paiement échoue. La réponse contient toujours la réservation enregistrée ou son identifiant, avec paymentRequest.sent=false et une erreur explicite. Vérifiez ce résultat avant de réessayer pour éviter une réservation en double. Une URL de paiement est renvoyée uniquement par une action de demande de paiement autorisée.

GET /bookings/{id}/payment-request, avec bookings.read, permet de consulter le statut de la réservation et de sa demande de paiement sans exposer l’URL de paiement. Cette version n’ajoute pas d’API autonome d’administration des paiements.

Remboursements et restrictions liées aux paiements

Le PATCH de réservation accepte désormais reservationNumber, cancellationRefundMode (none, full ou partial) et cancellationRefundAmount. Un remboursement explicite total ou partiel à l’annulation nécessite bookings.write et payments.write. Une annulation sans remboursement nécessite seulement bookings.write.

curl -X PATCH "https://api.nowistay.com/public/v1/bookings/9001" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "status": "cancelled",
  "cancellationRefundMode": "partial",
  "cancellationRefundAmount": "50.00"
}'

Une réservation annulée ne prouve pas qu’un remboursement a abouti. Consultez le statut du paiement avant de confirmer le remboursement au voyageur. payments.write autorise les demandes de paiement et remboursements d’annulation, mais pas la modification du prix d’un paiement déjà créé.

  • Dès qu’un ordre de paiement existe, les changements de dates, montant total, tarif par nuit, ménage, taxe ou devise sont refusés. Cela inclut les anciens ordres échoués ou expirés. Les valeurs tarifaires identiques déjà acceptées restent autorisées.
  • Une réservation confirmée et payée ne peut pas repasser en attente. Une réservation annulée et remboursée ne peut pas être réactivée.
  • Les coordonnées, adresses postales, notes et modifications prises en charge des heures d’arrivée/départ restent autorisées.
  • Les modifications concurrentes et la création d’une demande de paiement utilisent un état cohérent de la réservation : le paiement utilise une modification déjà validée, ou la modification incompatible est refusée après création de l’ordre.

Fiches voyageurs et historique de séjours

POST /guests utilise customers.write ; une création en double renvoie 409. Les lectures existantes de la base clients du propriétaire restent disponibles. La liste accepte propertyId, plusieurs propertyIds et orderBy avec created_at, updated_at, full_name, bookings_count, total_amount ou last_stay. Préfixez la valeur par - pour un ordre décroissant ; la valeur par défaut est -updated_at.

curl -X POST "https://api.nowistay.com/public/v1/guests" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "firstName": "Maya",
  "lastName": "Martin",
  "email": "maya@example.com",
  "language": "en"
}'

GET /guests/{id}/bookings nécessite customers.read et bookings.read et renvoie l’historique des séjours éligibles.

Détails, rapports et filtres des missions

GET /missions/{id} et GET /missions/{id}/report utilisent missions.read. Une mission valide sans rapport de réalisation renvoie 200 avec report: null. La liste accepte les dates UTC planifiées inclusives fromDate/toDate, plusieurs propertyIds et teamMemberIds, en complément des filtres existants.

curl "https://api.nowistay.com/public/v1/missions?propertyIds=101&propertyIds=102&teamMemberIds=301&fromDate=2026-12-01&toDate=2026-12-31" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Lire les conversations et envoyer une réponse hôte

GET /conversations accepte les filtres propertyId et bookingId. GET /conversations/{id}/messages lit l’historique. Ces deux lectures nécessitent messages.read. Pour envoyer une réponse, utilisez POST /messages avec messages.write et exactement un champ parmi conversationId et bookingId. Les champs facultatifs channelKind ou threadChannelId sélectionnent un canal ; n’indiquez pas les deux.

curl -X POST "https://api.nowistay.com/public/v1/messages" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "bookingId": 9001,
  "message": "Hello Maya, your arrival instructions are ready."
}'

Les messages suivent le circuit habituel des réponses hôtes, y compris le routage des réservations multi-logements. Un message enregistré dont la livraison échoue renvoie son identifiant et son statut : vérifiez le résultat avant de réessayer. Ces endpoints ne prennent pas en charge les pièces jointes.

Questions-réponses privées des logements

Utilisez GET /properties/{id}/knowledge-questions et GET /properties/{id}/knowledge-questions/{questionId} avec knowledge.read. Utilisez POST sur la collection et PATCH sur une question avec knowledge.write.

curl -X POST "https://api.nowistay.com/public/v1/properties/101/knowledge-questions" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "question": "Where is the recycling area?",
  "answer": "In the courtyard, next to the bicycle shelter."
}'

POST /properties/{id}/knowledge-questions/copy utilise {id} comme source et nécessite knowledge.read plus knowledge.write. Indiquez targetPropertyIds pour fusionner les réponses dans d’autres logements. La copie ne supprime ni n’écrase les réponses des cibles ; toutes les cibles sont validées avant le début des écritures.

curl -X POST "https://api.nowistay.com/public/v1/properties/101/knowledge-questions/copy" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "targetPropertyIds": [
    102,
    103
  ]
}'

Livrets multilingues, pages et images

  • GET/PATCH /properties/{id}/welcome-guide : consulter ou modifier le livret, notamment ses introductions multilingues.
  • GET/POST /properties/{id}/welcome-guide/pages : lister ou créer des pages.
  • GET/PATCH /properties/{id}/welcome-guide/pages/{pageId} : consulter ou modifier une page et sa position.
  • POST /properties/{id}/welcome-guide/images : importer une image depuis une URL publique prise en charge ou des données base64.

Les lectures nécessitent guides.read et les écritures guides.write. Conservez les blocs EditorJS content/contents, leurs identifiants, la mise en forme imbriquée et les médias lors de la modification des pages structurées. Les langues omises restent inchangées. Le remplacement d’une page structurée par du texte brut est refusé. Les tokens de livret et liens d’accès voyageurs ne sont pas renvoyés.

curl -X PATCH "https://api.nowistay.com/public/v1/properties/101/welcome-guide" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "intros": {
    "en": "Welcome! Your practical information is below.",
    "fr": "Bienvenue ! Retrouvez les informations pratiques ci-dessous."
  }
}'

Les téléchargements d’images doivent utiliser des adresses publiques. Les redirections sont vérifiées et des limites de taille s’appliquent. Consultez le schéma de la requête d’image pour les champs URL/base64 et limites pris en charge.

Consulter les options et achats

GET /upsells nécessite upsells.read. GET /bookings/{id}/upsell-purchases nécessite upsells.read et bookings.read. Ces endpoints sont en lecture seule.

curl "https://api.nowistay.com/public/v1/bookings/9001/upsell-purchases" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Exporter les réservations et missions en tableaux

Ajoutez responseFormat=table aux listes de réservations ou de missions pour obtenir columns, rows et pagination, avec limit jusqu’à 500. Le JSON standard reste le format par défaut, limité à 100. Utilisez offset pour récupérer les pages suivantes. Si propertyIds et propertyId sont fournis ensemble, leurs filtres sont intersectés.

curl "https://api.nowistay.com/public/v1/bookings?propertyIds=101&propertyIds=102&fromDate=2026-12-01&toDate=2026-12-31&responseFormat=table&limit=500&offset=0" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Les tableaux de réservations utilisent bookings.read et ceux de missions missions.read. Les assignations de ménage dans un rapport de réservations nécessitent aussi missions.read, et la présence d’une conversation nécessite messages.read. Les dates de réservation gardent leur logique de chevauchement ; les dates de mission correspondent à leur planification UTC.

Compatibilité avec les intégrations existantes

Les routes REST existantes, valeurs par défaut JSON, pagination, masquage des données, effacement par null pris en charge et réglages de notification restent disponibles. Les restrictions liées aux paiements décrites plus haut sont désormais appliquées. REST et MCP partagent les opérations sous-jacentes et leurs effets, tandis que les contrats des outils MCP et leur fonctionnement de prévisualisation/confirmation restent inchangés.

Comment structurer les blocs d’une page de livret

Une page Nowistay contient un document EditorJS par langue. Le tableau blocks définit l’ordre d’affichage. Chaque objet de ce tableau représente un bloc ; les exemples ci-dessous sont à placer dans content.blocks ou dans contents.<langue>.blocks. Les valeurs d’exemple sont fictives et doivent être adaptées à votre logement.

Structure commune : id est une chaîne stable et unique dans le document ; conservez les identifiants existants et créez-en un nouveau pour chaque nouveau bloc. type est le nom exact du bloc, sensible à la casse. data est un objet dont les champs dépendent du type. Conservez aussi les éventuels tunes et métadonnées du document renvoyés par la lecture, sans les reconstruire inutilement.

Requête de création : POST /public/v1/properties/{id}/welcome-guide/pages avec guides.write. Utilisez locale + title + content pour une seule langue, ou titles + contents pour plusieurs langues. icon vaut info par défaut. position accepte start, end ou after_page ; ce dernier exige afterPageId. Voici un corps de requête complet en anglais et français :

{
  "titles": {
    "en": "Arrival",
    "fr": "Arrivée"
  },
  "contents": {
    "en": {
      "blocks": [
        {
          "id": "arrival001",
          "type": "header",
          "data": {
            "text": "Getting here",
            "level": 2
          }
        },
        {
          "id": "arrival002",
          "type": "paragraph",
          "data": {
            "text": "Use the <b>main entrance</b>."
          }
        }
      ]
    },
    "fr": {
      "blocks": [
        {
          "id": "arrival001",
          "type": "header",
          "data": {
            "text": "Venir au logement",
            "level": 2
          }
        },
        {
          "id": "arrival002",
          "type": "paragraph",
          "data": {
            "text": "Utilisez l’<b>entrée principale</b>."
          }
        }
      ]
    }
  },
  "position": "end"
}

Titres (header)

data.text contient le titre de section. Utilisez data.level: 2, comme dans l’éditeur du livret. Le titre de la page se place dans title ou titles, en dehors des blocs.

{
  "id": "guide00001",
  "type": "header",
  "data": {
    "text": "Arrival / Arrivée",
    "level": 2
  }
}

Paragraphes et mise en forme (paragraph)

data.text est une chaîne. Les balises <b>, <i> et <a href="https://…"> permettent le gras, l’italique et les liens. Échappez les guillemets dans les chaînes JSON. N’envoyez pas une page HTML complète ou du Markdown dans content.

{
  "id": "guide00002",
  "type": "paragraph",
  "data": {
    "text": "<b>Wi-Fi</b>: GuestNetwork. <i>Welcome / Bienvenue !</i> <a href=\"https://example.com/\">Information</a>"
  }
}

Listes à puces et sous-listes (list)

Utilisez style: unordered. items contient des objets, pas des chaînes. Chaque élément possède content (texte mis en forme), meta (objet) et items (sous-éléments, tableau vide au dernier niveau). Répétez cette structure pour les sous-listes.

{
  "id": "guide00003",
  "type": "list",
  "data": {
    "style": "unordered",
    "meta": {},
    "items": [
      {
        "content": "Kitchen / Cuisine",
        "meta": {},
        "items": [
          {
            "content": "Coffee machine / Machine à café",
            "meta": {},
            "items": []
          }
        ]
      }
    ]
  }
}

Listes numérotées (list)

Utilisez style: ordered et meta.counterType: numeric à la racine. Conservez la même structure d’éléments et de sous-listes que pour une liste à puces.

{
  "id": "guide00004",
  "type": "list",
  "data": {
    "style": "ordered",
    "meta": {
      "counterType": "numeric"
    },
    "items": [
      {
        "content": "Close the windows / Fermez les fenêtres",
        "meta": {},
        "items": []
      },
      {
        "content": "Return the keys / Rendez les clés",
        "meta": {},
        "items": []
      }
    ]
  }
}

Listes à cocher (list)

Utilisez style: checklist. L’état initial de chaque case est le booléen meta.checked de l’élément. Il s’agit du contenu du livret, pas d’un rapport de mission.

{
  "id": "guide00005",
  "type": "list",
  "data": {
    "style": "checklist",
    "meta": {},
    "items": [
      {
        "content": "Windows closed / Fenêtres fermées",
        "meta": {
          "checked": false
        },
        "items": []
      }
    ]
  }
}

Citations et encadrés (quote)

data.text contient la citation, caption son attribution (ou une chaîne vide), et alignment le réglage d’alignement de l’éditeur. Utilisez left ; la vue voyageur applique sa propre présentation.

{
  "id": "guide00006",
  "type": "quote",
  "data": {
    "text": "Please respect the neighbours / Merci de respecter le voisinage.",
    "caption": "Your host / Votre hôte",
    "alignment": "left"
  }
}

Séparateurs (delimiter)

Un séparateur visuel possède un objet data vide. Sa position dans blocks détermine où il apparaît.

{
  "id": "guide00007",
  "type": "delimiter",
  "data": {}
}

Tableaux (table)

data.content est un tableau de lignes ; chaque ligne est un tableau de chaînes représentant les cellules. Gardez le même nombre de cellules par ligne. Avec withHeadings: true, la première ligne contient les en-têtes. Utilisez false si toutes les lignes contiennent des données ordinaires.

{
  "id": "guide00008",
  "type": "table",
  "data": {
    "withHeadings": true,
    "content": [
      [
        "Service",
        "Time / Horaire"
      ],
      [
        "Check-in / Arrivée",
        "16:00"
      ],
      [
        "Check-out / Départ",
        "10:00"
      ]
    ]
  }
}

Images (image)

data.file.url est l’URL de l’image importée, pas un nom de fichier local ni une chaîne base64. caption est une chaîne ; withBorder, withBackground et stretched sont des booléens. Importez d’abord l’image via POST /public/v1/properties/{id}/welcome-guide/images, puis insérez le editorJsImageBlock renvoyé dans content.blocks. Conservez ses champs et un identifiant de bloc unique. Remplacez l’URL illustrative ci-dessous par l’URL réellement renvoyée.

{
  "id": "guide00009",
  "type": "image",
  "data": {
    "file": {
      "url": "https://images.example.com/replace-with-uploaded-image.jpg"
    },
    "caption": "Entrance / Entrée",
    "withBorder": false,
    "withBackground": false,
    "stretched": false
  }
}

Vidéos YouTube et Shorts (video)

Utilisez service: youtube pour une vidéo classique ou youtubeShort pour un Short. source est l’URL d’origine ; embed est l’URL YouTube d’intégration correspondante. width et height sont des nombres, caption une chaîne. L’éditeur utilise 580 × 320 pour une vidéo classique et 315 × 560 pour un Short. Remplacez VIDEO_ID_11 dans les deux URL par le même identifiant réel de 11 caractères ; pour un Short, le chemin source est /shorts/VIDEO_ID_11. Du HTML iframe libre n’est pas un format de bloc.

{
  "id": "guide00010",
  "type": "video",
  "data": {
    "service": "youtube",
    "source": "https://www.youtube.com/watch?v=VIDEO_ID_11",
    "embed": "https://www.youtube.com/embed/VIDEO_ID_11",
    "width": 580,
    "height": 320,
    "caption": "Arrival instructions / Instructions d’arrivée"
  }
}

Blocs embed existants (embed)

embed utilise les mêmes champs data que video. Conservez le type existant lors d’une modification ; les deux affichent YouTube et YouTube Shorts. Ne supposez pas que tous les fournisseurs d’intégration d’EditorJS sont pris en charge par Nowistay.

{
  "id": "guide00011",
  "type": "embed",
  "data": {
    "service": "youtubeShort",
    "source": "https://www.youtube.com/shorts/VIDEO_ID_11",
    "embed": "https://www.youtube.com/embed/VIDEO_ID_11",
    "width": 315,
    "height": 560,
    "caption": ""
  }
}

Catalogue d’options (upsellCatalog)

Utilisez un objet data vide pour afficher ici le catalogue d’options éligibles. Les produits, prix et disponibilités proviennent du catalogue configuré et du contexte voyageur ; ils ne sont pas stockés dans ce bloc.

{
  "id": "guide00012",
  "type": "upsellCatalog",
  "data": {}
}

Une option précise (upsellProduct)

data.itemId est l’identifiant numérique d’une option existante. Retrouvez-le avec GET /public/v1/upsells et le scope upsells.read ; remplacez l’identifiant fictif 401 ci-dessous. Conservez l’identifiant d’origine sauf si vous choisissez volontairement une autre option. L’écriture de la page utilise guides.write ; ce bloc ne crée pas de produit, ne fixe pas de prix et n’effectue aucun achat.

{
  "id": "guide00013",
  "type": "upsellProduct",
  "data": {
    "itemId": 401
  }
}

Validation REST : les créations et modifications de pages rejettent avec HTTP 422 les types de blocs non pris en charge et les données mal formées. Toutes les langues fournies sont vérifiées avant toute écriture, y compris les éléments des listes imbriquées, les cellules des tableaux et les champs des médias. L’erreur indique le document et le bloc concernés. Les identifiants restent facultatifs pour les documents existants ; s’ils sont fournis, ils doivent être des chaînes non vides et uniques. Les métadonnées et la mise en forme sont conservées. Cette validation de structure ne vérifie pas la disponibilité réelle d’une image ni l’existence d’une option.

Contrôles de l’éditeur et formats à préserver

imageLibrary est un contrôle de sélection d’image de l’éditeur, pas une galerie à envoyer à l’API. Utilisez des blocs image. linkPreview est un réglage de bloc (tunes), pas un type de contenu indépendant ; préservez les données existantes. N’inventez pas de blocs html, code, button ou de fournisseurs vidéo non documentés : l’acceptation d’un objet JSON ne garantit pas son affichage dans le livret.

Modifier une page sans perdre son contenu

Lisez d’abord GET /public/v1/properties/{id}/welcome-guide/pages/{pageId} avec guides.read pour récupérer content et contents. Travaillez sur une copie du document complet de la langue concernée, modifiez seulement les blocs voulus, puis envoyez ce document avec PATCH sur la même URL et guides.write. Un bloc envoyé seul remplace le document par ce seul bloc ; les blocs ne sont pas fusionnés par id. Les autres langues omises restent inchangées. Pour déplacer une page existante, utilisez order, pas position.

Les champs text et texts ne remplacent pas le document structuré. Respectez les indications de lecture simpleTextUpdateAllowed, recommendedUpdateMode et recommendedUpdateField ; une page structurée refuse un remplacement par du texte brut. Les exemples unitaires de cette section décrivent des blocs, pas des requêtes PATCH complètes pour une page existante. Après la modification, relisez la page et vérifiez les langues, identifiants, liens et médias. Avec seulement guides.write, la réponse d’écriture est minimale ; ajoutez guides.read pour recevoir le détail.

Webhooks (événements en temps réel)

Si vous ne voulez pas interroger l'API en boucle pour détecter les changements, vous pouvez vous abonner à des webhooks. Nowistay appellera alors une URL que vous fournissez chaque fois qu'une réservation est créée ou modifiée sur la propriété, en envoyant les mêmes données que GET /public/v1/bookings/{id}.

Deux événements sont supportés aujourd'hui :

  • booking.created : une nouvelle réservation a été enregistrée sur la propriété.
  • booking.updated : une réservation existante a changé (dates, statut, infos voyageur, code de serrure, etc.).

Chaque webhook est lié à une seule propriété et vous pouvez en enregistrer jusqu'à trois par propriété. Les livraisons sont signées (HMAC-SHA256) pour que vous puissiez vérifier qu'elles viennent bien de Nowistay, réessayées avec un délai croissant en cas d'échec, et le endpoint est désactivé automatiquement s'il continue d'échouer pendant plus de 72 heures. Les payloads webhook ne contiennent pas les champs voyageur sensibles (email, téléphone, adresse, code de serrure). Si vous en avez besoin, appelez l'API avec le scope bookings.sensitive.read.

La procédure complète et la vérification de signature sont dans l'article dédié : API publique : recevoir les webhooks de réservation en temps réel.

Limites d'appel

  • Endpoints de lecture : 120 requêtes par minute par client et par route.
  • Endpoints d'écriture (POST, PATCH) : 30 requêtes par minute par client et par route.
  • Endpoints OAuth : 20 requêtes par minute par IP ou par client.

Quand vous atteignez la limite, l'API renvoie 429 Rate limit exceeded. Attendez quelques secondes avant de réessayer.

Format des erreurs

Toutes les erreurs suivent le même format, avec un code stable, un message lisible et un requestId à inclure dans vos demandes au support.

{
  "error": {
    "code": "validation_error",
    "message": "The request contains an invalid value.",
    "requestId": "example-request-id"
  }
}
  • 400: Requête mal formée (corps invalide, champ manquant).
  • 401: Token absent ou invalide.
  • 403: Token valide mais sans le scope requis, ou abonnement AI Channel Manager manquant.
  • 404: Ressource introuvable ou non accessible.
  • 409: Conflit (par exemple, clé de paramètre déjà existante).
  • 422: Validation en échec.
  • 429: Limite d'appel atteinte.

Bonnes pratiques

  • Demandez le plus petit jeu de scopes possible. Ajoutez des scopes plus tard, à mesure que votre intégration grandit.
  • Stockez vos tokens côté serveur. Ne déployez jamais un token longue durée dans un client public et ne le commitez pas dans un dépôt.
  • Traitez les 401 par un refresh. Si le refresh échoue aussi, renvoyez l'utilisateur dans le flux d'autorisation.
  • Utilisez le flag silent en écriture. Mettez-le à true quand vous synchronisez en masse et que vous ne voulez pas déclencher les notifications voyageur.
  • Conservez le requestId dans vos logs. Ça accélère beaucoup la résolution côté support.

Pour aller plus loin

  • Ouvrez la documentation interactive et explorez chaque endpoint : api.nowistay.com/public/docs.
  • Récupérez le schéma complet et le fichier OpenAPI à l'adresse /public/openapi.json.
  • Vous avez besoin d'un scope ou d'un endpoint qui n'existe pas encore ? Écrivez au support en décrivant votre cas d'usage.

‍

‍

‍

Prêt à Mettre Votre Airbnb en Pilote Automatique ?

Rejoignez 300+ professionnels qui automatisent les échanges avec les voyageurs et gagnent un temps précieux.