# Webhooks Les webhooks envoient les événements à votre système au moment même où ils se produisent : vous n'avez plus besoin d'interroger l'API en boucle. ## Événements | Événement | Quand | |---|---| | `booking.created` | Une réservation a été créée (tableau de bord, import d'un canal ou API) | | `booking.updated` | Les dates, les voyageurs, les heures d'arrivée/de départ ou les coordonnées ont changé | | `booking.cancelled` | Une réservation a été annulée | | `checkin.completed` | Le voyageur a effectué (ou mis à jour) le check-in en ligne | | `payment.completed` | Taxe de séjour payée ou dépôt de garantie bloqué/payé (y compris lorsque l'équipe le marque comme payé) | | `booking.unlocked` | Toutes les étapes sont terminées — le guide de séjour est déverrouillé | | `extras.ordered` | Le voyageur a payé des extras — y compris une arrivée anticipée ou un départ tardif | | `message.sent` | Un message a été envoyé au voyageur via le canal de réservation (Airbnb, Booking.com … via Hostaway ou Channex) | | `feedback.received` | Le voyageur a noté le séjour (1–5) sur la page d'avis après une demande d'avis | | `ping` | Événement de test envoyé par le bouton **Test** | ### Contenu de `data` | Événement | Champs de `data` | |---|---| | `booking.created`, `booking.updated`, `booking.cancelled` | `bookingId`, `ref`, `source` (imports des canaux) ; `reason: "stay-times"` lorsqu'une arrivée anticipée / un départ tardif acheté a modifié les horaires | | `checkin.completed` | `bookingId`, `ref` | | `payment.completed` | `bookingId`, `leg` (`eco` ou `deposit`), `status` (`paid` ou `held`), `manual: true` lorsque l'équipe l'a marqué comme payé | | `booking.unlocked` | `bookingId` | | `extras.ordered` | `bookingId`, `leg: "extras"`, `status: "paid"` | | `message.sent` | `bookingId`, `ref`, `via` (par ex. `Hostaway`) | | `feedback.received` | `bookingId`, `ref`, `rating` (1–5) | La version lisible par machine de chaque événement — en-têtes, corps et `data` — se trouve dans la section `webhooks` du [document OpenAPI](/api/v1/openapi.json) et dans la [référence de l'API](/api/docs#webhooks). ## Ajouter un webhook **Settings → Webhooks** : saisissez une URL `https://`, cochez les événements souhaités (aucun = tous) et cliquez sur **Add webhook**. Le **secret de signature** ne s'affiche qu'une seule fois — conservez-le avec votre récepteur. ![Settings → Webhooks](/site/img/docs/d-settings-webhooks.webp "Settings → Webhooks") ## La requête `POST` avec un corps JSON : ``` { "id": "evt_…", "event": "payment.completed", "createdAt": "2026-11-18T14:03:11.000Z", "tenant": "t_…", "data": { "bookingId": "b_…", "leg": "eco", "status": "paid" } } ``` En-têtes : | En-tête | Contenu | |---|---| | `X-GP-Event` | le nom de l'événement | | `X-GP-Delivery` | identifiant unique de la livraison | | `X-GP-Timestamp` | heure Unix en secondes | | `X-GP-Signature` | `v1=` + HMAC-SHA256 hexadécimal de `.` avec votre secret de signature | ## Vérifier la signature (Node.js) ``` const crypto = require('crypto'); function verify(rawBody, headers, secret) { const ts = headers['x-gp-timestamp']; const expected = 'v1=' + crypto.createHmac('sha256', secret).update(ts + '.' + rawBody).digest('hex'); const given = String(headers['x-gp-signature'] || ''); const fresh = Math.abs(Date.now() / 1000 - Number(ts)) < 300; return fresh && given.length === expected.length && crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected)); } ``` Vérifiez toujours par rapport au corps **brut** de la requête, et rejetez les horodatages trop anciens pour bloquer les rejeux. ## Vérifier la signature (C# / .NET) ``` using System.Security.Cryptography; using System.Text; static bool Verify(string rawBody, string ts, string signature, string secret) { if (!long.TryParse(ts, out var t) || Math.Abs(DateTimeOffset.UtcNow.ToUnixTimeSeconds() - t) > 300) return false; using var h = new HMACSHA256(Encoding.UTF8.GetBytes(secret)); var hex = Convert.ToHexString(h.ComputeHash(Encoding.UTF8.GetBytes(ts + "." + rawBody))).ToLowerInvariant(); return CryptographicOperations.FixedTimeEquals(Encoding.ASCII.GetBytes("v1=" + hex), Encoding.ASCII.GetBytes(signature ?? "")); } ``` Sous .NET Framework, utilisez `BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant()` au lieu de `Convert.ToHexString`, et comparez en temps constant à l'aide d'une simple boucle. ## Vérifier la signature (PHP) ``` $expected = 'v1=' . hash_hmac('sha256', $_SERVER['HTTP_X_GP_TIMESTAMP'] . '.' . file_get_contents('php://input'), $secret); $ok = abs(time() - (int)$_SERVER['HTTP_X_GP_TIMESTAMP']) < 300 && hash_equals($expected, $_SERVER['HTTP_X_GP_SIGNATURE'] ?? ''); ``` ## Livraison et nouvelles tentatives Répondez avec n'importe quel statut 2xx dans un délai de 10 secondes. Les livraisons échouées sont relancées après 30 secondes puis après 5 minutes (trois tentatives au total). **Recent deliveries** affiche les 200 dernières tentatives avec leur résultat. - L'`id` du corps et l'en-tête `X-GP-Delivery` restent identiques à chaque nouvelle tentative — enregistrez-les et ignorez les doublons. - Les événements peuvent arriver dans le désordre ; utilisez `createdAt` et récupérez la réservation pour connaître son état actuel. - Les redirections ne sont pas suivies, et seules des adresses `https://` publiques sont appelées (pas de réseaux privés ou locaux). - Les requêtes sont envoyées avec `Content-Type: application/json` et `User-Agent: GuestPortal-Webhooks/1`. ## Questions fréquentes ### Est-ce que je reçois la réservation complète dans l'événement ? L'événement contient les identifiants et la modification. Récupérez la réservation complète avec `GET /api/v1/bookings/{id}` à l'aide d'une clé API — ainsi, aucune donnée personnelle ne transite dans le corps des webhooks. ### Les canaux peuvent-ils envoyer des webhooks à Guestbud ? Oui — c'est l'autre sens. Hostaway et Channex peuvent informer Guestbud instantanément des réservations nouvelles ou modifiées ; voir [Hostaway](/docs/connect-hostaway) et [Channex](/docs/connect-channex). Ces adresses entrantes sont secrètes, propres à chaque entreprise, et ne déclenchent qu'un nouvel import de la réservation concernée. ### Des webhooks sont-ils envoyés depuis le compte de démonstration ? Non.