Calendly
Calendly est un outil de prise de rendez-vous en ligne largement utilisé, mais sa fonction de rappel par SMS intégrée ne prend en charge que certains pays et régions, laissant des marchés comme l'Asie-Pacifique, l'Asie du Sud-Est et le Moyen-Orient avec une couverture limitée. En associant les Webhooks Calendly à EngageLab SMS, vous pouvez envoyer des notifications SMS vers n'importe quel numéro dans le monde lors de la création ou de l'annulation d'un rendez-vous, ainsi qu'avant le début d'une réunion, comblant ainsi les lacunes géographiques des capacités natives de Calendly.
Prérequis
Avant de commencer, assurez-vous que les configurations suivantes sont terminées :
Côté EngageLab
- Le service EngageLab SMS est activé
- Des modèles SMS ont été créés et validés sur la page de gestion des modèles, et les ID de modèle ont été obtenus
- Une clé API a été créée sur la page des clés API, et les
dev_keyetdev_secretont été obtenus
Côté Calendly
- Vous disposez d'un forfait Calendly Standard ou supérieur (la fonctionnalité Webhook nécessite un forfait payant)
- Un Personal Access Token a été créé sur la page Integrations & apps → API and webhooks
Côté serveur
- Vous disposez d'un serveur accessible publiquement et configuré avec un certificat HTTPS valide
- Pour le développement et le débogage en local, vous pouvez utiliser ngrok pour exposer temporairement un port local
Étape 1 : Préparer les modèles SMS
L'appel de l'API pour envoyer des SMS nécessite des modèles pré-validés ; la transmission directe de texte personnalisé n'est pas prise en charge.
Connectez-vous à la console EngageLab, accédez à SMS → Gestion des modèles et créez les trois modèles suivants selon le scénario :
| Usage du modèle | Exemple de contenu de modèle |
|---|---|
| Confirmation de rendez-vous | Bonjour {{name}}, votre réunion est confirmée pour le {{time}}. Au plaisir de vous rencontrer. |
| Notification d'annulation | Bonjour {{name}}, votre rendez-vous de réunion a été annulé. Contactez-nous si vous souhaitez le reprogrammer. |
| Rappel de réunion | Bonjour {{name}}, vous avez une réunion qui commence dans {{advance}}, le {{time}}. Merci de vous préparer à l'avance. |
Après avoir soumis les modèles, attendez leur validation et notez les trois ID de modèle.
Recommandation : créer un modèle indépendant pour chaque scénario rend la sémantique plus claire et augmente le taux de validation. Si un modèle contient des variables personnalisées (comme
{{name}}), vous devez transmettre les valeurs via le champparamslors de l'appel à l'API, sinon les variables seront envoyées telles quelles.
Étape 2 : Créer un abonnement Webhook dans Calendly
La gestion des Webhooks de Calendly ne dispose pas d'interface visuelle et doit se faire via l'API.
Obtenir l'URI de l'organisation
Appelez d'abord l'interface suivante pour obtenir l'URI de l'organisation du compte actuel :
curl https://api.calendly.com/users/me \
-H "Authorization: Bearer YOUR_PERSONAL_ACCESS_TOKEN"
Repérez le champ current_organization dans la réponse, au format suivant :
https://api.calendly.com/organizations/xxxxxxxxxxxxxxxx
Créer l'abonnement Webhook
Utilisez l'URI de l'organisation pour créer un Webhook qui s'abonne aux événements de création et d'annulation de rendez-vous :
curl -X POST https://api.calendly.com/webhook_subscriptions \
-H "Authorization: Bearer YOUR_PERSONAL_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server-address/webhooks/calendly",
"events": [
"invitee.created",
"invitee.canceled"
],
"organization": "https://api.calendly.com/organizations/your-organization-ID",
"scope": "organization"
}'
Une fois la création réussie, Calendly envoie une requête de vérification à l'url fournie, et le serveur doit renvoyer HTTP 200 pour finaliser la prise de contact. Par la suite, chaque fois qu'une personne prend ou annule un rendez-vous, un événement est envoyé à cette adresse.
Remarque : l'
urldoit être une adresse HTTPS accessible publiquement. Pendant le développement local, utilisez ngrok pour générer une adresse temporaire :ngrok http 3000, puis renseignez l'adressehttps://xxxx.ngrok.ioainsi générée.
Étape 3 : Mettre en place le service de réception des Webhooks
Voici un exemple complet de serveur Node.js qui, après réception des événements Webhook de Calendly, appelle l'API EngageLab SMS pour envoyer un SMS.
Installer les dépendances
npm install express
Code complet
// server.js
import express from 'express';
const app = express();
app.use(express.json());
// Authentification EngageLab : base64(dev_key:dev_secret)
const ENGAGELAB_AUTH = Buffer.from(
`${process.env.ENGAGELAB_DEV_KEY}:${process.env.ENGAGELAB_DEV_SECRET}`
).toString('base64');
// Appel de l'API EngageLab SMS pour envoyer un SMS
async function sendSMS({ to, templateId, params }) {
const res = await fetch('https://smsapi.engagelab.com/v1/messages', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Basic ${ENGAGELAB_AUTH}`,
},
body: JSON.stringify({
to: [to],
template: {
id: templateId,
params,
},
}),
});
const data = await res.json();
// HTTP 200 ne signifie pas que l'envoi a réussi ; vérifiez le champ code
if (data.code && data.code !== 0) {
console.error(`Échec de l'envoi du SMS : code=${data.code}, message=${data.message}`);
} else {
console.log(`SMS envoyé avec succès : plan_id=${data.plan_id}, message_id=${data.message_id}`);
}
}
// Convertit une chaîne de temps ISO en heure locale (Asia/Shanghai)
function formatTime(isoString) {
return new Date(isoString).toLocaleString('fr-FR', {
timeZone: 'Asia/Shanghai',
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
});
}
// Planifie les tâches de rappel avant la réunion (une à 24 h et une à 1 h)
function scheduleReminders({ phone, name, startTime }) {
const meetingTime = new Date(startTime).getTime();
const now = Date.now();
const reminders = [
{ advance: '24 heures', triggerAt: meetingTime - 24 * 60 * 60 * 1000 },
{ advance: '1 heure', triggerAt: meetingTime - 60 * 60 * 1000 },
];
for (const { advance, triggerAt } of reminders) {
const delay = triggerAt - now;
if (delay <= 0) continue; // L'heure de déclenchement est passée, on ignore
setTimeout(async () => {
await sendSMS({
to: phone,
templateId: process.env.TEMPLATE_ID_REMINDER,
params: { name, time: formatTime(startTime), advance },
});
}, delay);
}
}
// Point de terminaison de réception des Webhooks
app.post('/webhooks/calendly', async (req, res) => {
const { event, payload } = req.body;
const phone = payload.text_reminder_number; // Numéro de téléphone renseigné par l'utilisateur lors de la réservation
const name = payload.invitee?.name ?? 'Utilisateur';
const startTime = payload.scheduled_event?.start_time;
// Ignorer l'envoi du SMS si aucun numéro de téléphone n'est renseigné
if (!phone) {
console.log('Aucun numéro de téléphone renseigné pour ce rendez-vous, notification SMS ignorée');
return res.sendStatus(200);
}
if (event === 'invitee.created') {
// Envoyer le SMS de confirmation de rendez-vous
await sendSMS({
to: phone,
templateId: process.env.TEMPLATE_ID_CONFIRM,
params: { name, time: formatTime(startTime) },
});
// Planifier les tâches de rappel avant la réunion
scheduleReminders({ phone, name, startTime });
} else if (event === 'invitee.canceled') {
// Envoyer le SMS de notification d'annulation
await sendSMS({
to: phone,
templateId: process.env.TEMPLATE_ID_CANCEL,
params: { name },
});
}
// Doit renvoyer 200 dans les 2 secondes, sinon Calendly considère l'envoi comme échoué et réessaie
res.sendStatus(200);
});
app.listen(3000, () => console.log('Server running on port 3000'));
Configuration des variables d'environnement
Créez un fichier .env à la racine du projet et renseignez les variables suivantes :
ENGAGELAB_DEV_KEY=votre_dev_key
ENGAGELAB_DEV_SECRET=votre_dev_secret
TEMPLATE_ID_CONFIRM=id_du_modele_de_confirmation_de_rendez_vous
TEMPLATE_ID_CANCEL=id_du_modele_de_notification_d_annulation
TEMPLATE_ID_REMINDER=id_du_modele_de_rappel_de_reunion
Description des champs clés
Dans le payload envoyé par le Webhook Calendly, les champs suivants sont directement liés à l'envoi du SMS :
| Champ | Description |
|---|---|
event |
Type d'événement, invitee.created (création de rendez-vous) ou invitee.canceled (annulation de rendez-vous) |
payload.text_reminder_number |
Numéro de téléphone renseigné par l'invité, incluant l'indicatif pays ; peut être vide |
payload.invitee.name |
Nom de l'invité |
payload.scheduled_event.start_time |
Heure de début de la réunion, au format ISO 8601 |
Scénarios étendus
Envoyer un SMS de suivi après la réunion
Ajoutez à scheduleReminders une tâche déclenchée après la réunion, afin d'envoyer une enquête de satisfaction ou une invitation à un prochain rendez-vous :
// À ajouter dans la fonction scheduleReminders
const followUpAt = meetingTime + 30 * 60 * 1000; // 30 minutes après la fin de la réunion
const followUpDelay = followUpAt - now;
if (followUpDelay > 0) {
setTimeout(async () => {
await sendSMS({
to: phone,
templateId: process.env.TEMPLATE_ID_FOLLOWUP,
params: { name },
});
}, followUpDelay);
}
Notifier simultanément l'organisateur
Lors de la création d'un rendez-vous, en plus de notifier l'invité, vous pouvez également envoyer un rappel à l'organisateur de la réunion :
if (event === 'invitee.created') {
// Notifier l'invité
await sendSMS({
to: phone,
templateId: process.env.TEMPLATE_ID_CONFIRM,
params: { name, time: formatTime(startTime) },
});
// Notifier l'organisateur
await sendSMS({
to: process.env.HOST_PHONE,
templateId: process.env.TEMPLATE_ID_HOST_NOTIFY,
params: { name, time: formatTime(startTime) },
});
}
Points d'attention
- Le Webhook doit répondre par
HTTP 200dans les 2 secondes, sinon Calendly considère l'envoi comme échoué et réessaie. Il est recommandé de renvoyer d'abord 200, puis d'exécuter la logique d'envoi du SMS de façon asynchrone, afin d'éviter que Calendly ne se trompe à cause d'un délai de réponse de l'API EngageLab. - Le champ
text_reminder_numberpeut être vide ; vous devez gérer ce cas nul dans votre code pour éviter de transmettre un numéro vide à l'API SMS, ce qui provoquerait une erreur. - Le format du numéro de téléphone doit inclure l'indicatif pays, par exemple
+6591234567pour un numéro de Singapour. Calendly guide les utilisateurs vers le format international lors de la collecte des numéros, mais il est recommandé d'effectuer une validation du format côté serveur. setTimeoutn'est pas adapté à un environnement de production, car toutes les tâches planifiées sont perdues au redémarrage du serveur. En production, il est recommandé d'utiliser une file de tâches persistante (comme BullMQ + Redis) ou les tâches planifiées d'un service cloud, en stockant les rappels dans une base de données afin de pouvoir les restaurer après un redémarrage.- Un modèle ne peut être utilisé qu'après validation ; si le modèle est en attente de validation ou refusé au moment de l'appel, l'API renverra une erreur
4001. - Une réponse HTTP 200 de l'API ne signifie pas que le SMS a été envoyé avec succès ; vérifiez le champ
codedans le corps de la réponse, et s'il est différent de zéro, consultez les explications des codes d'erreur pour en identifier la cause.










