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, tenir ses fiches clients à jour 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 17 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

Planevia offre deux API, avec la même clé :

APIAdresse de baseSert àClé
Réservationhttps://api.planevia.ca/api/publicLire les soins et les heures libres, créer un rendez-vousFacultative
Gestionhttps://api.planevia.ca/api/v1Lire et tenir à jour clients, rendez-vous, reçus, assignations, webhooksObligatoire, avec permissions

Les sections qui suivent décrivent d’abord l’API de réservation, puis l’API de gestion. Dans l’API de réservation, 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.

Clés API

Le propriétaire de l’entreprise crée ses clés dans Réglages, Intégrations, Clés API, et coche pour chacune ce qu’elle peut faire. À la création, une fenêtre affiche la clé complète, pk_live_ suivi de 32 caractères, avec un bouton pour la copier en entier.

La clé s’envoie depuis votre serveur seulement, jamais depuis une page web, dans l’un de ces deux en-têtes :

x-api-key: pk_live_0123456789abcdef0123456789abcdef
Authorization: Bearer pk_live_0123456789abcdef0123456789abcdef

Vérifier une clé

GET /v1/me répond avec l’entreprise et les permissions de la clé. C’est le premier appel à faire.

Terminal
curl https://api.planevia.ca/api/v1/me \
  -H "x-api-key: pk_live_0123456789abcdef0123456789abcdef"

{
  "success": true,
  "data": {
    "organization": { "id": "org_1", "name": "Spa Exemple", "slug": "spa-exemple" },
    "apiKey": {
      "id": "key_1", "name": "CRM", "keyPrefix": "pk_live_01234567",
      "permissions": ["clients:read", "bookings:read"],
      "expiresAt": null, "createdAt": "2026-10-17T14:02:11.000Z"
    }
  }
}

Permissions

Chaque permission ouvre des routes de l’API de gestion. Une clé sans permission sert seulement à l’API de réservation. Cochez le minimum dont votre outil a besoin.

PermissionOuvre
clients:readGET /v1/clients GET /v1/clients/:id
clients:writePOST /v1/clients PATCH /v1/clients/:id
bookings:readGET /v1/bookings GET /v1/bookings/:id
professionals:readGET /v1/professionals
receipts:readGET /v1/receipts GET /v1/receipts/:id
assignments:readGET /v1/assignments
assignments:writePOST /v1/assignments DELETE /v1/assignments/:id
webhooks:manageGET POST PATCH DELETE sur /v1/webhooks

Les permissions d’une clé ne se modifient pas : pour en changer, créez une nouvelle clé et révoquez l’ancienne. Une clé révoquée est refusée immédiatement.

Limites d’appels

API de gestion : 120 appels par minute et par clé, toutes routes confondues.

API de réservation : ses routes sont les mêmes que celles de la page de réservation de l’entreprise et répondent aussi sans clé. La clé y relève certaines limites quand toutes les demandes partent d’un même serveur :

API de réservation, 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

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.

API de gestion

Base : https://api.planevia.ca/api/v1. La clé est obligatoire et chaque route exige sa permission. Toutes les réponses ont la même forme :

{ "success": true, "data": { … } }
{ "success": true, "data": [ … ], "pagination": { "page": 1, "limit": 50, "total": 132, "totalPages": 3 } }
{ "success": false, "error": { "code": "permission_manquante", "message": "…" } }

Formats

  • Dates sans heure (rendez-vous, naissance) : AAAA-MM-JJ.
  • Moments précis (création, modification, paiement) : ISO 8601 en UTC, par exemple 2026-10-17T14:02:11.000Z.
  • Heures de rendez-vous : HH:MM, heure locale de l’entreprise.
  • Montants : nombres en dollars canadiens, deux décimales, taxes à part (taxTPS, taxTVQ).
  • Corps des requêtes : JSON, avec l’en-tête Content-Type: application/json.

Pagination

Les listes prennent page (à partir de 1) et limit (50 par défaut, 100 au plus). Lisez les pages jusqu’à totalPages. Pour une synchronisation, gardez l’heure de votre dernier passage et demandez seulement ce qui a changé depuis avec updatedSince : les clients sont triés du plus ancien changement au plus récent.

Erreurs

