Contratto tecnico · versione 1.0 · https://api.webreservations.it
WebReservations è il motore di prenotazione diretto delle strutture elencate nella vostra credenziale. Questa API vi permette di scaricare le prenotazioni dirette — quelle prese dal sito ufficiale della struttura e quelle registrate dal suo back-office — con lo stesso schema che usate per gli altri canali: si chiede cosa c'è di nuovo, si scarica, si conferma di averlo preso.
Ogni credenziale vede solo le strutture il cui titolare ha chiesto il collegamento:
l'elenco lo trovate rispondendo a /v1/ping.
I nomi dei campi e i codici di errore sono in inglese. Questo documento esiste anche in versione inglese, identica nei contenuti.
Tre operazioni, nello schema che già conoscete dagli altri canali:
| Operazione | A cosa serve |
|---|---|
| GET /v1/reservations | elenco di cosa è nuovo o cambiato |
| GET /v1/reservations/{id} | il dettaglio di una singola prenotazione |
| POST /v1/reservations/{id}/confirm | presa in carico: non me la mandare più |
Il ciclo consigliato è un giro ogni pochi minuti:
GET /v1/reservations: risponde con le prenotazioni che non
avete ancora preso in carico, o che sono cambiate da quando l'avete fatto;POST /v1/reservations/{id}/confirm con il vostro identificativo;Una prenotazione cancellata non sparisce: torna nell'elenco con
"status": "cancelled". È il vostro segnale per rimettere la camera
in vendita.
Vi consegniamo due valori:
| chiave | pubblica, identifica la vostra credenziale. Viaggia in chiaro. |
| segreto | riservato. Con il metodo consigliato non viaggia mai. |
Tutte le richieste devono essere in HTTPS. In chiaro sono rifiutate.
| X-WR-Key | la vostra chiave |
| X-WR-Timestamp | orario Unix in secondi. Scarto ammesso: 5 minuti |
| X-WR-Nonce | casuale, da 16 a 64 caratteri A-Z a-z 0-9 _ -, diverso a ogni richiesta |
| X-WR-Signature | HMAC-SHA256 in esadecimale minuscolo |
La firma si calcola su sei elementi separati da un a capo singolo
(\n). Le parti assenti restano righe vuote, non si saltano:
METODO es. GET
PERCORSO es. /v1/reservations (senza dominio, senza query)
QUERY es. pending=1&limit=50 (vuota se non c'e')
TIMESTAMP lo stesso di X-WR-Timestamp
NONCE lo stesso di X-WR-Nonce
SHA256 sha256 esadecimale del corpo; per un GET, sha256 della stringa vuota
Esempio completo in PHP:
$chiave = 'wr0123456789abcdef012345';
$segreto = 'il-segreto-che-vi-abbiamo-consegnato';
$metodo = 'GET';
$percorso = '/v1/reservations';
$query = 'pending=1';
$corpo = '';
$ts = (string)time();
$nonce = bin2hex(random_bytes(12));
$daFirmare = $metodo . "\n" . $percorso . "\n" . $query . "\n"
. $ts . "\n" . $nonce . "\n" . hash('sha256', $corpo);
$firma = hash_hmac('sha256', $daFirmare, $segreto);
$intestazioni = array(
'X-WR-Key: ' . $chiave,
'X-WR-Timestamp: ' . $ts,
'X-WR-Nonce: ' . $nonce,
'X-WR-Signature: ' . $firma,
);
Ogni nonce vale una volta sola: una richiesta intercettata e rigiocata riceve
request_replayed.
Se il vostro connettore non può firmare, accettiamo
Authorization: Basic base64(chiave:segreto) — ma solo se sulla
vostra credenziale abbiamo configurato gli indirizzi IP da cui chiamate. Senza
quella lista, Basic viene rifiutato: il metodo che espone il segreto a ogni richiesta si
accetta solo insieme a un controllo di rete. Comunicateci gli indirizzi (anche in forma
80.88.87. per un'intera classe) e li abilitiamo.
Verifica che la credenziale funzioni e mostra quali strutture vede.
{
"ok": true,
"data": {
"client": "RoomCloud",
"properties": [194, 207],
"serverTime": "2026-09-02T10:15:00+02:00"
}
}
| Parametro | Significato |
|---|---|
| pending | 1 (predefinito) solo quelle non ancora prese in carico o cambiate dopo. 0 tutte quelle nella finestra. |
| from, to | filtro sulla data di arrivo, formato YYYY-MM-DD |
| after | id oltre il quale continuare: si passa il valore di next |
| limit | quante per pagina, massimo 200 (predefinito 100) |
{
"ok": true,
"count": 2,
"next": 18344, // null quando non c'e' altro
"data": [ { ... }, { ... } ]
}
Restituiamo le prenotazioni con partenza da 60 giorni fa in avanti. Se vi serve uno storico più lungo, ditecelo e allarghiamo la finestra.
Una sola prenotazione. 404 se non esiste o non appartiene a una struttura
della vostra credenziale: i due casi non si distinguono, di proposito.
L'id di una qualunque riga di una prenotazione con più camere restituisce la
prenotazione intera, con il suo id (vedi sotto); lo stesso vale per
confirm, che nella risposta riporta l'id effettivamente preso in carico.
Presa in carico. Corpo JSON con il vostro identificativo:
{ "reference": "CRS-91138035" }
Da quel momento la prenotazione esce dagli elenchi pending=1 finché
non cambia qualcosa nei dati che vi abbiamo mandato.
{
"id": 18342,
"propertyId": 194,
"status": "confirmed", // oppure "cancelled"
"source": "website", // "website" | "admin_manual" | "agency"
"createdAt": "2026-09-02 11:30:00",
"checkin": "2026-09-22",
"checkout": "2026-09-25",
"nights": 3,
"guest": {
"firstName": "Mario",
"lastName": "Rossi",
"email": "m.rossi@example.com",
"phone": "+39 081 1234567"
},
"rooms": [{
"roomId": 77,
"roomName": "Doppia Vista Mare",
"partnerRoomId": 21829, // il roomId con cui la conoscete voi
"quantity": 1,
"adults": 2,
"children": 1,
"rateName": "BB Rimborsabile",
"partnerRateId": 14266, // il rateId con cui la conoscete voi
"nightlyAverage": 116.67, // media a notte della camera
"dayPrices": [ // quanto e' costata ogni notte
{ "day": "2026-09-22", "price": 110.00, "rooms": 1, "total": 110.00 },
{ "day": "2026-09-23", "price": 130.00, "rooms": 1, "total": 130.00 },
{ "day": "2026-09-24", "price": 110.00, "rooms": 1, "total": 110.00 }
]
}],
"amounts": {
"currency": "EUR",
"total": 350.00, // soggiorno + supplementi
"balance": 0.00
},
"extras": "Culla (EUR 15,00); Parcheggio (EUR 10,00)",
"notes": "Arrivo dopo le 22",
"payment": {
"method": "stripe", // stripe | paypal | nexi | bonifico
"status": "paid", // pending | guaranteed | paid | released
"paid": 350.00,
"reference": "pi_3Qabc123" // identificativo presso il gateway
}
}
Sulle cancellate compaiono in più cancelledAt e
cancellationReason.
source dice da dove arriva la prenotazione: website (prenotata
online dall'ospite sul motore della struttura, compresi preventivi e pacchetti accettati
online), admin_manual (inserita dal personale della struttura: telefono, email,
al banco) e agency (inserita dal personale per conto di un'agenzia). Tutte e tre
tengono le camere allo stesso modo e arrivano nello stesso elenco, con gli stessi campi.
Una prenotazione con più tipologie (per esempio 1 Doppia + 1 Suite, online o dal
pannello) è un oggetto solo, con una voce per tipologia in
rooms[], ognuna con i suoi codici, i suoi ospiti e i suoi dayPrices.
amounts.total è il totale di tutte le camere, payment.paid
quanto è stato incassato per tutta la prenotazione, checkin/checkout
coprono tutte le camere. L'id resta lo stesso per tutta la vita della prenotazione.
Se la struttura toglie una camera, la prenotazione torna nell'elenco senza quella camera in
rooms[] (e con i totali aggiornati): va trattata come una modifica, liberando
quella camera. Quando tutte le camere sono cancellate, status diventa
cancelled e rooms[] le elenca tutte.
nights riporta già
questo conteggio.
Di ogni prenotazione mandiamo tre cose: amounts.total, cioè il
totale del soggiorno supplementi compresi;
rooms[].nightlyAverage, la media a notte della camera come l'ha calcolata il
motore al momento della prenotazione; e extras, i supplementi come sono
stati registrati (testo libero con nome e importo).
In rooms[].dayPrices c'è il dettaglio: price è
quanto è costata una camera quella notte, rooms quante
ne sono state prese, total il prodotto dei due. La somma dei
total è il soggiorno senza supplementi; amounts.total li
comprende.
dayPrices è un elenco vuoto. Vuoto significa non lo
sappiamo, non è costata zero: non ricaviamo quei numeri dividendo
il totale per le notti, sarebbe un valore inventato con l'aspetto di un dato. Per lo
stesso motivo una notte a prezzo non impostato non compare.
partnerRoomId e partnerRateId valgono null
quando quella camera o quella tariffa non sono mappate: la prenotazione è comunque
valida, semplicemente non sappiamo a quale vostro codice corrisponda.
{ "ok": false, "error": "auth_failed", "message": "Authentication failed." }
Il campo error è stabile e ci potete scrivere sopra del codice;
message può cambiare.
| HTTP | error | Cosa fare |
|---|---|---|
| 400 | invalid_date, missing_reference | correggere la richiesta |
| 401 | auth_failed | chiave, segreto o firma non tornano |
| 401 | signature_missing | mancano le intestazioni della firma |
| 401 | timestamp_out_of_window | sincronizzare l'orologio del vostro server |
| 401 | invalid_nonce | nonce fuori formato (16–64 caratteri) |
| 401 | request_replayed | nonce riutilizzato: uno nuovo a ogni richiesta |
| 403 | https_required | chiamare in HTTPS |
| 403 | ip_not_allowed | indirizzo non abilitato: segnalatecelo |
| 403 | basic_not_allowed | usate la firma, o chiedeteci di abilitare i vostri IP |
| 403 | no_properties | credenziale senza strutture: segnalatecelo |
| 404 | not_found | id inesistente, fuori dalle vostre strutture, o rotta sbagliata |
| 405 | method_not_allowed | metodo HTTP sbagliato per quella rotta |
| 429 | rate_limited | oltre 120 richieste al minuto: rallentare |
| 429 | too_many_attempts | troppe autenticazioni fallite: si sblocca entro 15 minuti |
| 503 | service_unavailable | problema nostro, riprovare fra qualche minuto |
Con la credenziale in mano, il primo comando da provare:
curl -s https://api.webreservations.it/v1/ping \
-H "X-WR-Key: LA-VOSTRA-CHIAVE" \
-H "X-WR-Timestamp: $TS" \
-H "X-WR-Nonce: $NONCE" \
-H "X-WR-Signature: $FIRMA"
Se risponde con l'elenco delle strutture, il collegamento è a posto.