English version →

WebReservations Partner API

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.

Nessun dato di carta di credito, mai.
WebReservations non raccoglie e non conserva numeri di carta, scadenze o CVV: gli incassi passano da un gateway (Stripe, PayPal, Nexi) e la carta non passa da noi. In questa API i campi della carta non esistono: non sono opzionali, non sono vuoti, non ci sono proprio. Della parte economica trovate metodo, gateway, importo incassato, residuo e il riferimento della transazione, che è l'unico appiglio verificabile e va usato al posto della carta.

1. Come funziona

Tre operazioni, nello schema che già conoscete dagli altri canali:

OperazioneA cosa serve
GET /v1/reservationselenco di cosa è nuovo o cambiato
GET /v1/reservations/{id}il dettaglio di una singola prenotazione
POST /v1/reservations/{id}/confirmpresa in carico: non me la mandare più

Il ciclo consigliato è un giro ogni pochi minuti:

  1. chiamate GET /v1/reservations: risponde con le prenotazioni che non avete ancora preso in carico, o che sono cambiate da quando l'avete fatto;
  2. per ognuna che avete registrato, chiamate POST /v1/reservations/{id}/confirm con il vostro identificativo;
  3. da quel momento quella prenotazione non compare più nell'elenco. Se in seguito cambia qualcosa (importo, date, cancellazione) ricompare da sola: confrontiamo un'impronta dei dati consegnati, quindi non serve che controlliate voi.

Una prenotazione cancellata non sparisce: torna nell'elenco con "status": "cancelled". È il vostro segnale per rimettere la camera in vendita.

2. Autenticazione

Vi consegniamo due valori:

chiavepubblica, identifica la vostra credenziale. Viaggia in chiaro.
segretoriservato. Con il metodo consigliato non viaggia mai.

Tutte le richieste devono essere in HTTPS. In chiaro sono rifiutate.

2.1 Metodo consigliato: richiesta firmata

X-WR-Keyla vostra chiave
X-WR-Timestamporario Unix in secondi. Scarto ammesso: 5 minuti
X-WR-Noncecasuale, da 16 a 64 caratteri A-Z a-z 0-9 _ -, diverso a ogni richiesta
X-WR-SignatureHMAC-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.

2.2 Metodo di ripiego: Basic

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.

3. Le rotte

GET /v1/ping

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"
  }
}

GET /v1/reservations

ParametroSignificato
pending1 (predefinito) solo quelle non ancora prese in carico o cambiate dopo. 0 tutte quelle nella finestra.
from, tofiltro sulla data di arrivo, formato YYYY-MM-DD
afterid oltre il quale continuare: si passa il valore di next
limitquante 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.

GET /v1/reservations/{id}

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.

POST /v1/reservations/{id}/confirm

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.

4. La prenotazione

{
  "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.

Provenienza

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.

Più camere nella stessa prenotazione

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.

Le notti. Un soggiorno dal 22 al 25 occupa le notti del 22, 23 e 24: il 25 la camera è di nuovo vendibile. nights riporta già questo conteggio.

Prezzi

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.

Il dettaglio può mancare. Viene registrato al momento della prenotazione e vale dalle prenotazioni recenti in avanti: sulle più vecchie 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.

5. Errori

{ "ok": false, "error": "auth_failed", "message": "Authentication failed." }

Il campo error è stabile e ci potete scrivere sopra del codice; message può cambiare.

HTTPerrorCosa fare
400invalid_date, missing_referencecorreggere la richiesta
401auth_failedchiave, segreto o firma non tornano
401signature_missingmancano le intestazioni della firma
401timestamp_out_of_windowsincronizzare l'orologio del vostro server
401invalid_noncenonce fuori formato (16–64 caratteri)
401request_replayednonce riutilizzato: uno nuovo a ogni richiesta
403https_requiredchiamare in HTTPS
403ip_not_allowedindirizzo non abilitato: segnalatecelo
403basic_not_allowedusate la firma, o chiedeteci di abilitare i vostri IP
403no_propertiescredenziale senza strutture: segnalatecelo
404not_foundid inesistente, fuori dalle vostre strutture, o rotta sbagliata
405method_not_allowedmetodo HTTP sbagliato per quella rotta
429rate_limitedoltre 120 richieste al minuto: rallentare
429too_many_attemptstroppe autenticazioni fallite: si sblocca entro 15 minuti
503service_unavailableproblema nostro, riprovare fra qualche minuto

6. Limiti

7. Prova rapida

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.