Lokyzen · Développeurs

API REST v1

Connectez vos outils à Lokyzen : logements, disponibilités, devis, réservations, voyageurs et webhooks. Spécification complète au format OpenAPI 3.1 : /api/v1/openapi.json.

L’essentiel

Points d’accès

Connexion

get/api/v1/metoute clé valide

Organisation et droits de la clé (test de connexion des connecteurs)

Logements

get/api/v1/propertiesproperties:read

Logements et types de logement

Disponibilités

get/api/v1/room-types/{id}/availabilityproperties:read

Disponibilités, prix et restrictions par nuit (400 jours au plus)

ParamètreEmplacementTypeDescription
id *cheminuuid
from *requêtestringDate AAAA-MM-JJ
to *requêtestringDate AAAA-MM-JJ

Devis

post/api/v1/quotesreservations:read

Devis exact (mêmes calculs que le back-office) et disponibilité

ParamètreEmplacementTypeDescription
Idempotency-Keyen-têtestringRejouer la requête avec la même clé renvoie la même réponse sans doublon (24 h)
Corps de la requête (JSON Schema)
{
  "type": "object",
  "properties": {
    "roomTypeId": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    },
    "checkIn": {
      "type": "string",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
      "description": "Date AAAA-MM-JJ"
    },
    "checkOut": {
      "type": "string",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
      "description": "Date AAAA-MM-JJ"
    },
    "adults": {
      "type": "integer",
      "minimum": 1,
      "maximum": 50
    },
    "children": {
      "default": 0,
      "type": "integer",
      "minimum": 0,
      "maximum": 50
    },
    "promoCode": {
      "anyOf": [
        {
          "type": "string",
          "maxLength": 30
        },
        {
          "type": "null"
        }
      ]
    }
  },
  "required": [
    "roomTypeId",
    "checkIn",
    "checkOut",
    "adults"
  ]
}

Réservations

get/api/v1/reservationsreservations:read

Réservations (pagination par curseur, filtre de période, synchronisation incrémentale)

ParamètreEmplacementTypeDescription
fromrequêtestringSéjours qui se terminent après cette date
torequêtestringSéjours qui commencent avant cette date
statusrequêteinquiry | option | confirmed | cancelled | no_show | completed
updatedSincerequêtedate-timeModifiées depuis (ISO 8601) : synchronisation incrémentale
cursorrequêteuuid
limitrequêteinteger
post/api/v1/reservationsreservations:write

Créer une réservation (prix calculé par Lokyzen, restrictions de séjour appliquées)

