DEV Community

Cover image for Comment utiliser les APIs Calendly : Guide du développeur pour l'intégration de la planification
Antoine Laurent
Antoine Laurent

Posted on • Originally published at apidog.com

Comment utiliser les APIs Calendly : Guide du développeur pour l'intégration de la planification

En bref

Les API Calendly permettent d'automatiser vos flux de planification : authentification par OAuth 2.0, accès aux types d'événements et réservations via api.calendly.com, notifications temps réel avec les webhooks. Pour tester vos intégrations, utilisez Apidog afin de valider les charges utiles de webhooks et simuler des réservations sans toucher à la production.

Essayez Apidog dès aujourd'hui

Introduction

Calendly gère des millions de réunions chaque mois pour des appels de vente, du support, des consultations ou entretiens. L'API vous permet d'intégrer la planification directement dans vos applications.

Cas pratique : déclencher des actions automatiques lors d'une réservation Calendly. Par exemple, créer un lead dans votre CRM lors d'une démo réservée, envoyer un questionnaire lors d’une consultation planifiée, ou notifier l’équipe si la réunion est annulée.

L’API Calendly s’appuie sur les webhooks : à chaque événement (création, annulation, replanification), Calendly effectue une requête POST vers votre endpoint. À vous de traiter cette charge utile et d’automatiser vos processus.

💡 Si vous développez des intégrations de planification, Apidog facilite le test de vos gestionnaires de webhooks : simulez les réponses de Calendly, validez toutes les charges utiles, et assurez la robustesse avant la connexion à des calendriers réels.

Authentification avec OAuth 2.0

Calendly n’accepte pas de clé API : l’accès passe obligatoirement par OAuth 2.0.

Créer une application OAuth

  1. Rendez-vous sur Calendly → Intégrations → API & Webhooks
  2. Cliquez sur «Créer une nouvelle application»
  3. Définissez votre URI de redirection (ex : https://yourapp.com/auth/calendly/callback)
  4. Récupérez votre client ID et secret client

Le flux OAuth

Étape 1 : Rediriger l'utilisateur pour l'autorisation

https://auth.calendly.com/oauth/authorize?
  client_id=VOTRE_ID_CLIENT&
  response_type=code&
  redirect_uri=https://yourapp.com/auth/calendly/callback
Enter fullscreen mode Exit fullscreen mode

Étape 2 : L'utilisateur autorise et est redirigé

https://yourapp.com/auth/calendly/callback?code=CODE_D_AUTORISATION
Enter fullscreen mode Exit fullscreen mode

Étape 3 : Échanger le code contre un jeton d'accès

const response = await fetch('https://auth.calendly.com/oauth/token', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded',
    'Authorization': 'Basic ' + Buffer.from(clientId + ':' + clientSecret).toString('base64')
  },
  body: new URLSearchParams({
    grant_type: 'authorization_code',
    code: authCode,
    redirect_uri: 'https://yourapp.com/auth/calendly/callback'
  })
})

const { access_token, refresh_token, expires_in } = await response.json()
Enter fullscreen mode Exit fullscreen mode

Étape 4 : Utiliser le jeton

curl -X GET "https://api.calendly.com/users/me" \
  -H "Authorization: Bearer JETON_D_ACCES"
Enter fullscreen mode Exit fullscreen mode

Jetons d'actualisation

Les jetons d'accès expirent après 2 heures. Rafraîchissez avec le refresh token :

const response = await fetch('https://auth.calendly.com/oauth/token', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded',
    'Authorization': 'Basic ' + Buffer.from(clientId + ':' + clientSecret).toString('base64')
  },
  body: new URLSearchParams({
    grant_type: 'refresh_token',
    refresh_token: storedRefreshToken
  })
})
Enter fullscreen mode Exit fullscreen mode

Obtention des informations utilisateur

Obtenir l'utilisateur actuel

curl -X GET "https://api.calendly.com/users/me" \
  -H "Authorization: Bearer JETON_D_ACCES"
Enter fullscreen mode Exit fullscreen mode

Réponse :

{
  "resource": {
    "avatar_url": "https://calendly.com/avatar.jpg",
    "created_at": "2024-01-15T10:00:00Z",
    "current_organization": "https://api.calendly.com/organizations/ABC123",
    "email": "vous@exemple.com",
    "name": "Jean Dupont",
    "scheduling_url": "https://calendly.com/jeandupont",
    "slug": "jeandupont",
    "timezone": "America/New_York",
    "uri": "https://api.calendly.com/users/ABC123"
  }
}
Enter fullscreen mode Exit fullscreen mode

Obtenir l'adhésion à l'organisation

