Openbare API: ontvang boekingswebhooks in realtime

Bijgewerkt

Webhooks zijn de snelste manier om synchroon te blijven met Nowistay. In plaats van om de paar minuten de API te bevragen om nieuwe of gewijzigde reserveringen op te sporen, geef je Nowistay aan waarheen de events moeten worden gestuurd, en je service ontvangt ze zodra ze gebeuren. Dit artikel laat zien hoe je een webhook aanmaakt, de handtekening controleert en nieuwe pogingen afhandelt.

Een korte herinnering: wat de openbare API is

Met de openbare REST-API van Nowistay kunnen je tools en partners via HTTPS gegevens van Nowistay lezen en schrijven. Je authenticeert met OAuth2 (authorization code + PKCE), ontvangt een toegangstoken en roept endpoints aan onder /public/v1/*. Webhooks zijn het push-equivalent: dezelfde gegevens, dezelfde vorm als GET /public/v1/bookings/{id}, maar Nowistay stuurt ze naar jou wanneer er iets verandert.

Heb je OAuth nog niet ingesteld, begin dan met de hoofdgids: De openbare REST-API van Nowistay gebruiken. Die behandelt de registratie, de toestemmingsprocedure, scopes en hoe je aanroepen test in de playground van de documentatie.

Wat je krijgt

  • Levering in realtime van de events booking.created en booking.updated.
  • Dezelfde JSON-body als GET /public/v1/bookings/{id}, inclusief data, kanaal, status, bedragen, velden die voor opdrachten relevant zijn en je eigen reserveringsparameters.
  • HMAC-SHA256-handtekening, zodat je kunt controleren dat elk verzoek echt van Nowistay komt.
  • Automatische nieuwe pogingen met exponentiële backoff wanneer je endpoint faalt of een time-out geeft.
  • Automatisch uitschakelen na 72 uur aaneengesloten mislukkingen (je kunt het via de API opnieuw activeren).

Voordat je begint

  • Je hebt het AI Channel Manager-abonnement op de accommodatie nodig. Zonder dat worden leveringen overgeslagen.
  • Het OAuth-token waarmee je de webhook aanmaakt, moet de scope webhooks.write hebben. POST /public/v1/webhooks vereist ook bookings.read, omdat de payload een volledig reserveringsdetail is.
  • Je ontvangende URL moet bereikbaar zijn op het openbare internet, in productie via HTTPS worden aangeboden en binnen 10 seconden antwoorden met een 2xx-status.
  • Eén webhook hoort bij één accommodatie. Je kunt maximaal 3 webhooks per accommodatie registreren.

Stap 1. Een webhook aanmaken

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

Antwoord:

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

Bewaar de signingSecret nu. Hij wordt maar één keer teruggegeven, bij het aanmaken. Je kunt hem niet opnieuw uit de API lezen. Raak je hem kwijt, verwijder dan de webhook en maak een nieuwe aan.

Stap 2. Je eerste event ontvangen

Elke levering is een HTTP POST naar je URL, met deze headers:

  • Content-Type: application/json
  • User-Agent: Nowistay-Webhooks/1.0
  • Nowistay-Webhook-Id: 801
  • Nowistay-Webhook-Event-Id: evt_LkP2sxR9zV8QyW... (uniek per event)
  • Nowistay-Webhook-Event-Type: booking.updated
  • Nowistay-Webhook-Timestamp: 1779292800 (Unix-seconden)
  • Nowistay-Webhook-Signature: v1=3a7b...e1c (HMAC-SHA256, hex)

En een JSON-body die er zo uitziet:

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

Je endpoint moet antwoorden met een willekeurige 2xx-status (200 of 204 zijn gebruikelijk). Al het andere, of een antwoord dat langer dan 10 seconden duurt, telt als een mislukking en leidt tot een nieuwe poging.

Stap 3. De handtekening controleren

Controleer altijd de handtekening voordat je de body vertrouwt. Zonder controle kan iedereen die je URL raadt, nep-events naar je service sturen.

Nowistay berekent de handtekening als volgt:

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

Bereken hem aan jouw kant opnieuw met de headers Nowistay-Webhook-Timestamp en Nowistay-Webhook-Event-Id en de exacte ruwe request-body (parseer en serialiseer de JSON niet opnieuw voordat je controleert, anders loop je het risico op verschillen in bytes).

Voorbeeld in 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);
}

Voorbeeld in 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)

Controleer ook of de timestamp recent is (bijvoorbeeld binnen de laatste 5 minuten) als bescherming tegen replay-aanvallen.

Wat wel en niet in de payload zit

Het object data.booking weerspiegelt wat GET /public/v1/bookings/{id} teruggeeft wanneer het wordt aangeroepen zonder de scope bookings.sensitive.read. Dat betekent:

  • Inbegrepen: data, kanaal, status, bedragen, voor- en achternaam van de gast, volwassenen en kinderen, eigen reserveringsparameters (indien ingeschakeld op de webhook).
  • Niet inbegrepen: e-mailadres, telefoonnummer, adres, plaats, postcode, land en taal van de gast en de code van het slimme slot. Die worden als gevoelig beschouwd en Nowistay pusht ze niet naar webhook-abonnees.

Heeft je tool een van die gevoelige velden nodig, haal dan het reserveringsdetail uit de API op met een token met de scope bookings.sensitive.read. De bookingId in de envelop van de webhook is de juiste invoer voor die aanroep.

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

Nieuwe pogingen en het automatisch uitschakelen na 72 uur

Als je endpoint een niet-2xx-status teruggeeft, een time-out geeft of om een andere reden faalt, laat Nowistay het event niet vallen. Het plant een nieuwe poging in, met exponentiële backoff en een beetje jitter:

  • 1e nieuwe poging: ongeveer 1 minuut na de mislukking.
  • 2e nieuwe poging: ongeveer 2 minuten later.
  • 3e nieuwe poging: ongeveer 4 minuten later, daarna 8, 16, 32 enzovoort.
  • Maximum: nieuwe pogingen liggen hoogstens 6 uur uit elkaar.

Dit gaat door tot je endpoint het event accepteert, of tot de webhook meer dan 72 uur aaneen faalt. Op dat moment zet Nowistay de status van de webhook op disabled en stopt het met het sturen van nieuwe events. De foutentellers worden gereset telkens wanneer een levering slaagt, dus een op zichzelf staand incident leidt nooit tot automatisch uitschakelen.

Leveringsgegevens worden 30 dagen bewaard en daarna opgeruimd.

Een uitgeschakelde webhook opnieuw activeren

Een uitgeschakelde webhook kun je via de API weer inschakelen zodra je endpoint weer gezond is. Stuur een PATCH met 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 speelt de events die zijn afgegaan terwijl de webhook was uitgeschakeld niet opnieuw af. Heb je er een paar gemist, doe dan eenmalig een backfill met GET /public/v1/bookings met een datumfilter en hervat daarna je flow op basis van webhooks.

Pauzeren, hervatten, verwijderen

Je kunt leveringen tijdelijk pauzeren (bijvoorbeeld tijdens een onderhoudsvenster) zonder de configuratie van de webhook te verliezen:

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

Je kunt in dezelfde aanroep ook de url, de lijst events of de description aanpassen. Het wijzigen van de URL roteert het ondertekeningsgeheim niet.

Je webhooks bekijken

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

Het antwoord van de lijst toont dezelfde velden als het antwoord bij het aanmaken, min het ondertekeningsgeheim (dat wordt alleen bij het aanmaken teruggegeven).

Regels en limieten voor URL’s

  • Absolute HTTP- of HTTPS-URL van maximaal 2048 tekens. HTTPS is verplicht in productie.
  • Geen basic-auth-gegevens in de URL (https://user:pass@... wordt geweigerd).
  • Geen localhost, geen privé- of gereserveerde IP-bereiken. Nowistay lost de hostnaam ook op het moment van levering op en blokkeert aanroepen die naar privéadressen verwijzen.
  • Geen HTTP-redirects. Nowistay volgt ze niet; de levering wordt als mislukt gemarkeerd als je server doorverwijst.
  • Maximaal 3 webhooks per accommodatie, over al je apps heen.
  • Beschrijving: optioneel, maximaal 120 tekens.

Goede werkwijzen

  • Controleer de handtekening elke keer. Het is het enige wat bewijst dat de aanroep echt van Nowistay komt.
  • Wees idempotent. Hetzelfde event-id kan na een mislukking opnieuw worden afgeleverd. Sla het id uit de body op en sla het event over als je het al hebt verwerkt.
  • Antwoord snel. Bevestig de aanroep meteen met een 2xx en verwerk het event in een achtergrondtaak als het zwaar is.
  • Log de headers. Bewaar Nowistay-Webhook-Event-Id en het request-id in je logs. Dat versnelt de support.
  • Behandel de webhook als een hint, niet als de bron van waarheid. Heeft je bedrijfslogica gevoelige velden of de allernieuwste gegevens nodig, roep dan na ontvangst van het event GET /public/v1/bookings/{id} aan.

Waar nu heen

Klaar om je verhuur op de automatische piloot te zetten?

Sluit je aan bij meer dan 1.000 verhuurbeheerders die elke week uren besparen met AI-gestuurde gastcommunicatie.