Développeurs

Brancher vos outils sur Planevia

Un site, un CRM ou un outil d’automatisation peut lire l’agenda d’une entreprise, y créer des rendez-vous et être prévenu en temps réel de ce qui s’y passe. L’API et les webhooks sont inclus dans tous les forfaits, essai compris.

Version du 15 octobre 2026. Les réponses sont en JSON, les dates en AAAA-MM-JJ et les heures en heure locale de l’entreprise.

Démarrer

Toutes les adresses partent de la même base :

https://api.planevia.ca/api/public

Chaque appel nomme l’entreprise par l’identifiant de sa page de réservation, celui qu’on lit dans son adresse planevia.ca/mon-spa : ?org=mon-spa en lecture, "organizationSlug": "mon-spa" dans le corps d’une création. Chaque réponse a la même forme :

{ "success": true, "data": { … } }
{ "success": false, "message": "Texte à montrer au client" }

Une réservation se fait en trois temps :

  1. lire les soins et les professionnelles qui les donnent ;
  2. lire les heures libres ;
  3. créer le rendez-vous.

Accès et limites

Le propriétaire de l’entreprise crée ses clés dans Réglages, Intégrations, Clés API. Une clé s’envoie dans l’en-tête x-api-key, depuis votre serveur seulement : elle ne doit jamais apparaître dans une page web. Elle est conservée sous forme d’empreinte et se révoque à tout moment.

Les routes de réservation sont publiques

Ce sont les mêmes que celles de la page de réservation de l’entreprise : elles répondent aussi sans clé. Aujourd’hui, la clé relève les limites d’appels quand toutes les demandes partent d’un même serveur, et trace votre utilisation. Elle n’ouvre pas d’accès supplémentaire.

Limites d’appels, par heure

AppelSans cléAvec clé
Création de rendez-vous (solo, duo, groupe)20 par adresse IP20 par adresse IP
Heures libres duo et groupe, lien du client150 par adresse IP150 par adresse IP
Vérification d’une carte-cadeau20 par adresse IP300 par clé
Options de paiement150 par adresse IP3 000 par clé

Au-delà, la réponse est 429 et les en-têtes RateLimit-* indiquent quand réessayer. Si votre intégration crée plus de 20 rendez-vous à l’heure depuis un même serveur, écrivez-nous avant la mise en service.

Soins et disponibilités

AppelParamètresRenvoie
GET /servicesorgCatégories et soins réservables en ligne : id, nom, durée, prix, déclinaisons
GET /services/:slugorgLe détail d’un soin
GET /packagesorgLes forfaits vendus en ligne
GET /professionals/by-serviceorg serviceIdLes professionnelles qui donnent ce soin, avec leur prix si elles en ont un
GET /available-slotsorg therapistId date duration serviceIdLes heures libres de la professionnelle ce jour-là, ou la raison s’il n’y en a pas
GET /month-availabilityorg therapistId month duration serviceIdLes jours du mois qui ont au moins une heure libre
GET /next-availabilityorg therapistId duration serviceIdLe premier jour libre dans les 90 prochains jours
GET /branchesorgLes succursales d’une entreprise à plusieurs adresses
Les heures libres d’une professionnelle
GET /available-slots?org=mon-spa&therapistId=usr_1&date=2026-10-20&duration=60&serviceId=svc_1

{
  "success": true,
  "data": {
    "date": "2026-10-20",
    "isBlocked": false,
    "slots": ["09:00", "09:15", "10:30"]
  }
}

Les heures tiennent compte de l’horaire de chaque professionnelle, de ses congés, des rendez-vous déjà pris et des règles de réservation de l’entreprise. Un jour sans horaire répond isBlocked: true avec la raison.

Créer un rendez-vous

POST /bookings
{
  "organizationSlug": "mon-spa",
  "serviceId": "svc_1",
  "serviceVariationId": "var_60",
  "therapistId": "usr_1",
  "date": "2026-10-20",
  "startTime": "09:00",
  "firstName": "Camille",
  "lastName": "Tremblay",
  "email": "camille@exemple.com",
  "phone": "418-555-0142",
  "clientNotes": "Première visite",
  "emailReminder": true,
  "smsReminder": true
}