curl -X GET "https://api.calendly.com/organization_memberships/me" \
  -H "Authorization: Bearer JETON_D_ACCES"
Enter fullscreen mode Exit fullscreen mode

Types d'événements

Les types d'événements sont les modèles de réunion (ex : appel 30 min, consultation 60 min).

Lister les types d'événements

curl -X GET "https://api.calendly.com/event_types?user=https://api.calendly.com/users/ABC123" \
  -H "Authorization: Bearer JETON_D_ACCES"
Enter fullscreen mode Exit fullscreen mode

Réponse :

{
  "resource": {
    "uri": "https://api.calendly.com/event_types/ETC123",
    "active": true,
    "booking_method": "instant",
    "color": "#0066FF",
    "created_at": "2024-01-15T10:00:00Z",
    "description_html": "<p>Consultation de 30 minutes</p>",
    "duration": 30,
    "internal_note": "Utiliser le lien Zoom",
    "kind": "solo",
    "name": "Consultation de 30 min",
    "pooling_type": null,
    "profile": {
      "name": "Jean Dupont",
      "type": "User",
      "owner": "https://api.calendly.com/users/ABC123"
    },
    "scheduling_url": "https://calendly.com/jeandupont/30min",
    "slug": "30min",
    "type": "StandardEventType"
  },
  "pagination": {
    "count": 1,
    "next_page": null
  }
}
Enter fullscreen mode Exit fullscreen mode

Obtenir un type d’événement spécifique

curl -X GET "https://api.calendly.com/event_types/ETC123" \
  -H "Authorization: Bearer JETON_D_ACCES"
Enter fullscreen mode Exit fullscreen mode

Événements planifiés (réservations)

Les événements correspondent aux réservations concrètes créées via Calendly.

Lister les événements planifiés

curl -X GET "https://api.calendly.com/scheduled_events?user=https://api.calendly.com/users/ABC123" \
  -H "Authorization: Bearer JETON_D_ACCES"
Enter fullscreen mode Exit fullscreen mode

Pour filtrer par date :

curl -X GET "https://api.calendly.com/scheduled_events?min_start_time=2026-03-01T00:00:00Z&max_start_time=2026-03-31T23:59:59Z" \
  -H "Authorization: Bearer JETON_D_ACCES"
Enter fullscreen mode Exit fullscreen mode

Réponse :

{
  "resource": {
    "uri": "https://api.calendly.com/scheduled_events/ABC123",
    "status": "active",
    "tracking": {
      "utm_campaign": "vente_printemps",
      "utm_source": "email",
      "utm_medium": "newsletter"
    },
    "created_at": "2026-03-24T10:00:00Z",
    "end_time": "2026-03-25T11:00:00Z",
    "event_type": "https://api.calendly.com/event_types/ETC123",
    "invitees_counter": {
      "active": 1,
      "limit": 1,
      "total": 1
    },
    "location": {
      "type": "zoom",
      "join_url": "https://zoom.us/j/123456789"
    },
    "start_time": "2026-03-25T10:30:00Z",
    "updated_at": "2026-03-24T10:00:00Z"
  }
}
Enter fullscreen mode Exit fullscreen mode

Obtenir les détails d’un événement

curl -X GET "https://api.calendly.com/scheduled_events/ID_EVENEMENT" \
  -H "Authorization: Bearer JETON_D_ACCES"
Enter fullscreen mode Exit fullscreen mode

Obtenir les invités pour un événement

curl -X GET "https://api.calendly.com/scheduled_events/ID_EVENEMENT/invitees" \
  -H "Authorization: Bearer JETON_D_ACCES"
Enter fullscreen mode Exit fullscreen mode

Réponse :

