Calendly
Calendly ist ein weit verbreitetes Online-Terminplanungstool, aber seine integrierte SMS-Erinnerungsfunktion unterstützt nur bestimmte Länder und Regionen, sodass Märkte wie der asiatisch-pazifische Raum, Südostasien und der Nahe Osten nur eingeschränkt abgedeckt sind. Durch die Kombination von Calendly-Webhooks mit EngageLab SMS können Sie SMS-Benachrichtigungen an jede beliebige Nummer weltweit senden, wenn Termine erstellt oder storniert werden und bevor Meetings beginnen, und so die geografischen Lücken der nativen Funktionen von Calendly schließen.
Voraussetzungen
Bevor Sie beginnen, stellen Sie bitte sicher, dass die folgenden Konfigurationen abgeschlossen sind:
Auf der EngageLab-Seite
- Der EngageLab SMS Dienst ist aktiviert
- SMS-Vorlagen wurden auf der Seite Vorlagenverwaltung erstellt und genehmigt und Vorlagen-IDs erhalten
- API Keys wurden auf der Seite API Key erstellt und
dev_keyunddev_secreterhalten
Auf der Calendly-Seite
- Sie haben einen Calendly Standard-Plan oder höher (die Webhook-Funktion erfordert einen kostenpflichtigen Plan)
- Auf der Seite Integrations & apps → API and webhooks wurde ein Personal Access Token erstellt
Auf der Serverseite
- Sie haben einen öffentlich zugänglichen Server, der mit einem gültigen HTTPS-Zertifikat konfiguriert ist
- Für lokale Entwicklung und Debugging können Sie ngrok verwenden, um lokale Ports temporär freizugeben
Schritt 1: SMS-Vorlagen vorbereiten
Der Aufruf der API zum Senden von SMS erfordert vorab genehmigte Vorlagen; das direkte Übergeben von benutzerdefiniertem Text wird nicht unterstützt.
Melden Sie sich bei der EngageLab Konsole an, gehen Sie zu SMS → Vorlagenverwaltung und erstellen Sie je nach Szenario die folgenden drei Vorlagen:
| Vorlagenzweck | Beispiel für Vorlageninhalt |
|---|---|
| Terminbestätigung | Hello {{name}}, your meeting is confirmed for {{time}}. Looking forward to seeing you. |
| Stornierungsbenachrichtigung | Hello {{name}}, your meeting appointment has been canceled. Please contact us if you need to reschedule. |
| Meeting-Erinnerung | Hello {{name}}, you have a meeting starting in {{advance}} at {{time}}. Please prepare in advance. |
Warten Sie nach dem Einreichen der Vorlagen auf die Genehmigung und notieren Sie sich die drei Vorlagen-IDs.
Empfehlung: Das Erstellen unabhängiger Vorlagen für jedes Szenario sorgt für klarere Semantik und höhere Genehmigungsraten. Wenn die Vorlage benutzerdefinierte Variablen enthält (z. B.
{{name}}), müssen Sie beim Aufruf der API Werte über das Feldparamsübergeben, andernfalls werden die Variablen unverändert zugestellt.
Schritt 2: Webhook-Abonnement in Calendly erstellen
Die Webhook-Verwaltung von Calendly verfügt über keine visuelle Oberfläche und muss über die API erstellt werden.
Organisations-URI abrufen
Rufen Sie zunächst die folgende Schnittstelle auf, um die Organisations-URI des aktuellen Kontos abzurufen:
curl https://api.calendly.com/users/me \
-H "Authorization: Bearer YOUR_PERSONAL_ACCESS_TOKEN"
Suchen Sie das Feld current_organization in der Antwort, formatiert wie folgt:
https://api.calendly.com/organizations/xxxxxxxxxxxxxxxx
Webhook-Abonnement erstellen
Verwenden Sie die Organisations-URI, um einen Webhook zu erstellen, der Ereignisse zur Terminerstellung und -stornierung abonniert:
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"
}'
Nach erfolgreicher Erstellung übermittelt Calendly eine Verifizierungsanfrage an die angegebene url, und der Server muss HTTP 200 zurückgeben, um den Handshake abzuschließen. Anschließend wird bei jeder Terminbuchung oder -stornierung ein Ereignis an diese Adresse übermittelt.
Hinweis: Die
urlmuss eine öffentlich zugängliche HTTPS-Adresse sein. Verwenden Sie während der lokalen Entwicklung ngrok, um eine temporäre Adresse zu generieren:ngrok http 3000, und tragen Sie die ausgegebenehttps://xxxx.ngrok.ioein.
Schritt 3: Webhook-Empfangsdienst einrichten
Nachfolgend finden Sie ein vollständiges serverseitiges Node.js-Beispiel, das Calendly-Webhook-Ereignisse empfängt und die EngageLab SMS API aufruft, um eine SMS zu senden.
Abhängigkeiten installieren
npm install express
Vollständiger Code
// server.js
import express from 'express';
const app = express();
app.use(express.json());
// EngageLab Authentication: base64(dev_key:dev_secret)
const ENGAGELAB_AUTH = Buffer.from(
`${process.env.ENGAGELAB_DEV_KEY}:${process.env.ENGAGELAB_DEV_SECRET}`
).toString('base64');
// Call EngageLab SMS API to send 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 does not mean successful sending; check the code field
if (data.code && data.code !== 0) {
console.error(`SMS send failed: code=${data.code}, message=${data.message}`);
} else {
console.log(`SMS sent successfully: plan_id=${data.plan_id}, message_id=${data.message_id}`);
}
}
// Convert ISO time string to local time (Asia/Shanghai)
function formatTime(isoString) {
return new Date(isoString).toLocaleString('en-US', {
timeZone: 'Asia/Shanghai',
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
});
}
// Register pre-meeting reminder tasks (one at 24h and one at 1h)
function scheduleReminders({ phone, name, startTime }) {
const meetingTime = new Date(startTime).getTime();
const now = Date.now();
const reminders = [
{ advance: '24 hours', triggerAt: meetingTime - 24 * 60 * 60 * 1000 },
{ advance: '1 hour', triggerAt: meetingTime - 60 * 60 * 1000 },
];
for (const { advance, triggerAt } of reminders) {
const delay = triggerAt - now;
if (delay <= 0) continue; // Trigger time has passed, skip
setTimeout(async () => {
await sendSMS({
to: phone,
templateId: process.env.TEMPLATE_ID_REMINDER,
params: { name, time: formatTime(startTime), advance },
});
}, delay);
}
}
// Webhook Receiving Endpoint
app.post('/webhooks/calendly', async (req, res) => {
const { event, payload } = req.body;
const phone = payload.text_reminder_number; // Phone number provided by user during booking
const name = payload.invitee?.name ?? 'User';
const startTime = payload.scheduled_event?.start_time;
// Skip sending SMS if phone number is not provided
if (!phone) {
console.log('No phone number provided for this appointment, skipping SMS notification');
return res.sendStatus(200);
}
if (event === 'invitee.created') {
// Send appointment confirmation SMS
await sendSMS({
to: phone,
templateId: process.env.TEMPLATE_ID_CONFIRM,
params: { name, time: formatTime(startTime) },
});
// Register pre-meeting reminder tasks
scheduleReminders({ phone, name, startTime });
} else if (event === 'invitee.canceled') {
// Send cancellation notification SMS
await sendSMS({
to: phone,
templateId: process.env.TEMPLATE_ID_CANCEL,
params: { name },
});
}
// Must return 200 within 2 seconds, otherwise Calendly considers the push failed and retries
res.sendStatus(200);
});
app.listen(3000, () => console.log('Server running on port 3000'));
Konfiguration der Umgebungsvariablen
Erstellen Sie eine .env-Datei im Stammverzeichnis des Projekts und tragen Sie die folgenden Variablen ein:
ENGAGELAB_DEV_KEY=your_dev_key
ENGAGELAB_DEV_SECRET=your_dev_secret
TEMPLATE_ID_CONFIRM=appointment_confirmation_template_id
TEMPLATE_ID_CANCEL=cancellation_notification_template_id
TEMPLATE_ID_REMINDER=meeting_reminder_template_id
Beschreibung der wichtigsten Felder
Im vom Calendly-Webhook übermittelten Payload stehen die folgenden Felder in direktem Zusammenhang mit dem SMS-Versand:
| Feld | Beschreibung |
|---|---|
event |
Ereignistyp, invitee.created (Terminerstellung) oder invitee.canceled (Terminstornierung) |
payload.text_reminder_number |
Vom Eingeladenen ausgefüllte Telefonnummer, einschließlich Ländervorwahl; kann leer sein |
payload.invitee.name |
Name des Eingeladenen |
payload.scheduled_event.start_time |
Meeting-Startzeit, ISO 8601-Format |
Erweiterte Szenarien
Follow-up-SMS nach dem Meeting senden
Fügen Sie scheduleReminders eine nach dem Meeting ausgelöste Aufgabe hinzu, um eine Zufriedenheitsumfrage oder eine Einladung zum nächsten Termin zu senden:
// Append to the scheduleReminders function
const followUpAt = meetingTime + 30 * 60 * 1000; // 30 minutes after the meeting ends
const followUpDelay = followUpAt - now;
if (followUpDelay > 0) {
setTimeout(async () => {
await sendSMS({
to: phone,
templateId: process.env.TEMPLATE_ID_FOLLOWUP,
params: { name },
});
}, followUpDelay);
}
Gleichzeitige Benachrichtigung des Gastgebers
Wenn ein Termin erstellt wird, kann neben der Benachrichtigung des Eingeladenen auch eine Erinnerung an den Meeting-Gastgeber gesendet werden:
if (event === 'invitee.created') {
// Notify invitee
await sendSMS({
to: phone,
templateId: process.env.TEMPLATE_ID_CONFIRM,
params: { name, time: formatTime(startTime) },
});
// Notify host
await sendSMS({
to: process.env.HOST_PHONE,
templateId: process.env.TEMPLATE_ID_HOST_NOTIFY,
params: { name, time: formatTime(startTime) },
});
}
Hinweise
- Der Webhook muss innerhalb von 2 Sekunden mit
HTTP 200antworten, andernfalls betrachtet Calendly die Übermittlung als fehlgeschlagen und wiederholt sie. Es wird empfohlen, zuerst 200 zurückzugeben und die SMS-Sendelogik anschließend asynchron auszuführen, um zu verhindern, dass Calendly aufgrund von Antwortverzögerungen der EngageLab API eine Fehleinschätzung trifft. - Das Feld
text_reminder_numberkann leer sein, daher müssen Sie in Ihrem Code Null-Prüfungen durchführen, um zu vermeiden, dass eine leere Nummer an die SMS-API übergeben wird und Fehler verursacht. - Das Telefonnummernformat muss die Ländervorwahl enthalten, z. B.
+6591234567für eine Nummer aus Singapur. Calendly leitet Nutzer an, beim Erfassen von Telefonnummern internationale Formate einzugeben, es wird jedoch empfohlen, serverseitig eine Formatprüfung durchzuführen. setTimeoutist für Produktionsumgebungen nicht geeignet, da alle geplanten Aufgaben verloren gehen, wenn der Server neu gestartet wird. In einer Produktionsumgebung wird empfohlen, persistente Aufgaben-Warteschlangen (wie BullMQ + Redis) oder Cron-Jobs von Cloud-Diensten zu verwenden, um Erinnerungsdatensätze in einer Datenbank zu speichern, damit sie nach einem Neustart wiederhergestellt werden können.- Vorlagen müssen genehmigt sein, bevor sie verwendet werden können. Wenn die Vorlage beim Aufruf in Prüfung oder abgelehnt ist, gibt die API einen
4001-Fehler zurück. - Eine HTTP-200-Antwort der API bedeutet nicht, dass die SMS erfolgreich gesendet wurde. Bitte überprüfen Sie das Feld
codeim Antwortkörper. Wenn es ungleich null ist, finden Sie in der Fehlercode-Erläuterung Hinweise zur Fehlerbehebung.










