Ce document décrit ce qui est commun aux deux modèles (XML et JSON) : environnements, connexion, objets de données, erreurs, limites, fiabilité et API d'administration. Les détails de transport sont dans xml/README.md et json/README.md.
Environnements et versionnement
| Environnement | URL de base |
|---|---|
| Staging | https://staging.bridgchannel.app |
L'URL de production est communiquée par Bridg_ à la mise en service, une fois la certification passée. Les intégrations se développent et se certifient sur le staging.
L'API est versionnée dans le chemin : /api/pms/v1/. Les évolutions compatibles (nouveau champ
optionnel, nouveau code d'erreur) ne changent pas la version. Un changement incompatible donnerait
un nouveau préfixe /v2/.
Toutes les URL d'ingestion ont la forme :
{base}/api/pms/v1/connections/{connectionId}/{ressource}
La connexion
Une connexion relie un établissement Bridg_ à un PMS. Il y a au plus une connexion par établissement. Elle est créée par le propriétaire de l'établissement (ou par Bridg_) dans le tableau de bord, menu Interface PMS.
Fields
| Champ | Type | Description |
|---|---|---|
connectionId |
string | Identifiant de la connexion. Présent dans toutes les URL. |
adapterId |
string | Dialecte, figé à la création : opera-oxi (XML), bridg-hms (JSON webhook), winner (JSON feed). |
clientToken |
string | Identifiant public de l'appelant, forme pms_ + 32 caractères hexadécimaux. Non secret à lui seul. |
secret |
string | Secret partagé, 64 caractères hexadécimaux. Affiché une seule fois à la création ; stocké chiffré, jamais relisible. |
externalPropertyCode |
string | Code de l'établissement chez le PMS. En XML, c'est le propertyName de l'enveloppe et le resortId des RESULT. Ne peut contenir ni | ni ?. |
rateEndDateInclusive |
boolean | XML uniquement. RateDetail/endDate est-elle inclusive ? Défaut true. À vérifier en recette. |
ariHorizonDays |
integer | Horizon ARI attendu, défaut 365. Informatif : Bridg_ ne rejette pas un item au-delà. |
status |
enum | PENDING, ACTIVE, PAUSED, DISABLED. |
Statuts
| Statut | Messages entrants (ARI, réservations) | Réservations sortantes | Écrans ARI de Bridg_ |
|---|---|---|---|
PENDING |
acceptés | non émises | modifiables |
ACTIVE |
acceptés | émises | lecture seule (le PMS est seul maître) |
PAUSED |
refusés — 403 CONNECTION_INACTIVE |
non émises | modifiables |
DISABLED |
refusés — 403 CONNECTION_INACTIVE |
non émises | modifiables |
L'activation est un geste explicite du propriétaire. Régénérer les identifiants invalide
immédiatement l'ancien couple clientToken / secret.
Correspondance des codes (mapping)
Bridg_ ne crée jamais de type de chambre ni de plan tarifaire à partir d'un message. Chaque code du PMS doit être associé à un objet Bridg_ avant le premier message.
| Kind | Code PMS | Objet Bridg_ | Utilisé par |
|---|---|---|---|
UNIT_TYPE |
code de type de chambre (DLX, 1KN…) |
type de chambre | disponibilité, prix, restrictions par type, réservations |
RATE_PLAN |
code tarifaire (BAR1, RACK…) |
tarif | prix, restrictions, réservations |
Un code non mappé est refusé item par item (UNMAPPED_UNIT_TYPE, UNMAPPED_RATE_PLAN) ; les
autres items du même message sont appliqués. Le mapping se fait dans l'écran Interface PMS
(onglet Correspondance des codes) ou par l'API d'administration.
Objets communs
ARI item
Quel que soit le format, un message ARI est traduit en items indépendants. Un item porte une seule nature (disponibilité, prix ou restrictions) sur une plage de nuits. En JSON l'item est envoyé tel quel ; en XML il est produit par la traduction des messages Fidelio.
| Champ | Type | Requis | Description |
|---|---|---|---|
unitTypeCode |
string | pour availability et prices |
Code PMS du type de chambre (mapping UNIT_TYPE). Optionnel pour une restriction : sans lui, la restriction porte sur le tarif seul. |
ratePlanCode |
string | pour prices et restrictions |
Code PMS du plan tarifaire (mapping RATE_PLAN). |
from |
date AAAA-MM-JJ |
oui | Première nuit, incluse. |
to |
date AAAA-MM-JJ |
oui | Borne de fin, exclue (to > from). from=2026-10-01, to=2026-10-04 couvre les nuits du 1, 2 et 3. |
availability |
integer ≥ 0 | une des trois natures | Nombre de chambres vendables du type sur chaque nuit de la plage. |
overbookingAllowance |
integer ≥ 0 | non | Marge de survente autorisée, stockée à part, jamais additionnée à la disponibilité. |
prices |
object | une des trois natures | { currency, byOccupancy: [ { guests, gross?, net? } ] }. |
restrictions |
object | une des trois natures | { closed?, closedToArrival?, closedToDeparture?, minLos?, maxLos? }. |
Règles d'application :
- Disponibilité — le chiffre du PMS fait foi. S'il dépasse l'inventaire paramétré dans Bridg_, l'inventaire du type de chambre est relevé à ce niveau.
- Prix — l'occupation 2 est obligatoire (prix de référence du tarif ; les autres occupations
dérivent des modificateurs du tarif Bridg_). Bridg_ retient
grosssi le tarif est paramétré TTC,nets'il est HT. Le prix doit être > 0. Une baisse de plus de 50 % par rapport au dernier prix connu sur une date est refusée (PRICE_DROP_REJECTED) — tout l'item est rejeté, rien n'est écrit. La devise est celle de l'établissement ; Bridg_ ne convertit pas. - Restrictions — seuls les champs fournis sont écrits : un item
minLosseul ne rouvre pas un tarif fermé et n'efface pas un CTA. SansunitTypeCodela restriction porte sur le tarif ; avec, elle porte sur tarif × type et la vue tarif est recalculée comme l'agrégat des types connus (fermé seulement si tous les types sont fermés ; LOS la moins restrictive). - Chaque item est appliqué indépendamment ; l'accusé détaille le résultat par item.
- Après application, Bridg_ relaie la donnée vers les OTA connectés.
Réservation (booking)
Les deux modèles véhiculent la même structure, dans les deux sens. En JSON elle est envoyée
telle quelle (camelCase) ; en XML elle est traduite depuis/vers <Reservation> Fidelio ; en
webhook JSON elle est envoyée en PascalCase.
| Champ | Type | Description |
|---|---|---|
cmRef |
string | Référence Bridg_ (numéro de réservation). Clé de rapprochement Bridg_ ↔ PMS. |
externalRef |
string | Référence chez l'OTA (vide pour une réservation directe). En entrée : référence du PMS. |
channel |
string | BOOKING_COM, EXPEDIA, DIRECT… ; PMS pour une réservation venue du PMS. |
revision |
integer | Strictement croissant par réservation. Une révision ≤ la dernière traitée est ignorée. |
currency |
string | ISO 4217. |
totalAmount |
{ gross, net } |
Montant de la chambre seule. Aujourd'hui gross = net : Bridg_ n'a pas de ventilation de taxes sur les réservations. |
paymentType |
enum | PREPAID (payée), ON_SITE (à régler sur place), GUARANTEED (réservé). |
paymentCardToken |
string | Jeton PSP uniquement — jamais de numéro de carte. |
booker |
guest | { lastName, firstName?, email?, phone? } — celui qui réserve. |
customer |
guest | Optionnel, l'occupant s'il diffère. |
reservations[] |
line[] | Une ligne par chambre. |
Ligne de réservation (reservations[]) :
| Champ | Type | Description |
|---|---|---|
lineCode |
string | Identifiant de ligne, stable à travers les révisions (01, 02…). |
state |
enum | CONFIRMED, CANCELLED. |
unitTypeCode |
string | Code PMS du type de chambre. |
ratePlanCode |
string | Code PMS du plan tarifaire. |
from |
date | Arrivée. |
to |
date | Départ, exclusif. |
occupancy[] |
{ bucket, count }[] |
bucket = ADULT ou CHILD. |
nights[] |
{ date, gross, net }[] |
Une entrée par nuit, datée explicitement. La somme vaut le total de la ligne. |
guests[] |
guest[] | Optionnel. |
Règles :
- Une réservation de 2 chambres identiques = 2 lignes (
01,02). - Modification = état complet : la réservation est renvoyée entière ; toute ligne absente est considérée annulée.
- Annulation : toutes les lignes en
CANCELLED(XML), ou message d'annulation dédié (JSON). - Bridg_ n'émet jamais vers le PMS une réservation qui vient de ce PMS (anti-boucle).
- Une réservation entrante est enregistrée avec la source
PMSet la référence externepms:<référence>; elle ne repart ni vers les OTA ni vers le PMS, et ne modifie pas la disponibilité (seul le flux ARI le fait).
Accusé de traitement (result)
| Champ | Description |
|---|---|
| identifiant du message d'origine | messageId (JSON) ou transactionId (XML) |
success |
true / false |
| détail | par item en JSON (results[]), texte libre en XML (resultMessage) |
Erreurs
Hors accusé XML, toute erreur HTTP a le format :
{ "error": "Type d'unité \"SUP\" non mappé sur cette connexion", "code": "UNMAPPED_UNIT_TYPE" }| HTTP | code |
Signification | Action côté PMS |
|---|---|---|---|
| 400 | VALIDATION_ERROR, MISSING_LABEL, MALFORMED_LABEL, INVALID_TIMESPAN, INVALID_DATE_RANGE, MISSING_REFERENCE, MISSING_AMOUNT, DUPLICATE_NIGHT, NEGATIVE_COUNT |
message illisible ou incomplet | corriger, ne pas réémettre tel quel |
| 401 | UNAUTHORIZED |
identifiants, clé d'URL ou signature invalides | vérifier clientToken / secret / signature |
| 403 | CONNECTION_INACTIVE |
connexion en pause ou désactivée | contacter l'hôtel |
| 404 | NOT_FOUND |
connectionId inconnu |
vérifier l'URL |
| 405 | INGRESS_NOT_SUPPORTED |
flux non prévu pour ce dialecte | vérifier le modèle utilisé |
| 422 | UNSUPPORTED_MESSAGE_TYPE, UNSUPPORTED_RESTRICTION, UNSUPPORTED_ROOM_CLASS, UNSUPPORTED_RATE_CATEGORY, UNSUPPORTED_SCOPE, UNSUPPORTED_BLOCK_RESTRICTION, UNSUPPORTED_TIME_UNIT, UNSUPPORTED_TIERED_RATE, UNSUPPORTED_WEEKEND_RATE, UNSUPPORTED_RATE_MESSAGE, RESTRICTION_GRANULARITY_MISMATCH, UNMAPPED_UNIT_TYPE, NO_LINES |
refus déterministe | corriger la donnée ou le mapping ; le rejeu donnera le même refus |
| 429 | RATE_LIMIT_EXCEEDED |
trop de requêtes | ralentir, réessayer |
| 500 | INGEST_FAILED, INTERNAL_ERROR |
échec transitoire, message conservé | réémettre après un délai |
Codes par item ARI (dans results[] en JSON, dans resultMessage en XML) :
UNMAPPED_UNIT_TYPE, UNMAPPED_RATE_PLAN, INVALID_DATE_RANGE, PRICE_DROP_REJECTED,
VALIDATION_ERROR, INTERNAL_ERROR.
En XML, un refus déterministe est signalé par un RESULT d'échec mis en file (réponse HTTP
200), jamais par un 4xx — voir xml/README.md.
Limites
| Limite | Valeur |
|---|---|
| Transport | HTTPS uniquement, TLS 1.2 ou plus |
| Taille maximale d'un message entrant | 5 Mo |
| Débit | 600 requêtes / minute / adresse IP sur /api/pms/v1/connections/* → 429 RATE_LIMIT_EXCEEDED |
| Compression | non supportée en entrée : corps non compressé (OXI : zipData=N) |
| Encodage | UTF-8 |
| Plage de dates d'un item | 1 à 3 700 nuits |
| Délai de réponse de Bridg_ | quelques centaines de ms ; timeout client conseillé : 60 s |
Fiabilité
- Écriture avant traitement. Tout message entrant est persisté tel quel (octets bruts) avant d'être appliqué. Un message reçu est appliqué ou visible en erreur, jamais perdu.
- Déduplication. Un identifiant de message déjà traité renvoie l'accusé mémorisé (
200) sans retraiter. Reçu à nouveau pendant son traitement :202(en attente, ne pas retraiter). - Reprise. Un échec transitoire (base indisponible…) répond
500; le message reste en inbox et est rejoué automatiquement (5 tentatives, toutes les 30 s). Le PMS doit réémettre sur500; les deux filets se cumulent sans doublon. - Échec déterministe (code non mappé, message illisible) : jamais rejoué. Réémettre le même message donnera le même refus tant que la cause n'est pas corrigée. Les octets sont conservés quand même — y compris pour un message que Bridg_ n'a pas su traduire — dès lors que l'enveloppe porte un identifiant : le message reste auditable et rejouable une fois la cause corrigée, au lieu de disparaître avec son refus.
- Réservations sortantes. File durable FIFO par connexion, ordre garanti par réservation (le MODIFY d'une réservation n'est jamais servi avant son CREATE). Échec transitoire : rejeu avec backoff exponentiel (30 s, 1 min, 2 min… plafonné à 30 min, 8 tentatives). Refus métier : dead-letter visible dans l'écran Interface PMS, rejouable à la main après correction.
Rétention
| Donnée | Durée |
|---|---|
| Messages entrants traités (inbox) | 30 jours |
Émissions sortantes livrées (outbox DONE) |
7 jours |
Émissions en échec (FAILED) et messages en erreur |
conservés jusqu'à rejeu ou suppression de la connexion |
API d'administration
Base : {base}/api/pms/v1/admin. Authentification par session Bridg_ (JWT). Réservée au
propriétaire de l'établissement et au super-administrateur. C'est l'API derrière l'écran
Interface PMS ; un intégrateur n'a normalement pas à l'appeler.
| Méthode | Chemin | Rôle |
|---|---|---|
POST |
/connections |
Crée la connexion (propertyId, name, adapterId, externalPropertyCode). Renvoie clientToken + secret une seule fois. |
GET |
/connections/by-property/:propertyId |
La connexion de l'établissement (mappings inclus, sans secret). |
PATCH |
/connections/:id/status |
{ status: PENDING | ACTIVE | PAUSED | DISABLED }. |
PATCH |
/connections/:id/settings |
name, externalPropertyCode, rateEndDateInclusive, ariHorizonDays. |
POST |
/connections/:id/credentials |
Régénère clientToken + secret ; les anciens sont invalidés. |
PUT |
/connections/:id/mappings |
{ kind: UNIT_TYPE | RATE_PLAN, bridgId, externalCode }. Un élément Bridg ne porte qu'un code : s'il en avait un, il est remplacé (replacedCode dans la réponse). Un code déjà associé à un autre élément est refusé (409 CODE_ALREADY_USED), jamais déplacé. |
DELETE |
/connections/:id/mappings |
{ kind, externalCode }. |
PUT |
/connections/:id/outbound |
JSON webhook : pmsBaseUrl, clientToken, accessToken de l'API du PMS. |
GET |
/connections/:id/configuration |
JSON webhook : découverte du catalogue du PMS (UnitTypes, RatePlans). |
GET |
/connections/:id/overview |
Vue d'ensemble de la synchronisation : compteurs entrants/sortants par statut, dernières activités, codes reçus sans mapping (unmapped.inbound) et éléments Bridg sans code rencontrés à l'émission (unmapped.outbound). |
GET |
/connections/:id/inbox |
Journal des messages entrants. Filtres status (RECEIVED, PROCESSED, FAILED), kind (ARI, RESERVATION), limit (≤ 200). Chaque message ARI porte un summary (items rejetés) et ses unmappedCodes. |
GET |
/connections/:id/inbox/:messageId/raw |
Le message entrant tel que reçu (octets exacts). |
GET |
/connections/:id/reservations |
Journal des émissions sortantes. Filtre status (PENDING, SENT, DONE, FAILED). |
POST |
/reservations/:outboxId/retry |
Rejoue une émission FAILED après correction (mapping ajouté…). |
DELETE |
/connections/:id |
Supprime la connexion, ses mappings et ses journaux. |