HTTPcodeSignifie
401cle_manquanteAucune clé dans la requête
401cle_invalideClé inconnue, incomplète ou révoquée
401cle_expireeClé arrivée à sa date d’expiration
403permission_manquanteLa clé n’a pas la permission de cette route
403compte_inactifAbonnement de l’entreprise terminé ou suspendu
403limite_forfaitPlafond de clients du forfait atteint
400parametre_invalide donnees_invalidesParamètre ou corps mal formé ; le message nomme le champ
404introuvable route_inconnueFiche absente de cette entreprise, ou adresse inconnue
409doublonUn client a déjà ce courriel
429trop_de_requetesPlus de 120 appels dans la minute
500erreur_internePanne de notre côté : réessayez

Lisez error.code dans votre programme ; error.message est écrit pour le développeur, en français.

Clients

AppelParamètresFait
GET /v1/clientsupdatedSince email externalId isActive page limitListe les clients
GET /v1/clients/:idLit un client
POST /v1/clientsCorps JSONCrée un client
PATCH /v1/clients/:idCorps JSONModifie seulement les champs envoyés
Un client
{
  "id": "cli_1",
  "firstName": "Camille",
  "lastName": "Tremblay",
  "email": "camille@exemple.com",
  "phone": "418-555-0142",
  "phoneSecondary": null,
  "address": "12 rue des Érables", "city": "Québec", "province": "QC", "postalCode": "G1R 1A1",
  "dateOfBirth": "1990-01-02",
  "gender": "FEMME",
  "preferredLanguage": "FR",
  "smsOptIn": true,
  "acceptsMarketing": false,
  "tags": ["vip"],
  "isActive": true,
  "isVip": false,
  "externalId": "crm-42",
  "branchId": null,
  "totalVisits": 7,
  "lastVisitAt": "2026-10-02T15:00:00.000Z",
  "createdAt": "2025-03-11T13:20:00.000Z",
  "updatedAt": "2026-10-02T16:04:51.000Z"
}

Créer et modifier

Obligatoires à la création : firstName, lastName, phone. Modifiables : firstName lastName email phone phoneSecondary address city province postalCode dateOfBirth gender (HOMME, FEMME, AUTRE, NON_SPECIFIE) preferredLanguage (FR, EN) smsOptIn acceptsMarketing tags externalId. Envoyez null pour vider un champ facultatif.

POST /v1/clients
{
  "firstName": "Camille",
  "lastName": "Tremblay",
  "phone": "418-555-0142",
  "email": "camille@exemple.com",
  "dateOfBirth": "1990-01-02",
  "externalId": "crm-42"
}

La réponse 201 contient le client créé. Le courriel est unique dans l’entreprise : s’il existe déjà, la réponse est 409 doublon avec l’identifiant de la fiche existante dans error.existingId, à modifier avec PATCH. Gardez votre propre identifiant dans externalId : GET /v1/clients?externalId=crm-42 retrouve la fiche.

Un champ inconnu ou protégé est refusé et nommé dans le message, jamais ignoré en silence. Les champs en lecture seule d’une réponse (id, createdAt, totalVisits…) sont tolérés : vous pouvez renvoyer un client tel que lu, après l’avoir modifié. La suppression d’un client se fait dans Planevia.

Rendez-vous et professionnelles

AppelParamètresFait
GET /v1/bookingsfrom to status professionalId clientId updatedSince page limitListe les rendez-vous, du plus tôt au plus tard
GET /v1/bookings/:idLit un rendez-vous
GET /v1/professionalsListe les professionnelles actives

from et to sont des dates AAAA-MM-JJ, bornes comprises. status accepte une ou plusieurs valeurs séparées par des virgules : PENDING, CONFIRMED, CHECKED_IN, IN_PROGRESS, COMPLETED, NO_SHOW, CANCELLED, RESCHEDULED.

GET /v1/bookings?from=2026-10-20&to=2026-10-20&status=CONFIRMED,COMPLETED
{
  "success": true,
  "data": [
    {
      "id": "bk_1", "bookingNumber": "BK1790694045669",
      "clientId": "cli_1", "professionalId": "usr_1",
      "serviceId": "svc_1", "serviceVariantId": "var_60", "serviceName": "Massage suédois 60 min",
      "date": "2026-10-20", "startTime": "09:00", "endTime": "10:00", "duration": 60,
      "status": "CONFIRMED", "source": "ONLINE", "branchId": null,
      "subtotal": 103.00, "discountAmount": 0, "taxTPS": 5.15, "taxTVQ": 10.27,
      "total": 118.42, "depositAmount": 0,
      "createdAt": "2026-10-15T14:02:11.000Z", "updatedAt": "2026-10-15T14:02:11.000Z"
    }
  ],
  "pagination": { "page": 1, "limit": 50, "total": 1, "totalPages": 1 }
}
Une professionnelle
{ "id": "usr_1", "firstName": "Marie", "lastName": "Dubois", "email": "marie@exemple.com",
  "title": "Massothérapeute", "specialties": ["Deep tissue"], "photoUrl": null, "branchId": null }

