API pubblica: ricevi i webhook delle prenotazioni in tempo reale

Aggiornato il

I webhook sono il modo più rapido per restare sincronizzato con Nowistay. Invece di interrogare l’API ogni pochi minuti per rilevare prenotazioni nuove o modificate, indichi a Nowistay dove inviare gli eventi e il tuo servizio li riceve non appena accadono. Questo articolo ti mostra come creare un webhook, verificare la firma e gestire i nuovi tentativi.

Un breve promemoria: cos’è l’API pubblica

L’API REST pubblica di Nowistay permette ai tuoi strumenti e ai tuoi partner di leggere e scrivere i dati di Nowistay tramite HTTPS. Ti autentichi con OAuth2 (authorization code + PKCE), ricevi un token di accesso e chiami gli endpoint sotto /public/v1/*. I webhook sono il corrispettivo push: stessi dati, stessa struttura di GET /public/v1/bookings/{id}, ma è Nowistay a inviarteli quando qualcosa cambia.

Se non hai ancora configurato OAuth, parti dalla guida principale: Usare l’API REST pubblica di Nowistay. Copre la registrazione, il flusso di consenso, gli scope e come testare le chiamate dal playground della documentazione.

Cosa ottieni

  • Consegna in tempo reale degli eventi booking.created e booking.updated.
  • Lo stesso corpo JSON di GET /public/v1/bookings/{id}, comprensivo di date, canale, stato, importi, campi rilevanti per le missioni e i tuoi parametri personalizzati della prenotazione.
  • Firma HMAC-SHA256 per verificare che ogni richiesta provenga davvero da Nowistay.
  • Nuovi tentativi automatici con backoff esponenziale quando il tuo endpoint fallisce o va in timeout.
  • Disattivazione automatica dopo 72 ore di errori continui (puoi riattivarlo dall’API).

Prima di iniziare

  • Serve l’abbonamento AI Channel Manager sull’alloggio. Senza, le consegne vengono saltate.
  • Il token OAuth usato per creare il webhook deve avere lo scope webhooks.write. POST /public/v1/webhooks richiede anche bookings.read, perché il payload è il dettaglio completo di una prenotazione.
  • Il tuo URL di ricezione deve essere raggiungibile su internet pubblico, servito in HTTPS in produzione, e rispondere entro 10 secondi con uno stato 2xx.
  • Un webhook è legato a un alloggio. Puoi registrare fino a 3 webhook per alloggio.

Passo 1. Crea un webhook

curl -X POST https://api.nowistay.com/public/v1/webhooks \
  -H "Authorization: Bearer nis_access_token_example" \
  -H "Content-Type: application/json" \
  -d '{
    "propertyId": 101,
    "url": "https://partner.example.com/nowistay/webhooks",
    "events": ["booking.created", "booking.updated"],
    "description": "PMS sync"
  }'

Risposta:

{
  "id": 801,
  "propertyId": 101,
  "url": "https://partner.example.com/nowistay/webhooks",
  "events": ["booking.created", "booking.updated"],
  "description": "PMS sync",
  "status": "active",
  "createdAt": "2026-05-27T16:40:00Z",
  "updatedAt": "2026-05-27T16:40:00Z",
  "signingSecret": "whsec_9npwH6J8QyZp1L8U3xqG7sN2"
}

Salva subito il signingSecret. Viene restituito una sola volta, alla creazione. Non è possibile leggerlo di nuovo dall’API. Se lo perdi, elimina il webhook e creane uno nuovo.

Passo 2. Ricevi il tuo primo evento

Ogni consegna è una richiesta HTTP POST al tuo URL, con questi header:

  • Content-Type: application/json
  • User-Agent: Nowistay-Webhooks/1.0
  • Nowistay-Webhook-Id: 801
  • Nowistay-Webhook-Event-Id: evt_LkP2sxR9zV8QyW... (univoco per evento)
  • Nowistay-Webhook-Event-Type: booking.updated
  • Nowistay-Webhook-Timestamp: 1779292800 (secondi Unix)
  • Nowistay-Webhook-Signature: v1=3a7b...e1c (HMAC-SHA256 in esadecimale)

E un corpo JSON strutturato così:

{
  "id": "evt_LkP2sxR9zV8QyW...",
  "type": "booking.updated",
  "createdAt": "2026-05-27T16:40:00Z",
  "apiVersion": "2026-05-27",
  "propertyId": 101,
  "bookingId": 9001,
  "data": {
    "booking": {
      "id": 9001,
      "propertyId": 101,
      "arrival": "2026-07-14",
      "departure": "2026-07-18",
      "status": "confirmed",
      "channel": "airbnb",
      "reservationNumber": "HMABCDE",
      "guest": { "firstName": "Maya", "lastName": "M." },
      "amounts": { "amount": "620.00", "currency": "EUR" },
      "identity": { "status": "verified" }
    }
  }
}

Il tuo endpoint deve rispondere con uno stato 2xx qualsiasi (200 o 204 sono i più comuni). Qualsiasi altra risposta, o una risposta che impiega più di 10 secondi, conta come errore e fa scattare un nuovo tentativo.

Passo 3. Verifica la firma

Verifica sempre la firma prima di fidarti del corpo. Senza verifica, chiunque indovini il tuo URL potrebbe inviare eventi falsi al tuo servizio.

Nowistay calcola la firma così:

signature = "v1=" + HMAC_SHA256(
  signing_secret,
  timestamp + "." + event_id + "." + raw_request_body
).hex()

Ricalcolala dal tuo lato usando gli header Nowistay-Webhook-Timestamp e Nowistay-Webhook-Event-Id e il corpo grezzo esatto della richiesta (non analizzare e riserializzare il JSON prima della verifica, o rischi differenze a livello di byte).

Esempio Node.js

import crypto from "node:crypto";

function verify(req, signingSecret) {
  const timestamp = req.headers["nowistay-webhook-timestamp"];
  const eventId = req.headers["nowistay-webhook-event-id"];
  const signature = req.headers["nowistay-webhook-signature"];
  const rawBody = req.rawBody; // Buffer of the raw HTTP body

  const signed = Buffer.concat([
    Buffer.from(`${timestamp}.${eventId}.`, "utf8"),
    rawBody,
  ]);
  const expected = "v1=" + crypto
    .createHmac("sha256", signingSecret)
    .update(signed)
    .digest("hex");

  const a = Buffer.from(signature);
  const b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Esempio Python

import hmac, hashlib

def verify(request, signing_secret: str) -> bool:
    timestamp = request.headers["Nowistay-Webhook-Timestamp"]
    event_id = request.headers["Nowistay-Webhook-Event-Id"]
    signature = request.headers["Nowistay-Webhook-Signature"]
    raw_body = request.body  # bytes

    signed = f"{timestamp}.{event_id}.".encode("utf-8") + raw_body
    expected = "v1=" + hmac.new(
        signing_secret.encode("utf-8"),
        signed,
        hashlib.sha256,
    ).hexdigest()

    return hmac.compare_digest(signature, expected)

Controlla anche che il timestamp sia recente (ad esempio entro gli ultimi 5 minuti) per proteggerti dagli attacchi di replay.

Cosa c’è (e cosa non c’è) nel payload

L’oggetto data.booking rispecchia ciò che restituisce GET /public/v1/bookings/{id} quando viene chiamato senza lo scope bookings.sensitive.read. Questo significa che:

  • Inclusi: date, canale, stato, importi, nome e cognome dell’ospite, adulti/bambini, parametri personalizzati della prenotazione (se attivati sul webhook).
  • Non inclusi: email, telefono, indirizzo, città, CAP, paese, lingua dell’ospite e codice della serratura intelligente. Sono considerati dati sensibili e Nowistay non li invia agli iscritti ai webhook.

Quando il tuo strumento ha bisogno di uno di questi campi sensibili, recupera il dettaglio della prenotazione dall’API con un token che ha lo scope bookings.sensitive.read. Il bookingId nell’envelope del webhook è l’input giusto per quella chiamata.

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

Nuovi tentativi e disattivazione automatica dopo 72 ore

Se il tuo endpoint restituisce uno stato diverso da 2xx, va in timeout o fallisce per qualsiasi altro motivo, Nowistay non scarta l’evento. Programma un nuovo tentativo, con backoff esponenziale e un po’ di jitter:

  • 1° tentativo: circa 1 minuto dopo l’errore.
  • 2° tentativo: circa 2 minuti dopo.
  • 3° tentativo: circa 4 minuti dopo, poi 8, 16, 32 e così via.
  • Limite: i tentativi sono distanziati al massimo di 6 ore.

Si continua finché il tuo endpoint non accetta l’evento, oppure finché il webhook non fallisce da più di 72 ore consecutive. A quel punto Nowistay imposta lo stato del webhook su disabled e smette di inviargli nuovi eventi. I contatori degli errori si azzerano a ogni consegna riuscita, quindi un incidente isolato non fa mai scattare la disattivazione automatica.

I record delle consegne vengono conservati per 30 giorni, poi eliminati.

Riattiva un webhook disattivato

Un webhook disattivato può essere riattivato dall’API quando il tuo endpoint è di nuovo in salute. Invia un PATCH con status: "active":

curl -X PATCH https://api.nowistay.com/public/v1/webhooks/801 \
  -H "Authorization: Bearer nis_access_token_example" \
  -H "Content-Type: application/json" \
  -d '{"status": "active"}'

Nowistay non rinvia gli eventi scattati mentre il webhook era disattivato. Se ne hai persi alcuni, esegui un recupero una tantum con GET /public/v1/bookings usando un filtro per data, poi riprendi il tuo flusso basato sui webhook.

Sospendi, riprendi, elimina

Puoi sospendere temporaneamente le consegne (durante una finestra di manutenzione, ad esempio) senza perdere la configurazione del webhook:

# Pause
curl -X PATCH https://api.nowistay.com/public/v1/webhooks/801 \
  -H "Authorization: Bearer nis_access_token_example" \
  -H "Content-Type: application/json" \
  -d '{"status": "paused"}'

# Resume
curl -X PATCH https://api.nowistay.com/public/v1/webhooks/801 \
  -H "Authorization: Bearer nis_access_token_example" \
  -H "Content-Type: application/json" \
  -d '{"status": "active"}'

# Delete permanently
curl -X DELETE https://api.nowistay.com/public/v1/webhooks/801 \
  -H "Authorization: Bearer nis_access_token_example"

Nella stessa chiamata puoi anche modificare url, l’elenco events o description. Cambiare l’URL non ruota il segreto di firma.

Ispeziona i tuoi webhook

# List all webhooks (optionally filtered by property)
curl "https://api.nowistay.com/public/v1/webhooks?propertyId=101" \
  -H "Authorization: Bearer nis_access_token_example"

# Read one webhook
curl https://api.nowistay.com/public/v1/webhooks/801 \
  -H "Authorization: Bearer nis_access_token_example"

La risposta dell’elenco mostra gli stessi campi della risposta di creazione, tranne il segreto di firma (viene restituito solo alla creazione).

Regole e limiti degli URL

  • URL HTTP o HTTPS assoluto, di al massimo 2048 caratteri. HTTPS obbligatorio in produzione.
  • Nessuna credenziale di basic-auth nell’URL (https://user:pass@... viene rifiutato).
  • Nessun localhost, nessun intervallo IP privato o riservato. Nowistay risolve inoltre il nome host al momento della consegna e blocca le chiamate che risolvono verso indirizzi privati.
  • Nessun reindirizzamento HTTP. Nowistay non li segue: la consegna viene contrassegnata come fallita se il tuo server reindirizza.
  • Massimo 3 webhook per alloggio, su tutte le tue app.
  • Descrizione: facoltativa, massimo 120 caratteri.

Buone pratiche

  • Verifica la firma ogni volta. È l’unica cosa che dimostra che la chiamata proviene davvero da Nowistay.
  • Sii idempotente. Lo stesso id evento può essere riconsegnato dopo un errore. Memorizza l’id del corpo e salta l’evento se l’hai già elaborato.
  • Rispondi in fretta. Conferma la chiamata con uno 2xx subito ed elabora l’evento in un job in background se è oneroso.
  • Registra gli header nei log. Conserva Nowistay-Webhook-Event-Id e l’id della richiesta nei tuoi log. Velocizza l’assistenza.
  • Considera il webhook un segnale, non la fonte di verità. Se la tua logica di business richiede campi sensibili o i dati più recenti in assoluto, chiama GET /public/v1/bookings/{id} dopo aver ricevuto l’evento.

Dove andare dopo

Pronto a mettere la tua casa vacanza in pilota automatico?

Unisciti a oltre 1.000 property manager che risparmiano ore ogni settimana con la comunicazione con gli ospiti basata sull'IA.