Le modèle XML s'adresse aux PMS installés chez l'hôtel. Il parle le dialecte XML Fidelio d'Oracle OPERA (OXI) et repose sur un principe simple : toutes les connexions partent de l'hôtel. Bridg_ n'appelle jamais le PMS ; le PMS pousse ses messages vers Bridg_ et vient chercher ceux de Bridg_. Aucune adresse publique ni ouverture de pare-feu n'est nécessaire côté hôtel.
Identifiant d'adaptateur : opera-oxi. Cible validée : OPERA 5 on-premise avec OXI (module
servlet) — voir Paramétrage Oracle OPERA / OXI. Tout PMS capable d'émettre et
de lire ce dialecte peut utiliser ce modèle.
Pages du modèle XML :
- Messages ARI —
RTAV,RATE,RAVL,RESTRICTION - Réservations et accusés —
RESERVATION(deux sens),RESULT,MESSAGEREQUEST - Paramétrage Oracle OPERA / OXI
Topologie
POST …/messages?key=<secret> (le PMS envoie : ARI, réservations, RESULT, MessageRequest)
PMS (OXI) ────────────────────────────────────────────▶ Bridg_
PMS (OXI) ◀──────────────────────────────────────────── Bridg_
GET …/messages?key=<secret> (le PMS vient chercher : réservations OTA/directes, RESULT)
Une seule URL à configurer dans le PMS, pour l'envoi comme pour la réception :
{base}/api/pms/v1/connections/{connectionId}/messages
Authentification
Deux schémas sont acceptés sur chaque appel ; le premier est recommandé pour OXI, dont l'écran de configuration HTTP n'a pas de champ d'identifiants.
| Schéma | Comment | Exemple |
|---|---|---|
| Clé dans l'URL (recommandé) | paramètre de query key=<secret> |
…/messages?key=9f1e… |
| HTTP Basic | Authorization: Basic base64(clientToken:secret) |
user = pms_3f9c…, password = secret |
- Si
keyest présent dans l'URL, il est seul évalué. - OXI ajoute ses propres paramètres à l'URL configurée (
&propertyName=<code>&zipData=N) ; ils sont acceptés et ignorés.zipDatadoit rester àN(pas de compression). - Réponse
401 UNAUTHORIZEDsi la clé, l'identifiant ou le mot de passe est faux ;404si leconnectionIdest inconnu ;403 CONNECTION_INACTIVEsi la connexion est en pause.
L'enveloppe <?Label ?>
Tout message, dans les deux sens, commence par la déclaration XML puis l'instruction de
traitement Label, qui sert au routage :
<?xml version="1.0" encoding="UTF-8"?>
<?Label ATLALG|RTAV|2841178|NEW?>| Champ | Position | Description |
|---|---|---|
propertyName |
1 | Code de l'établissement chez le PMS (externalPropertyCode de la connexion). Bridg_ le renvoie tel quel dans ses RESULT. |
messageType |
2 | Type de message : RTAV, RATE, RAVL, RESTRICTION, RESERVATION, RESULT, MESSAGEREQUEST… |
transactionId |
3 | Identifiant du message chez l'émetteur. Entier de 1 à 999999999 pour les messages servis par Bridg_ ; recommandé aussi pour ceux du PMS. C'est la clé de déduplication et de corrélation des RESULT. |
status |
4 | NEW pour un message ordinaire ; SUCCESS ou FAILED pour un RESULT. |
Un message sans Label est refusé 400 MISSING_LABEL ; un Label à moins de 4 champs,
400 MALFORMED_LABEL. Aucun champ ne peut contenir | ni ?.
Types de message
messageType |
Sens | Contenu | Support |
|---|---|---|---|
RTAV |
PMS → Bridg_ | disponibilité par type de chambre et par nuit | ✅ détail |
RATE |
PMS → Bridg_ | prix par plan tarifaire (DETAIL, HEADERWITHDETAIL) ; un HEADER est acquitté sans effet |
✅ détail |
RAVL |
PMS → Bridg_ | restrictions par code tarifaire | ✅ détail |
RESTRICTION |
PMS → Bridg_ | restrictions par tarif × type de chambre | ✅ détail |
RESERVATION |
PMS → Bridg_ | réservation créée, modifiée ou annulée dans le PMS | ✅ détail |
RESERVATION |
Bridg_ → PMS | réservation OTA ou directe (servie par GET) |
✅ détail |
RESULT |
les deux sens | accusé de traitement | ✅ détail |
MESSAGEREQUEST |
PMS → Bridg_ | redemande d'une RESERVATION perdue |
✅ (type RESERVATION seulement) |
RAVR |
PMS → Bridg_ | restrictions par type de chambre | ❌ RESULT FAILED RESTRICTION_GRANULARITY_MISMATCH — utiliser RAVL |
INVENTORY, ALLOTMENT, HURDLE |
PMS → Bridg_ | hors-service et limites de vente, allotements, hurdles | ❌ RESULT FAILED UNSUPPORTED_MESSAGE_TYPE |
PROFILE, STAY, PACKAGES, autres |
PMS → Bridg_ | ❌ RESULT FAILED UNSUPPORTED_MESSAGE_TYPE |
Un message d'un type non supporté est acquitté par un RESULT d'échec (réponse HTTP 200),
jamais par un 4xx : le PMS peut ainsi continuer à envoyer ce type sans bloquer sa file, et
l'hôtelier voit le refus dans le journal.
Envoyer un message
Request
{base}/api/pms/v1/connections/{connectionId}/messages?key={secret}Content-Type: text/xml; charset=UTF-8
<?xml version="1.0" encoding="UTF-8"?>
<?Label ATLALG|RTAV|2841178|NEW?>
<RtavMessage xmlns="rtav.fidelio.4.0">
<HotelReference hotelCode="ATLALG"/>
<DailyInventories>
<DailyInventory datum="2026-10-10">
<RoomTypeInventories>
<RoomTypeInventory roomType="DLX" physicalRooms="20" outOfOrder="1" available="12" roomTypeOverbook="2"/>
</RoomTypeInventories>
</DailyInventory>
</DailyInventories>
</RtavMessage>curl -X POST "https://staging.bridgchannel.app/api/pms/v1/connections/$CONNECTION_ID/messages?key=$SECRET" \
-H "Content-Type: text/xml; charset=UTF-8" \
--data-binary @rtav.xmlSuccess Response
200 OK — le message est reçu et persisté. Le verdict de traitement est un RESULT mis en
file, à récupérer par GET. Le corps de la réponse contient, à titre indicatif, le même RESULT
(text/xml) pour un message ARI, ou un objet JSON pour les autres types ; OXI n'en lit pas le
contenu.
<?xml version="1.0" encoding="UTF-8"?>
<?Label ATLALG|RESULT|2841178|SUCCESS?>
<RESULT xmlns="result.fidelio.4.0" success="SUCCESS" timeStamp="2026-09-11T10:39:18.895">
<resortId>ATLALG</resortId>
<resultMessage>Successfully applied in Bridg Channel Manager</resultMessage>
</RESULT>200 OK avec verdict négatif en file (type non supporté, code non mappé, message illisible mais
enveloppe lisible) :
{ "accepted": true, "verdictQueued": true }202 Accepted — même transactionId reçu à nouveau pendant son traitement ; ne pas réémettre.
Error Response
400 Bad Request — enveloppe absente ou malformée (aucune corrélation possible) :
{ "error": "Instruction <?Label ...?> absente — enveloppe OXI obligatoire", "code": "MISSING_LABEL" }401 Unauthorized · 403 Forbidden (CONNECTION_INACTIVE) · 404 Not Found · 429 Too Many Requests
500 Internal Server Error — échec transitoire ; le message est conservé et rejoué côté
Bridg_, et le PMS doit le réémettre après un délai :
{ "error": "Message reçu mais non appliqué — reprise programmée", "code": "INGEST_FAILED" }Returns
| Cas | HTTP | Verdict |
|---|---|---|
| Message appliqué | 200 | RESULT SUCCESS en file |
| Message appliqué partiellement (items refusés) | 200 | RESULT FAILED en file, resultMessage détaille les items (#0 UNMAPPED_UNIT_TYPE — …) |
| Type non supporté, restriction non traduisible, corps illisible | 200 | RESULT FAILED en file |
| Doublon déjà traité | 200 | accusé mémorisé, pas de nouveau RESULT |
| Doublon en cours de traitement | 202 | — |
| Enveloppe illisible | 400 | — |
| Échec transitoire | 500 | réémettre |
Récupérer le prochain message
Le PMS vient chercher, un message par appel, dans l'ordre FIFO : réservations OTA et directes
(RESERVATION) et accusés de traitement (RESULT) des messages qu'il a envoyés.
Request
{base}/api/pms/v1/connections/{connectionId}/messages?key={secret}curl -i "https://staging.bridgchannel.app/api/pms/v1/connections/$CONNECTION_ID/messages?key=$SECRET"Success Response
200 OK avec un message. L'en-tête HTTP title reprend l'enveloppe et permet de router sans
parser le corps.
HTTP/1.1 200 OK Content-Type: text/xml; charset=UTF-8 title: ATLALG|RESERVATION|42|NEW <?xml version="1.0" encoding="UTF-8"?> <?Label ATLALG|RESERVATION|42|NEW?> <Reservation xmlns="reservation.fidelio.4.0" mfReservationAction="ADD"> … </Reservation>
200 OK sans message : corps vide et Content-Length: 0 (jamais un 204).
HTTP/1.1 200 OK Content-Length: 0
Error Response
401 Unauthorized · 403 Forbidden (CONNECTION_INACTIVE) · 404 Not Found · 429 Too Many Requests
Returns
| Cas | Réponse |
|---|---|
| Un message est en attente | 200, corps XML, en-tête title |
| Rien en attente | 200, Content-Length: 0 |
Note
- Rythme : interroger toutes les 30 à 120 secondes ; quand un message est servi, ré-interroger immédiatement jusqu'à obtenir un corps vide.
- Une
RESERVATIONservie reste en attente de votreRESULT(corrélé sur sontransactionId). SansRESULTsous 30 minutes, elle est resservie avec un nouveautransactionId: dédupliquez surreservationID. - Un
RESULTservi n'attend rien en retour. - L'ordre est garanti par réservation : la modification d'une réservation n'est jamais servie avant sa création ; une réservation en dead-letter bloque ses propres événements suivants, pas ceux des autres réservations.
Accusés de traitement
Le protocole est asynchrone par construction : la réponse HTTP n'accuse que la réception.
| Sens | Message | Verdict | Corrélation |
|---|---|---|---|
| PMS → Bridg_ | ARI, réservation, MessageRequest | RESULT mis en file par Bridg_, récupéré par GET |
transactionId du message du PMS |
| Bridg_ → PMS | RESERVATION servie par GET |
RESULT envoyé par le PMS sur POST …/messages |
transactionId que Bridg_ a posé sur la réservation |
Format et sémantique du RESULT : reservations.md.
Conventions de dates
| Forme | Où | Sémantique |
|---|---|---|
<TimeSpan timeUnitType="DAY"><startTime>2026-10-10T00:00:00.000</startTime><numberOfTimeUnits>3</numberOfTimeUnits></TimeSpan> |
RAVL, RESTRICTION, RESERVATION |
première nuit + nombre de nuits (1 à 3 700). DAY uniquement. |
<DaysOfWeek><monday>1</monday>…</DaysOfWeek> |
RAVL, RESTRICTION, RATE (RateDaysOfWeek) |
masque jour-de-semaine appliqué à la plage ; absent ou tout à 1 = toute la plage. 1, Y, true = actif. |
<startDate>2026-10-10</startDate><endDate>2026-10-12</endDate> |
RATE |
endDate inclusive par défaut (rateEndDateInclusive). |
datum="2026-10-10" |
RTAV |
une nuit. |
Les dates sont des dates civiles ; la partie heure de startTime est ignorée.
Codes d'erreur propres au XML
code |
Cause |
|---|---|
MISSING_LABEL, MALFORMED_LABEL |
enveloppe absente ou incomplète (HTTP 400) |
UNEXPECTED_MESSAGE |
élément racine inattendu pour le type annoncé |
INVALID_TIMESPAN, INVALID_DATE_RANGE |
TimeSpan ou startDate/endDate illisibles ou hors bornes |
UNSUPPORTED_MESSAGE_TYPE |
type non traduit (INVENTORY, PROFILE…) |
RESTRICTION_GRANULARITY_MISMATCH |
RAVR — utiliser RAVL |
UNSUPPORTED_RESTRICTION |
code de restriction sans équivalent (ADVBOOK_MIN…) |
UNSUPPORTED_ROOM_CLASS, UNSUPPORTED_RATE_CATEGORY, UNSUPPORTED_SCOPE, UNSUPPORTED_BLOCK_RESTRICTION |
restriction par classe, catégorie, hôtel entier ou bloc |
UNSUPPORTED_TIERED_RATE, UNSUPPORTED_WEEKEND_RATE, UNSUPPORTED_RATE_MESSAGE |
tarifs par durée de séjour, prix week-end, rateMessageType inconnu |
UNSUPPORTED_TIME_UNIT |
timeUnitType autre que DAY |
MISSING_RATE_CODE, MISSING_RESTRICTION_CODE, MISSING_REFERENCE, MISSING_AMOUNT, DUPLICATE_NIGHT, NEGATIVE_COUNT |
champ requis absent ou incohérent |