Pour créer un rendez-vous, utilisez POST /bookings de l’API de réservation : il applique les horaires, les règles et l’acompte de l’entreprise.

Reçus

AppelParamètresFait
GET /v1/receiptsfrom to status clientId updatedSince page limitListe les reçus émis, par date de facture
GET /v1/receipts/:idLit un reçu avec ses lignes

from et to portent sur la date de facture, bornes comprises. Statuts : PENDING, PAID, PARTIAL, OVERDUE, VOID, REVERSED, REFUNDED. Les brouillons ne sont jamais exposés. Un renversement est un reçu à montants négatifs qui nomme celui qu’il annule dans reversalOfInvoiceId.

Un reçu
{
  "id": "inv_1", "invoiceNumber": "INV-2026-0020", "clientId": "cli_1",
  "bookingIds": ["bk_1"], "type": "SERVICE", "status": "PAID",
  "invoiceDate": "2026-10-20T13:05:00.000Z", "paidAt": "2026-10-20T13:06:12.000Z",
  "subtotal": 103.00, "discountAmount": 0, "taxTPS": 5.15, "taxTVQ": 10.27,
  "total": 118.42, "amountPaid": 118.42, "amountDue": 0,
  "branchId": null, "reversalOfInvoiceId": null,
  "items": [
    { "description": "Massage suédois 60 min", "quantity": 1, "unitPrice": 103.00,
      "discountAmount": 0, "total": 103.00, "serviceId": "svc_1", "productId": null, "isTaxExempt": false }
  ],
  "createdAt": "2026-10-20T13:05:00.000Z", "updatedAt": "2026-10-20T13:06:12.000Z"
}

Assignations

Une assignation relie un client à la professionnelle qui le suit. isPrimary désigne la professionnelle attitrée ; autoAssigned vaut true quand Planevia l’a créée lui-même à la confirmation d’un rendez-vous.

AppelParamètresFait
GET /v1/assignmentsclientId professionalId page limitListe les assignations
POST /v1/assignmentsclientId professionalId isPrimaryAssigne une professionnelle à un client
DELETE /v1/assignments/:idRetire l’assignation

POST se rejoue sans risque : si l’assignation existe déjà, elle est renvoyée telle quelle (200), ou mise à jour si isPrimary change. Une nouvelle professionnelle attitrée retire ce titre à l’ancienne.

Santé et journal

Aucun renseignement de santé ne sort par l’API. Ni notes de séance, ni dossier de massothérapie ou d’esthétique, ni zones de douleur, ni formulaires d’accueil, ni allergies, conditions médicales ou assurances. Les notes libres de la fiche client et les notes internes ne sortent pas non plus : on ne peut pas garantir qu’elles n’en contiennent pas.

Exception à connaître : les webhooks de rendez-vous transmettent booking.notes, la note que le client a lui-même écrite en réservant. La synchronisation externe est un cas à part : le propriétaire la branche sur sa propre plateforme et choisit ce qu’elle échange.

Journal des appels

Chaque appel fait avec une clé, sur l’une ou l’autre API, est inscrit au journal : le moment, la méthode, l’adresse appelée, le code de réponse, la durée, l’adresse IP et la clé qui l’a fait. Le propriétaire le consulte dans Réglages, Intégrations, Journaux API.

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. Avec la permission webhooks:manage, votre outil peut aussi les gérer lui-même par l’API de gestion.

É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 : dans une fenêtre à copier, ou dans la réponse de POST /v1/webhooks. Vérifiez la signature sur le corps tel que reçu, avant de le lire.

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

Toute autre technologie convient de la même façon (Java, Ruby, Go…) : calculez le HMAC SHA-256 du corps brut avec le secret, en hexadécimal minuscule, et comparez-le à l’en-tête avec une comparaison à temps constant. Lisez le corps tel quel, avant tout décodage JSON : un corps re-sérialisé ne donne plus la même signature.

Gérer par l’API

AppelCorpsFait
GET /v1/webhooksListe les webhooks
POST /v1/webhooksurl eventsCrée un webhook ; la réponse porte son secret, une seule fois
PATCH /v1/webhooks/:idurl events isActiveModifie, met en pause ou réactive
DELETE /v1/webhooks/:idSupprime le webhook et son journal

url doit être une adresse publique en https://. events est une liste prise dans le tableau des événements ci-dessus.

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.

