Recevoir nos réservations
Quand un voyageur réserve sur Localoge, nous vous prévenons. Bloquez alors ces nuits chez vous, sans quoi vous continuerez à les proposer ailleurs.
Déclarer votre URL
Section intitulée « Déclarer votre URL »curl -X PUT https://localoge.com/api/v1/channel/webhooks \ -H "Authorization: Bearer $LOCALOGE_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://partenaire.example.com/webhooks/localoge" }'{ "success": true, "data": { "id": "…", "url": "…", "secret": "whsec_9f2c…", "events": [] } }HTTPS obligatoire. Une liste events vide veut dire tous les événements ; précisez-la pour
n’en recevoir qu’une partie.
Nous refusons (400 INVALID_PAYLOAD) un nom d’événement inconnu, plutôt que de l’ignorer : une
faute de frappe dans reservation.canceled vous priverait sinon des annulations sans un mot. Le
message d’erreur liste les noms connus. Nous refusons aussi une URL qui ne se résout pas vers une
adresse publique (localhost, 10.x, 192.168.x…) : exposez un point de réception public, ou utilisez le miroir pour vos
premiers essais.
Un abonnement vaut pour une instance : celui déclaré avec votre clé lok_test_ sur le sandbox
ne reçoit rien de la production, et inversement.
Ou depuis votre espace, sans écrire une ligne
Section intitulée « Ou depuis votre espace, sans écrire une ligne »Dans votre espace développeur, ouvrez votre application, renseignez l’URL de webhook et enregistrez : le secret s’affiche, une seule fois, au même endroit. C’est le même mécanisme, la même signature, le même comportement. À utiliser si vous voulez recevoir vos premiers événements avant d’avoir écrit votre client d’API.
L’écran vous dit si la livraison est réellement branchée, et non seulement si une adresse est enregistrée. Un bouton y régénère le secret : l’ancien meurt à la seconde où le nouveau s’affiche, déployez-le chez vous immédiatement.
Les événements
Section intitulée « Les événements »| Événement | Quand |
|---|---|
reservation.created |
Un voyageur a réservé sur Localoge. Bloquez ces nuits chez vous. |
reservation.updated |
Une réservation Localoge a changé (dates, voyageurs). |
reservation.cancelled |
Une réservation Localoge est annulée : les nuits sont libres. |
availability.updated |
L’hôte a modifié ses disponibilités directement sur Localoge. |
property.updated |
Une annonce a changé. |
property.status_changed |
Le statut d’une annonce a changé : publiée, en pause, en attente de validation. Voir Cycle de vie. |
message.received |
Un message a été écrit dans la conversation d’un séjour. Voir Messagerie. |
review.created |
Un avis a été publié sur un logement. La note est sur dix. |
payment.succeeded |
Le loyer d’une réservation Localoge est encaissé (confirmé par notre prestataire de paiement). |
payment.refunded |
Localoge a remboursé le voyageur d’une réservation Localoge, en totalité ou en partie. |
host.revoked |
Un hôte vous a retiré son autorisation : son host_ref ne vaut plus rien. Cessez de pousser ses logements. |
webhook.test |
Envoyé à la demande (POST /webhooks/test ou bouton de l’espace). Ne porte aucune donnée métier. |
Nous ne vous renvoyons jamais les réservations que vous nous avez envoyées : vous les connaissez, et vous les rejoueriez chez vous.
Tout événement inconnu de votre code doit être accepté en 2xx et ignoré. Nous ajouterons des
événements ; un 400 sur un nom nouveau ferait réessayer la livraison pendant trente heures.
Le corps
Section intitulée « Le corps »{ "event": "reservation.created", "event_key": "reservation.created:3f8a…", "created_at": "2026-08-26T14:03:11.000Z", "api_version": "v1", "environment": "production", "data": { "reservation_id": "3f8a…", "external_property_id": "PROP-4821", "check_in": "2026-09-10", "check_out": "2026-09-15", "status": "confirmed", "guests": 3, "total": 72500, "currency": "EUR", "updated_at": "2026-08-26T14:03:11.000Z" }}api_version vaut v1 pour toute la durée de vie de cette version. environment vaut sandbox
ou production : vérifiez-le, et refusez un événement de sandbox arrivé sur votre point de
réception de production.
Aucune donnée personnelle du voyageur ne vous est transmise : ni nom, ni adresse, ni téléphone.
Vous avez besoin de savoir que des nuits sont prises, pas qui dort dans le lit. Seule exception :
le texte d’un message.received, écrit par une personne.
data selon l’événement
Section intitulée « data selon l’événement »| Événement | Champs de data |
|---|---|
reservation.* |
reservation_id, external_property_id, check_in, check_out, status, guests, total (centimes), currency, updated_at |
availability.updated |
external_property_id, dates (liste de YYYY-MM-DD) |
property.updated |
external_property_id, listing_id, et status quand l’appel vient de vous |
property.status_changed |
external_property_id, listing_id, status, previous_status, reason (facultatif, par exemple archived by partner), updated_at |
message.received |
external_property_id, reservation_id (identifiant Localoge, ou null), from (guest / host), text, sent_at |
review.created |
external_property_id, reservation_id, rating (sur 10), comment, published_at |
payment.succeeded, payment.refunded |
reservation_id, external_property_id, amount (centimes : encaissé ou remboursé), reservation_total (centimes), currency, occurred_at |
host.revoked |
host_ref, revoked_at |
webhook.test |
message, sent_at |
Les paiements
Section intitulée « Les paiements »Les deux événements payment.* ne concernent que les réservations vendues sur Localoge :
jamais une réservation que vous nous avez envoyée, puisque nous n’encaissons rien sur celles-là.
Aucune donnée bancaire ne vous est transmise, seulement les montants.
{ "event": "payment.refunded", "event_key": "payment.refunded:re_3Q…", "created_at": "2026-09-29T16:20:00.000Z", "api_version": "v1", "environment": "production", "data": { "reservation_id": "3f8a…", "external_property_id": "PROP-4821", "amount": 30000, "reservation_total": 72500, "currency": "EUR", "occurred_at": "2026-09-29T16:20:00.000Z" }}event_key vaut payment.succeeded:<reservation_id> (un seul encaissement par réservation) et
payment.refunded:<identifiant du remboursement> : plusieurs remboursements partiels d’une même
réservation sont autant d’événements distincts. Un amount inférieur à reservation_total sur
un payment.refunded signale un remboursement partiel.
La référence de l’API décrit chaque charge utile champ par champ.
Vérifier la signature
Section intitulée « Vérifier la signature »L’en-tête X-Localoge-Signature porte t=<horodatage>,v1=<hmac>. Le HMAC-SHA256 est calculé sur
<horodatage>.<corps brut>, avec votre secret.
import { createHmac, timingSafeEqual } from 'node:crypto';
function signatureValide(secret, corpsBrut, entete, toleranceSec = 300) { const m = /^t=(\d+),v1=([0-9a-f]+)$/.exec(entete.trim()); if (!m) return false; const t = Number(m[1]); // L'horodatage entre dans le calcul : sans cette fenêtre, un envoi capté // resterait rejouable indéfiniment. if (Math.abs(Date.now() / 1000 - t) > toleranceSec) return false; const attendu = createHmac('sha256', secret).update(`${t}.${corpsBrut}`).digest('hex'); const a = Buffer.from(attendu, 'hex'); const b = Buffer.from(m[2], 'hex'); return a.length === b.length && timingSafeEqual(a, b);}Répondez vite, traitez après
Section intitulée « Répondez vite, traitez après »Répondez 2xx dès réception, puis traitez en tâche de fond. Nous coupons à 10 secondes.
Toute réponse hors 2xx, et toute absence de réponse, est un échec que nous réessayons.
Réessais
Section intitulée « Réessais »À 1 minute, 5, 15, 1 heure, 6 heures, puis 24 heures. Sept tentatives sur environ 30 heures, après quoi nous abandonnons et un humain est prévenu chez nous.
Déduplication
Section intitulée « Déduplication »Un réessai porte le même event_key. Utilisez-le comme clé d’unicité de votre côté : c’est ce
qui vous évite de traiter deux fois la même réservation quand notre première tentative a abouti
chez vous mais que sa réponse s’est perdue.
L’en-tête X-Localoge-Event-Key porte la même valeur, si vous préférez ne pas lire le corps.
Relire nos livraisons, et relancer
Section intitulée « Relire nos livraisons, et relancer »Chaque événement est une livraison que vous pouvez relire, avec son statut, son nombre de tentatives et la dernière réponse HTTP de votre serveur :
curl "https://localoge.com/api/v1/channel/webhooks/deliveries?status=failed&limit=50" \ -H "Authorization: Bearer $LOCALOGE_KEY"{ "success": true, "environment": "production", "data": { "deliveries": [ { "id": "d7c1…", "event": "reservation.created", "event_key": "reservation.created:3f8a…", "status": "failed", "attempts": 7, "last_http_status": 500, "last_response_ms": 812, "last_error": null, "next_attempt_at": null, "last_attempt_at": "2026-09-29T15:12:44.000Z", "delivered_at": null, "created_at": "2026-09-28T09:12:44.000Z", "payload": { "reservation_id": "3f8a…" } } ], "next_cursor": null }}Les livraisons sont triées de la plus récente à la plus ancienne, 50 par page par défaut
(200 au plus), et se parcourent avec next_cursor. status vaut pending (en attente, en cours
d’envoi ou de réessai), delivered ou failed (abandonnée après sept tentatives).
last_response_ms est le temps de réponse de votre serveur à la dernière tentative.
Une fois votre serveur réparé, remettez une livraison en file :
curl -X POST https://localoge.com/api/v1/channel/webhooks/deliveries/d7c1…/retry \ -H "Authorization: Bearer $LOCALOGE_KEY"{ "success": true, "environment": "production", "data": { "id": "d7c1…", "status": "pending" } }Elle repart avec le même event_key, compteur de tentatives remis à zéro : votre
déduplication la reconnaîtra si elle était en fait arrivée. 404 NOT_FOUND pour une livraison
inconnue, 409 CONFLICT si elle est en cours d’envoi à cet instant.
Envoyer un événement de test
Section intitulée « Envoyer un événement de test »curl -X POST https://localoge.com/api/v1/channel/webhooks/test \ -H "Authorization: Bearer $LOCALOGE_KEY"{ "success": true, "environment": "production", "data": { "event": "webhook.test", "event_key": "webhook.test:6c0e…", "status": "pending" }}Réponse 202 : l’événement est mis en file, puis part vers votre URL enregistrée,
signé comme les autres. Son data vaut { "message": "Test event from Localoge", "sent_at": "…" }.
C’est la façon la plus rapide de vérifier votre réception et votre signature sans créer de
réservation. Sans webhook actif, la réponse est 409 CONFLICT.
Le miroir
Section intitulée « Le miroir »Déclarez https://localoge.com/webhook-echo/<votre-identifiant> comme URL de réception (ou son
équivalent sur https://sandbox.localoge.com), provoquez un événement, puis relisez ce que nous
avons envoyé :
curl https://localoge.com/api/v1/channel/webhook-echo \ -H "Authorization: Bearer $LOCALOGE_KEY"Vous voyez les vingt dernières réceptions (conservées 24 heures), en-têtes compris, avec notre
verdict sur la signature. Si elle est valide chez nous et refusée chez vous, le défaut est dans
votre vérification. Pour éprouver votre refus d’une signature falsifiée, rejouez le corps reçu vers
votre serveur avec un en-tête X-Localoge-Signature modifié : il doit répondre 4xx.
Rattraper ce que vous avez manqué
Section intitulée « Rattraper ce que vous avez manqué »curl "https://localoge.com/api/v1/channel/reservations?updated_since=2026-08-25T00:00:00Z" \ -H "Authorization: Bearer $LOCALOGE_KEY"updated_since porte sur la date de modification : une réservation annulée depuis ressort
avec status: "cancelled". Voir Pagination.
Plus général encore, GET /events rend tous les événements émis pour vous, livrés ou non, du plus
ancien au plus récent, dans le même format que les webhooks (id, event, event_key,
created_at, data). Son filtre de date s’appelle since (date de création de l’événement) :
curl "https://localoge.com/api/v1/channel/events?since=2026-09-28T00:00:00Z&limit=200" \ -H "Authorization: Bearer $LOCALOGE_KEY"À appeler après une panne de votre côté, ou une fois par jour par sécurité. Les webhooks sont le chemin rapide, celui-ci est le filet.