Facultatifs : branchId (succursale), promoCode, giftCardCode (carte-cadeau à appliquer), packageId (forfait). Le client est retrouvé par son courriel ou son téléphone, ou créé s’il est nouveau. Il reçoit sa confirmation et ses rappels selon les réglages de l’entreprise.

La réponse 201 contient le rendez-vous dans data.booking, confirmé d’office ou en attente de confirmation par l’équipe, selon les réglages de l’entreprise.

Acompte et paiement

Si l’entreprise exige un paiement à la réservation, la réponse porte paymentRequired: true, le montant (paymentAmount, isDeposit), le lien de paiement Stripe paymentUrl et son délai paymentExpiresInMinutes. Le lien part aussi au client par courriel ou par texto. Le rendez-vous se confirme tout seul au paiement et s’annule à l’expiration du délai.

Refus

CodeSignifieÀ faire
400Champ manquant ou mal forméCorriger la demande
404Entreprise, soin ou professionnelle introuvableRelire les soins et les professionnelles
409L’heure vient d’être priseRelire les heures libres et proposer une autre heure
429Trop de demandesAttendre le délai indiqué

Le champ message est écrit pour être montré tel quel au client.

Duo et groupe

POST /bookings/duo réserve deux personnes à la même heure, avec deux professionnelles et une salle. Ses heures libres se lisent avec GET /duo/slots et GET /duo/month-availability.

POST /bookings/groupe pose plusieurs rendez-vous en une seule opération, à la même heure ou non, avec un soin différent par personne si besoin. Le nombre maximal suit le nombre de professionnelles réservables en ligne, jusqu’à 20.

POST /bookings/groupe
{
  "organizationSlug": "mon-spa",
  "contact": {
    "firstName": "Camille", "lastName": "Tremblay",
    "email": "camille@exemple.com", "phone": "418-555-0142"
  },
  "items": [
    { "serviceId": "svc_massage", "serviceVariationId": "var_50",
      "date": "2026-10-20", "startTime": "09:00" },
    { "serviceId": "svc_soin_visage", "date": "2026-10-20", "startTime": "09:00",
      "therapistId": "usr_2",
      "participant": { "firstName": "Olivier", "lastName": "Gagnon" } }
  ]
}

Avant d’appeler, GET /groupe/slots donne pour un soin et une date les heures libres et qui est libre à chacune ; GET /groupe/month-availability donne les jours possibles. L’acompte se compte par rendez-vous et se paie en un seul lien. Les cartes-cadeaux ne s’appliquent pas au duo ni au groupe.

Déplacer ou annuler

Avec sa confirmation, le client reçoit un lien signé qui lui donne la main sur son rendez-vous, sans compte. Ces appels prennent le jeton de ce lien et appliquent les règles d’annulation de l’entreprise.

AppelFait
GET /bookings/:tokenLe rendez-vous et ce qui est permis
GET /bookings/:token/servicesLes soins vers lesquels il peut être changé
GET /bookings/:token/slotsLes heures libres pour le déplacer
POST /bookings/:token/rescheduleDéplace le rendez-vous
POST /bookings/:token/cancelAnnule le rendez-vous
POST /bookings/retrouverRenvoie son lien au client qui l’a perdu

Un système externe ne peut pas encore déplacer ou annuler seul

La réponse de création ne renvoie pas ce jeton. Le déplacement et l’annulation se font donc par le client, avec son lien, ou par l’équipe dans Planevia. Chacun est annoncé par webhook.

Cartes-cadeaux

POST /gift-card-check
{ "organizationSlug": "mon-spa", "code": "XXXX-XXXX" }

La réponse dit si le code existe et ce qu’il couvre : un solde, ou un soin d’un forfait. Le code s’applique ensuite à un rendez-vous solo par giftCardCode. La vente et la gestion des cartes-cadeaux se font dans Planevia.

Webhooks

Planevia prévient votre système à chaque étape d’un rendez-vous, quelle qu’en soit l’origine : en ligne, au comptoir, au téléphone ou par la réceptionniste IA. Le propriétaire déclare l’adresse à appeler et les événements voulus dans Réglages, Intégrations, Webhooks, avec un bouton pour tester et le journal de chaque envoi.