{
  "resource": [
    {
      "cancel_url": "https://calendly.com/cancellations/ABC123",
      "created_at": "2026-03-24T10:00:00Z",
      "email": "jane@exemple.com",
      "event": "https://api.calendly.com/scheduled_events/ABC123",
      "name": "Jeanne Smith",
      "new_invitee": null,
      "old_invitee": null,
      "reschedule_url": "https://calendly.com/reschedulings/ABC123",
      "status": "active",
      "text_reminder_number": "+15551234567",
      "timezone": "America/New_York",
      "tracking": {
        "utm_campaign": null,
        "utm_source": null
      },
      "updated_at": "2026-03-24T10:00:00Z",
      "uri": "https://api.calendly.com/scheduled_event_invitees/INV123",
      "canceled": null
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Webhooks pour les mises à jour en temps réel

Les webhooks notifient automatiquement votre application en cas de création, annulation ou replanification de réservation.

Créer un abonnement webhook

curl -X POST "https://api.calendly.com/webhook_subscriptions" \
  -H "Authorization: Bearer JETON_D_ACCES" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yourapp.com/webhooks/calendly",
    "events": [
      "invitee.created",
      "invitee.canceled",
      "invitee.rescheduled"
    ],
    "organization": "https://api.calendly.com/organizations/ORG123",
    "scope": "organization"
  }'
Enter fullscreen mode Exit fullscreen mode

Événements disponibles :

  • invitee.created : Nouvelle réservation
  • invitee.canceled : Réservation annulée
  • invitee.rescheduled : Réservation replanifiée

Lister les abonnements webhook

curl -X GET "https://api.calendly.com/webhook_subscriptions?organization=https://api.calendly.com/organizations/ORG123" \
  -H "Authorization: Bearer JETON_D_ACCES"
Enter fullscreen mode Exit fullscreen mode

Supprimer un webhook

curl -X DELETE "https://api.calendly.com/webhook_subscriptions/ID_WEBHOOK" \
  -H "Authorization: Bearer JETON_D_ACCES"
Enter fullscreen mode Exit fullscreen mode

Gestion des charges utiles de webhook

Vérifier les signatures de webhook

Calendly signe chaque webhook via l’en-tête Calendly-Webhook-Signature. Pour la sécurité, vérifiez la signature :

import crypto from 'crypto'

function verifySignature(payload, signature, secret) {
  const [t, v1] = signature.split(',')
  const timestamp = t.split('=')[1]
  const hash = v1.split('=')[1]

  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(timestamp + '.' + payload)
    .digest('hex')

  return crypto.timingSafeEqual(
    Buffer.from(hash),
    Buffer.from(expectedSignature)
  )
}

app.post('/webhooks/calendly', (req, res) => {
  const signature = req.headers['calendly-webhook-signature']
  const payload = JSON.stringify(req.body)

  if (!verifySignature(payload, signature, process.env.CALENDLY_WEBHOOK_SECRET)) {
    return res.status(401).send('Signature invalide')
  }

  // Traiter le webhook
  handleWebhook(req.body)
  res.status(200).send('OK')
})
Enter fullscreen mode Exit fullscreen mode

Traiter les événements de réservation

function handleWebhook(payload) {
  const { event, payload: data } = payload

  switch (event) {
    case 'invitee.created':
      console.log(`Nouvelle réservation : ${data.event.start_time}`)
      console.log(`Invité : ${data.email}`)
      // Ajouter au CRM, envoyer un e-mail, etc.
      syncToCRM(data)
      break

    case 'invitee.canceled':
      console.log(`Réservation annulée : ${data.event.uri}`)
      // Mettre à jour le CRM, notifier l'équipe, etc.
      removeFromCRM(data)
      break

    case 'invitee.rescheduled':
      console.log(`Réservation reprogrammée : ${data.event.start_time}`)
      // Mettre à jour l'agenda, notifier l'équipe, etc.
      updateCRM(data)
      break
  }
}
Enter fullscreen mode Exit fullscreen mode

Tester avec Apidog

L’API Calendly nécessite OAuth, ce qui complique les tests. Apidog simplifie tout le processus.

Test Webhook avec Apidog

1. Simuler les réponses OAuth

Évitez de passer le flux OAuth complet à chaque test. Simulez la réponse :

{
  "access_token": "jeton_d_acces_simule",
  "refresh_token": "jeton_d_actualisation_simule",
  "expires_in": 7200,
  "created_at": 1700000000
}
Enter fullscreen mode Exit fullscreen mode

2. Tester les gestionnaires de webhook

Utilisez des charges utiles simulées :

{
  "created_at": "2026-03-24T10:00:00Z",
  "event": "invitee.created",
  "payload": {
    "email": "test@exemple.com",
    "name": "Utilisateur de test",
    "event": {
      "start_time": "2026-03-25T10:30:00Z",
      "end_time": "2026-03-25T11:00:00Z",
      "event_type": {
        "name": "Consultation de 30 min"
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Envoyez-les sur votre endpoint et vérifiez la réponse.

3. Variables d'environnement

CALENDLY_CLIENT_ID: abc123
CALENDLY_CLIENT_SECRET: xyz789
CALENDLY_ACCESS_TOKEN: jeton_enregistre
CALENDLY_REFRESH_TOKEN: actualisation_enregistree
CALENDLY_WEBHOOK_SECRET: secret_signature_webhook
Enter fullscreen mode Exit fullscreen mode

4. Valider les signatures de webhook

pm.test('La signature du webhook est valide', () => {
  const signature = pm.request.headers.get('Calendly-Webhook-Signature')
  pm.expect(signature).to.exist

  const payload = pm.request.body.raw
  const secret = pm.environment.get('CALENDLY_WEBHOOK_SECRET')

  // Vérifier la signature
  const valid = verifySignature(payload, signature, secret)
  pm.expect(valid).to.be.true
})
Enter fullscreen mode Exit fullscreen mode

Testez les webhooks Calendly gratuitement avec Apidog.

Erreurs courantes et correctifs

401 Non autorisé

Cause : Jeton invalide ou expiré.

Correctif :

  1. Vérifiez que le jeton n’a pas expiré (2h d’expiration)
  2. Rafraîchissez avec le refresh token
  3. L’en-tête Authorization doit être Bearer {token}

403 Interdit

Cause : Portée OAuth insuffisante.

Correctif : Ajoutez les scopes nécessaires lors de la demande d’autorisation. Les scopes sont implicites selon ce que l’utilisateur autorise.

404 Introuvable

Cause : Ressource inexistante ou non accessible.

Correctif :

  1. Vérifiez l’URI
  2. Assurez-vous que l’utilisateur authentifié a accès
  3. Vérifiez la validité de l’ID d’événement ou de type d’événement

422 Entité non traitable

Cause : Erreur de validation dans la requête.

Correctif : Consultez la réponse d’erreur pour le détail :

{
  "title": "Erreur de validation",
  "message": "Paramètre invalide : l'URL doit être une URL HTTPS valide"
}
Enter fullscreen mode Exit fullscreen mode

Alternatives et comparaisons

Fonctionnalité Calendly Acuity Cal.com Calendly
Tier gratuit Limité Limité Auto-hébergé gratuit
Accès API
Webhooks
OAuth Clé API Clé API OAuth
Planification d'équipe
Open source Non Non Oui Non

Calendly propose une documentation API et un flux OAuth soignés. Cal.com est l’alternative open-source avec authentification par clé API.

Cas d'utilisation réels

Intégration CRM de vente

  • Création automatique de prospect dans Salesforce à chaque réservation de démo
  • Notification Slack à l’équipe de vente
  • Ajout du contact à la séquence marketing
  • Enregistrement de l’activité dans la plateforme customer success

Plateforme de consultation

  • Synchronisation des réservations avec le système interne
  • Génération automatique de liens Zoom
  • Envoi d’un questionnaire d’admission avant la consultation
  • Création de dossier client après la réunion

Planification d’entretiens

  • Mise à jour automatique de l’ATS avec les détails de l’entretien
  • Notification du manager par e-mail
  • Envoi d’invitations calendrier à tous les participants
  • Suivi des absences pour relance

Conclusion

À retenir pour automatiser Calendly :

  • Authentification OAuth 2.0 obligatoire
  • API pour accéder à tous les types et événements de réservation
  • Webhooks pour la notification temps réel
  • Vérification de la signature webhook pour la sécurité
  • Testez vos intégrations avec Apidog avant la production

Prochaines étapes :

  1. Créez votre application OAuth dans Calendly
  2. Implémentez le flux OAuth dans votre app
  3. Configurez un webhook
  4. Testez avec des charges utiles simulées dans Apidog
  5. Déployez en production

Testez gratuitement les webhooks Calendly avec Apidog.

FAQ

Ai-je besoin d’un forfait Calendly payant pour utiliser l’API ?
Non. L’API est disponible sur tous les forfaits, même gratuit. Les webhooks aussi. Mais certaines fonctionnalités avancées sont limitées en gratuit.

Différence entre webhooks utilisateur et organisation ?
Un webhook utilisateur ne reçoit que les événements de ce compte. Un webhook organisation couvre l’ensemble de l’équipe (privilégiez-le pour la plupart des intégrations).

Comment obtenir le secret de signature webhook ?
Lors de la création du webhook via l’API, la réponse contient signing_key. Stockez-la, elle sert à valider les signatures.

Peut-on créer des réservations via l’API ?
Non. Il n’existe pas d’endpoint pour créer des réservations : elles se font via l’UI Calendly ou les widgets. L’API est en lecture seule pour les réservations.

Comment gérer les fuseaux horaires ?
Tous les timestamps sont en UTC (ISO 8601). Convertissez en heure locale côté application. Le fuseau utilisateur est dans la ressource utilisateur.

Quelle est la limite de débit ?
Non documenté publiquement. Restez raisonnable dans vos requêtes et implémentez un backoff exponentiel si besoin.

Peut-on obtenir les réservations historiques ?
Oui, utilisez min_start_time et max_start_time pour interroger n’importe quelle période.

Comment tester OAuth localement ?
Utilisez un tunnel (ex : ngrok) pour exposer votre serveur local. Définissez l’URI de redirection sur l’URL ngrok, complétez le flux dans un navigateur, et inspectez le callback.

Top comments (0)