Synchronisation externe

Ici, c’est Planevia qui appelle votre API. Le propriétaire la branche dans Réglages, Intégration externe : adresse de base, authentification (Bearer, Basic, en-tête X-API-Key ou OAuth 2) et chemins. Elle tient les fiches clients alignées dans un sens ou dans les deux.

Chemins (JSON saisi dans Planevia)
{
  "listClients": "/clients",
  "createClient": "/clients",
  "updateClient": "/clients/:id",
  "listProfessionals": "/staff",
  "listBranches": "/branches",
  "getClientNotes": "/clients/:clientId/notes",
  "createNote": "/clients/:clientId/notes",
  "listDossiers": "/clients/:clientId/records",
  "healthCheck": "/health",
  "webhookSecret": "un-long-secret-aleatoire"
}

Seuls les chemins présents sont utilisés. :id et :clientId sont remplacés par l’identifiant de la fiche chez vous. Facultatifs aussi : createBooking, createReceipt et updateReceipt, pour recevoir les rendez-vous et les reçus.

Ce que Planevia lit chez vous

GET listClients?page=1&limit=50, page après page. Pour les clients modifiés, Planevia ajoute updated_since (ISO 8601) : renvoyez seulement les fiches modifiées depuis ce moment. La réponse peut être une liste, ou un objet qui la contient :

Réponse attendue
{
  "data": [
    {
      "id": "42",
      "firstName": "Camille",
      "lastName": "Tremblay",
      "email": "camille@exemple.com",
      "phone": "418-555-0142",
      "dateOfBirth": "1990-01-02"
    }
  ],
  "pagination": { "totalPages": 3 }
}

Sont acceptés aussi une liste seule, { "results": [ … ] } et { "data": { "clients": [ … ] } }. Planevia s’arrête à pagination.totalPages, ou à la première page de moins de 50 fiches. L’identifiant est lu dans id, _id ou externalId.

Champs échangés

firstName lastName email phone phoneSecondary address city province postalCode dateOfBirth gender occupation notes. Si vos noms diffèrent, le propriétaire les relie dans l’onglet Mapping de champs, y compris pour un champ imbriqué (contact.courriel). La date de naissance s’accepte en AAAA-MM-JJ ou en ISO 8601. Un champ absent de votre réponse n’est jamais effacé dans Planevia.

Ce que Planevia vous envoie

À chaque création ou modification d’une fiche dans Planevia : PUT updateClient si la fiche a déjà un identifiant chez vous, sinon POST createClient. Le corps contient les champs reliés, sous vos noms ; sans mapping, les champs de contact (tous ceux ci-dessus sauf occupation et notes) sous leurs noms Planevia. Une fiche créée ou modifiée par l’API de gestion part de la même façon. Répondez à POST avec l’identifiant créé, dans id ou data.id : sans lui, Planevia consigne une erreur plutôt que de recréer la fiche à chaque modification.

Les notes partent vers createNote si ce chemin existe, sauf les notes privées d’une professionnelle. Les dossiers ne vont que de votre plateforme vers Planevia.

Conflits

Planevia garde la dernière version reçue de chaque fiche et compare champ par champ. Si seul votre côté a changé un champ, Planevia le prend. Si seul Planevia l’a changé, il le garde et vous l’envoie. Si les deux l’ont changé différemment :

  • en bidirectionnel, le champ n’est pas touché ; le propriétaire voit les deux valeurs et choisit. « Garder Planevia » vous renvoie la fiche, « Garder la plateforme » applique votre valeur ;
  • en import seulement, votre plateforme fait foi et sa valeur l’emporte.

Rythme et webhook

Les clients modifiés sont relus selon l’intervalle choisi par le propriétaire, de 5 à 1 440 minutes, depuis la dernière lecture réussie : une panne ne fait rien perdre. Pour une mise à jour immédiate, appelez :

POST https://api.planevia.ca/api/external-integration/webhook/{organizationId}/client-created
x-webhook-secret: un-long-secret-aleatoire

Le secret est celui de webhookSecret ; sans lui, l’appel est refusé. Planevia relit alors les clients modifiés depuis sa dernière synchronisation. Le corps de la requête n’est pas lu.

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.

  • Déplacer ou annuler un rendez-vous sans le lien du client.
  • Supprimer ou fusionner une fiche client.
  • Vendre ou gérer une carte-cadeau.
  • Recevoir un webhook de paiement, de client ou de carte-cadeau. Pour suivre les clients, interrogez GET /v1/clients?updatedSince=… à intervalle régulier.

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é.