ÉvénementQuand
booking.createdRendez-vous créé, en attente de confirmation
booking.confirmedRendez-vous confirmé
booking.rescheduledRendez-vous déplacé
booking.cancelledRendez-vous annulé
booking.completedRendez-vous terminé
booking.no_showClient absent
booking.reminder_dueMoment d’envoyer le rappel
booking.review_dueMoment de demander un avis
Corps envoyé
{
  "event": "booking.confirmed",
  "sentAt": "2026-10-15T14:02:11.000Z",
  "data": {
    "booking": {
      "id": "…", "number": "BK…", "status": "CONFIRMED", "source": "ONLINE",
      "date": "2026-10-20", "startTime": "09:00", "endTime": "10:00",
      "duration": 60, "total": 118.42, "notes": "Première visite"
    },
    "client": {
      "id": "…", "firstName": "Camille", "lastName": "Tremblay",
      "email": "camille@exemple.com", "phone": "+14185550142"
    },
    "service": { "id": "svc_1", "name": "Massage suédois 60 min" },
    "professional": { "id": "usr_1", "firstName": "Marie", "lastName": "Dubois" }
  }
}

Signature

Chaque envoi porte l’en-tête X-Planevia-Event et l’en-tête X-Planevia-Signature : le HMAC SHA-256 du corps brut, en hexadécimal, calculé avec le secret du webhook. Ce secret commence par whsec_ et n’est affiché qu’une fois, à la création. Vérifiez la signature sur le corps tel que reçu, avant de le lire.

Node.js et Express
import crypto from 'node:crypto';
import express from 'express';

const app = express();

app.post('/planevia', express.raw({ type: 'application/json' }), (req, res) => {
  const attendue = crypto
    .createHmac('sha256', process.env.PLANEVIA_WEBHOOK_SECRET)
    .update(req.body)
    .digest('hex');
  const recue = req.get('X-Planevia-Signature') || '';

  const valide = recue.length === attendue.length &&
    crypto.timingSafeEqual(Buffer.from(recue), Buffer.from(attendue));
  if (!valide) return res.sendStatus(401);

  const { event, data } = JSON.parse(req.body.toString('utf8'));
  // … traiter l'événement
  res.sendStatus(200);
});

Livraison

Toute réponse 2xx dans les 10 secondes vaut réception. Sinon, Planevia réessaie deux fois, après 2 puis 10 secondes. Répondez vite et traitez ensuite : un même événement peut arriver deux fois si votre réponse se perd, alors servez-vous de data.booking.id et de event pour ignorer un doublon.

Messagerie externe

Si votre CRM, GoHighLevel par exemple, écrit lui-même aux clients, le propriétaire passe Planevia en mode « messagerie externe ». Planevia n’envoie plus ses confirmations, modifications, annulations, rappels ni demandes d’avis : votre CRM les envoie à la réception des événements correspondants.

Filet de sécurité : si votre webhook échoue plusieurs fois de suite, Planevia reprend lui-même l’envoi, pour qu’aucun client ne reste sans message.

Ce qui n’existe pas encore

Ces besoins se font aujourd’hui dans Planevia, pas par l’API. Dites-nous lesquels comptent pour vous.

  • Lire, créer ou modifier une fiche client.
  • Déplacer ou annuler un rendez-vous sans le lien du client.
  • Lire les paiements, les factures et les reçus.
  • Vendre ou gérer une carte-cadeau.
  • Recevoir un webhook de paiement, de client ou de carte-cadeau.
  • Envoyer des changements d’un CRM vers Planevia.

Tester

Il n’y a pas d’environnement de test séparé : l’essai gratuit de 14 jours en tient lieu. Créez-y des soins, des professionnelles et des clients fictifs, une clé API et un webhook, puis appelez l’API avec l’identifiant de ce compte. Tant que le compte Stripe de l’essai n’est pas branché, aucun paiement en ligne n’est demandé.

Une question technique : support@planevia.ca. Ce que deviennent les données que vous recevez et envoyez : politique de confidentialité.