ParamètreEmplacementTypeDescription
Idempotency-Keyen-têtestringRejouer la requête avec la même clé renvoie la même réponse sans doublon (24 h)
Corps de la requête (JSON Schema)
{
  "type": "object",
  "properties": {
    "roomTypeId": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    },
    "checkIn": {
      "type": "string",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
      "description": "Date AAAA-MM-JJ"
    },
    "checkOut": {
      "type": "string",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
      "description": "Date AAAA-MM-JJ"
    },
    "adults": {
      "type": "integer",
      "minimum": 1,
      "maximum": 50
    },
    "children": {
      "default": 0,
      "type": "integer",
      "minimum": 0,
      "maximum": 50
    },
    "status": {
      "default": "confirmed",
      "type": "string",
      "enum": [
        "option",
        "confirmed"
      ]
    },
    "externalId": {
      "description": "Identifiant dans votre système (unique)",
      "anyOf": [
        {
          "type": "string",
          "maxLength": 100
        },
        {
          "type": "null"
        }
      ]
    },
    "notes": {
      "anyOf": [
        {
          "type": "string",
          "maxLength": 5000
        },
        {
          "type": "null"
        }
      ]
    },
    "guest": {
      "type": "object",
      "properties": {
        "firstName": {
          "type": "string",
          "minLength": 1,
          "maxLength": 80
        },
        "lastName": {
          "type": "string",
          "minLength": 1,
          "maxLength": 80
        },
        "email": {
          "anyOf": [
            {
              "type": "string",
              "format": "email",
              "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
            },
            {
              "type": "null"
            }
          ]
        },
        "phone": {
          "anyOf": [
            {
              "type": "string",
              "maxLength": 30
            },
            {
              "type": "null"
            }
          ]
        },
        "locale": {
          "default": "fr",
          "type": "string",
          "enum": [
            "fr",
            "en",
            "es",
            "de",
            "it",
            "pt"
          ]
        },
        "country": {
          "anyOf": [
            {
              "type": "string",
              "minLength": 2,
              "maxLength": 2
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "required": [
        "firstName",
        "lastName"
      ]
    }
  },
  "required": [
    "roomTypeId",
    "checkIn",
    "checkOut",
    "adults",
    "guest"
  ]
}
get/api/v1/reservations/{id}reservations:read

Une réservation

ParamètreEmplacementTypeDescription
id *cheminuuid
patch/api/v1/reservations/{id}reservations:write

Changer le statut (confirmer, annuler…) ; le remboursement éventuel n’est jamais exécuté automatiquement

ParamètreEmplacementTypeDescription
id *cheminuuid
Idempotency-Keyen-têtestringRejouer la requête avec la même clé renvoie la même réponse sans doublon (24 h)
Corps de la requête (JSON Schema)
{
  "type": "object",
  "properties": {
    "status": {
      "type": "string",
      "enum": [
        "confirmed",
        "cancelled",
        "no_show",
        "completed"
      ]
    },
    "reason": {
      "anyOf": [
        {
          "type": "string",
          "maxLength": 500
        },
        {
          "type": "null"
        }
      ]
    }
  },
  "required": [
    "status"
  ]
}

Voyageurs

get/api/v1/guestsguests:read

Voyageurs (recherche par e-mail)

ParamètreEmplacementTypeDescription
emailrequêtestring
cursorrequêteuuid
limitrequêteinteger
get/api/v1/guests/{id}guests:read

Un voyageur

ParamètreEmplacementTypeDescription
id *cheminuuid

Webhooks (REST hooks)

post/api/v1/webhookswebhooks:write

S’abonner à des événements (REST hooks Zapier / Make) ; le secret de signature n’est renvoyé qu’ici

ParamètreEmplacementTypeDescription
Idempotency-Keyen-têtestringRejouer la requête avec la même clé renvoie la même réponse sans doublon (24 h)
Corps de la requête (JSON Schema)
{
  "type": "object",
  "properties": {
    "url": {
      "type": "string",
      "format": "uri",
      "description": "URL https:// qui recevra les événements (POST JSON signé)"
    },
    "events": {
      "minItems": 1,
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "*",
          "reservation.created",
          "reservation.confirmed",
          "reservation.updated",
          "reservation.cancelled",
          "payment.received",
          "guest.created",
          "message.received",
          "review.received",
          "task.completed",
          "reservation.*"
        ]
      }
    },
    "description": {
      "anyOf": [
        {
          "type": "string",
          "maxLength": 200
        },
        {
          "type": "null"
        }
      ]
    },
    "source": {
      "default": "manual",
      "type": "string",
      "enum": [
        "zapier",
        "make",
        "manual"
      ]
    }
  },
  "required": [
    "url",
    "events"
  ]
}
delete/api/v1/webhooks/{id}webhooks:write

Se désabonner

ParamètreEmplacementTypeDescription
id *cheminuuid

Exemple : créer une réservation

Le prix est calculé par Lokyzen (mêmes règles que le back-office) ; obtenez roomTypeId via GET /properties et vérifiez le tarif avec POST /quotes.

curl -X POST https://staging.lokyzen.com/api/v1/reservations \
  -H "Authorization: Bearer hl_xxxxxxxx_votre_cle" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 3f1c2b9e-commande-1234" \
  -d '{
    "roomTypeId": "0190a1b2-…",
    "checkIn": "2027-07-14",
    "checkOut": "2027-07-21",
    "adults": 2,
    "status": "confirmed",
    "externalId": "CMD-1234",
    "guest": { "firstName": "Camille", "lastName": "Martin", "email": "camille@example.com", "locale": "fr" }
  }'

Webhooks

Créez un abonnement dans Lokyzen (Intégrations › Webhooks) ou par l’API (POST /api/v1/webhooks, utilisé par les REST hooks Zapier et Make). Chaque événement est envoyé en POST JSON. Abonnez-vous à des événements précis, à une famille (reservation.*) ou à tous (*).

Enveloppe

{
  "id": "0190b3c4-…",
  "type": "reservation.created",
  "createdAt": "2027-07-01T09:30:00.000Z",
  "data": { … la ressource concernée (réservation, paiement, voyageur…) }
}

Signature

En-tête Lokyzen-Signature: t=<unix>,v1=<signature>, où la signature est le HMAC-SHA256 (hexadécimal) de la chaîne t.corps calculé avec le secret de l’abonnement (whsec_…). Vérifiez-la sur le corps brut et refusez les horodatages trop anciens :

import { createHmac, timingSafeEqual } from 'node:crypto'

// body : corps brut de la requête (chaîne), avant JSON.parse
export function verifyLokyzen(body, header, secret, toleranceSec = 300) {
  const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')))
  const t = Number(parts.t)
  if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false
  const expected = createHmac('sha256', secret).update(`${t}.${body}`).digest('hex')
  const given = Buffer.from(parts.v1 ?? '', 'hex')
  const wanted = Buffer.from(expected, 'hex')
  return given.length === wanted.length && timingSafeEqual(given, wanted)
}

Livraison et nouvelles tentatives