DEV Community

Cover image for دليل المطور: كيفية استخدام واجهات برمجة تطبيقات Calendly لدمج الجدولة
Yusuf Khalidd
Yusuf Khalidd

Posted on • Originally published at apidog.com

دليل المطور: كيفية استخدام واجهات برمجة تطبيقات Calendly لدمج الجدولة

ملخص سريع

تتيح لك واجهات برمجة تطبيقات Calendly أتمتة مهام سير العمل الخاصة بالجدولة. يمكنك المصادقة باستخدام OAuth 2.0، والوصول إلى أنواع الأحداث والحجوزات عبر api.calendly.com، وتلقي التحديثات في الوقت الفعلي عبر webhooks. لاختبار التكاملات بسرعة، استخدم Apidog للتحقق من حمولات الـ webhook واختبار تكاملك بدون الحاجة لإنشاء حجوزات حقيقية.

جرّب Apidog اليوم

مقدمة

Calendly يعالج ملايين الاجتماعات شهريًا، ويُستخدم لمكالمات المبيعات، جلسات الدعم، الاستشارات، والمقابلات. يمكنك دمج واجهة برمجة التطبيقات مع تطبيقاتك الخاصة لتشغيل إجراءات تلقائية عند كل حجز أو إلغاء أو إعادة جدولة.

السيناريو العملي: عندما يحجز مستخدم عرضًا توضيحيًا، يتم تحديث نظام CRM. بعد جدولة استشارة، يُرسل استبيان. عند إلغاء اجتماع، يُخطر الفريق تلقائيًا.

تعتمد آلية التكامل على webhooks: عند حدوث حدث (إنشاء، إلغاء، إعادة جدولة)، يرسل Calendly طلب POST إلى endpoint تحدده أنت وتعالج الحمولة مباشرة.

💡 إذا كنت تبني تكاملاً للجدولة، استخدم Apidog لاختبار معالجات الـ webhook والتحقق من صحة الحمولات. يمكنك محاكاة استجابات Calendly أثناء التطوير والتأكد من أن تكاملك يدعم جميع أنواع الأحداث قبل الاتصال بالتقويمات الفعلية.

المصادقة باستخدام OAuth 2.0

Calendly لا يدعم مفاتيح API مباشرة. المصادقة تتم عبر OAuth 2.0 فقط.

إنشاء تطبيق OAuth

  1. توجه إلى Calendly → عمليات التكامل → API وWebhooks
  2. اختر "إنشاء تطبيق جديد"
  3. عيّن URI إعادة التوجيه (مثال: https://yourapp.com/auth/calendly/callback)
  4. احصل على معرّف العميل (Client ID) وسر العميل (Client Secret)

تدفق OAuth

الخطوة 1: إعادة توجيه المستخدم للمصادقة

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

الخطوة 2: المستخدم يصادق ويتم إعادة توجيهه

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

الخطوة 3: استبدال الكود برمز وصول

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

الخطوة 4: استخدام رمز الوصول

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

رموز التحديث (Refresh tokens)

رموز الوصول تنتهي بعد ساعتين. استخدم رمز التحديث للحصول على رمز جديد:

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

الحصول على معلومات المستخدم

الحصول على المستخدم الحالي

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

استجابة نموذجية:

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

الحصول على عضوية المؤسسة

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

أنواع الأحداث

أنواع الأحداث هي قوالب الاجتماعات (مثال: مكالمة 30 دقيقة).

سرد أنواع الأحداث

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

استجابة نموذجية:

{
  "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>30-minute consultation</p>",
    "duration": 30,
    "internal_note": "Use Zoom link",
    "kind": "solo",
    "name": "30 Min Consultation",
    "pooling_type": null,
    "profile": {
      "name": "John Doe",
      "type": "User",
      "owner": "https://api.calendly.com/users/ABC123"
    },
    "scheduling_url": "https://calendly.com/johndoe/30min",
    "slug": "30min",
    "type": "StandardEventType"
  },
  "pagination": {
    "count": 1,
    "next_page": null
  }
}
Enter fullscreen mode Exit fullscreen mode

الحصول على نوع حدث معين

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

الأحداث المجدولة (الحجوزات)

الحجوزات هي الأحداث التي ينشئها المستخدمون فعليًا عبر Calendly.

سرد الأحداث المجدولة

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

تصفية حسب النطاق الزمني:

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 ACCESS_TOKEN"
Enter fullscreen mode Exit fullscreen mode

استجابة نموذجية:

{
  "resource": {
    "uri": "https://api.calendly.com/scheduled_events/ABC123",
    "status": "active",
    "tracking": {
      "utm_campaign": "spring_sale",
      "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

الحصول على تفاصيل الحدث

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

الحصول على المدعوين لحدث

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

استجابة نموذجية:

{
  "resource": [
    {
      "cancel_url": "https://calendly.com/cancellations/ABC123",
      "created_at": "2026-03-24T10:00:00Z",
      "email": "jane@example.com",
      "event": "https://api.calendly.com/scheduled_events/ABC123",
      "name": "Jane 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 للتحديثات في الوقت الفعلي

Webhooks تمكّن تطبيقك من استقبال تحديثات الحجز مباشرة عند حدوثها.

إنشاء اشتراك webhook

curl -X POST "https://api.calendly.com/webhook_subscriptions" \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -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

الأحداث المتاحة:

  • invitee.created: تم إنشاء حجز جديد
  • invitee.canceled: تم إلغاء الحجز
  • invitee.rescheduled: تم إعادة جدولة الحجز

سرد اشتراكات الـ webhook

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

حذف webhook

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

معالجة حمولات الـ webhook

التحقق من توقيعات الـ webhook

Calendly يوقع كل webhook في رأس Calendly-Webhook-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('Invalid signature')
  }

  // معالجة الـ webhook
  handleWebhook(req.body)
  res.status(200).send('OK')
})
Enter fullscreen mode Exit fullscreen mode

معالجة أحداث الحجز

استخدم handler مركزي للتعامل مع أنواع الأحداث:

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

  switch (event) {
    case 'invitee.created':
      console.log(`New booking: ${data.event.start_time}`)
      console.log(`Invitee: ${data.email}`)
      // أضف للـ CRM، أرسل بريد تأكيد ... إلخ
      syncToCRM(data)
      break

    case 'invitee.canceled':
      console.log(`Booking canceled: ${data.event.uri}`)
      // حدث CRM، أخطر الفريق ... إلخ
      removeFromCRM(data)
      break

    case 'invitee.rescheduled':
      console.log(`Booking rescheduled: ${data.event.start_time}`)
      // حدث التقويم، أخطر الفريق ... إلخ
      updateCRM(data)
      break
  }
}
Enter fullscreen mode Exit fullscreen mode

الاختبار باستخدام Apidog

اختبار تكامل Calendly يتطلب OAuth، مما يزيد من تعقيد التجربة. Apidog يبسط العملية.

صورة توضيحية لواجهة Apidog

1. محاكاة استجابات OAuth

أثناء التطوير، استخدم بيانات رموز وهمية بدلًا من تدفق OAuth الكامل:

{
  "access_token": "mock_access_token",
  "refresh_token": "mock_refresh_token",
  "expires_in": 7200,
  "created_at": 1700000000
}
Enter fullscreen mode Exit fullscreen mode

2. اختبار معالجات الـ webhook

أنشئ payloads وهمية لإرسالها إلى endpoint الخاص بك:

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

أرسل الحمولة إلى نقطة النهاية وتحقق من المعالجة.

3. متغيرات البيئة

استخدم متغيرات البيئة التالية لتسهيل الاختبار:

CALENDLY_CLIENT_ID: abc123
CALENDLY_CLIENT_SECRET: xyz789
CALENDLY_ACCESS_TOKEN: stored_token
CALENDLY_REFRESH_TOKEN: stored_refresh
CALENDLY_WEBHOOK_SECRET: webhook_signing_secret
Enter fullscreen mode Exit fullscreen mode

4. التحقق من توقيعات الـ webhook

أضف اختبار تحقق من التوقيع في Apidog أو عبر Postman:

pm.test('Webhook signature is valid', () => {
  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')

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

اختبر webhooks Calendly باستخدام Apidog — مجانًا.

الأخطاء الشائعة والإصلاحات

401 غير مصرح به (Unauthorized)

السبب: رمز وصول غير صالح أو منتهي الصلاحية.

الإصلاح:

  1. تحقق من صلاحية رمز الوصول (تنتهي بعد ساعتين)
  2. استخدم refresh token لتجديد الرمز
  3. تأكد أن رأس Authorization بصيغة Bearer {token}

403 ممنوع (Forbidden)

السبب: نطاق OAuth غير كافٍ.

الإصلاح: عند طلب المصادقة، أضف النطاقات المطلوبة. نطاقات Calendly تُحدد حسب ما يصرح به المستخدم.

404 غير موجود (Not Found)

السبب: المورد غير موجود أو لا يملك المستخدم صلاحية الوصول.

الإصلاح:

  1. تحقق من URI المورد
  2. تأكد أن المستخدم المصادق لديه صلاحية الوصول
  3. تحقق من صلاحية معرف الحدث أو نوع الحدث

422 كيان غير قابل للمعالجة (Unprocessable Entity)

السبب: خطأ في التحقق من الطلب.

الإصلاح: تحقق من تفاصيل الاستجابة:

{
  "title": "Validation Error",
  "message": "Invalid parameter: url must be a valid HTTPS URL"
}
Enter fullscreen mode Exit fullscreen mode

البدائل والمقارنات

الميزة Calendly Acuity Cal.com Calendly
الخطة المجانية محدود محدود مُستضاف ذاتيًا مجانًا
الوصول إلى API
Webhooks
OAuth مفتاح API مفتاح API OAuth
جدولة الفريق
مفتوح المصدر لا لا نعم لا

يقدّم Calendly أفضل توثيق لـ API وتدفق OAuth. إذا كنت تبحث عن بديل مفتوح المصدر بآلية أبسط، جرّب Cal.com.

حالات استخدام واقعية

تكامل CRM للمبيعات:

شركة SaaS تدمج Calendly في صفحة التسعير. عند حجز demo، يُطلق webhook:

  1. إنشاء lead في Salesforce
  2. إرسال إشعار Slack للمبيعات
  3. إضافة المستخدم إلى تسلسل تسويق
  4. تسجيل النشاط في منصة نجاح العملاء

منصة استشارية:

منصة خدمات قانونية تتيح العملاء حجز استشارات. التكامل يشمل:

  1. مزامنة الحجوزات مع النظام الداخلي
  2. إنشاء روابط Zoom
  3. إرسال استبيان قبول قبل 24 ساعة
  4. إنشاء ملف حالة بعد الاجتماع

جدولة المقابلات:

منصة توظيف تستخدم Calendly لمقابلات المرشحين. الـ webhooks:

  1. تحديث نظام تتبع المتقدمين (ATS)
  2. إخطار مدير التوظيف
  3. إرسال دعوات تقويم
  4. تتبع حالات عدم الحضور

الخاتمة

تلخيص عملي:

  • Calendly يستخدم OAuth 2.0 لمصادقة الـ API.
  • يمكنك الوصول لأنواع الأحداث والحجوزات عبر الـ API.
  • Webhooks توفر إشعارات حجز في الوقت الفعلي.
  • تحقق دائمًا من توقيعات الـ webhook.
  • اختبر تكاملك باستخدام Apidog قبل ربطه بالتقويمات الحقيقية.

خطوات التنفيذ:

  1. أنشئ تطبيق OAuth في Calendly
  2. نفّذ تدفق OAuth في تطبيقك
  3. أضف اشتراك webhook
  4. اختبر باستخدام حمولات وهمية في Apidog
  5. انشر التكامل في الإنتاج

اختبر webhooks Calendly باستخدام Apidog — مجانًا.

الأسئلة الشائعة

هل أحتاج إلى خطة Calendly مدفوعة لاستخدام الـ API؟

لا، الـ API متاح لجميع الخطط بما في ذلك المجانية. لكن الميزات محدودة في الخطة المجانية. Webhooks متوفرة للجميع.

ما الفرق بين webhooks على مستوى المستخدم والمؤسسة؟

Webhooks المستخدم تلتقط أحداث مستخدم واحد فقط. Webhooks المؤسسة تلتقط الأحداث لكل أعضاء الفريق. الأغلب يختار نطاق المؤسسة.

كيف أحصل على مفتاح توقيع الـ webhook السري؟

عند إنشاء webhook عبر الـ API، ستجد signing_key في الاستجابة. احفظه بشكل آمن للتحقق من التوقيعات.

هل يمكنني إنشاء الحجوزات عبر الـ API؟

لا. لا يوجد endpoint لإنشاء حجوزات. جميع الحجوزات تتم عبر واجهة مستخدم Calendly أو الأدوات المدمجة. الـ API للقراءة فقط.

كيف أتعامل مع المناطق الزمنية؟

جميع الطوابع الزمنية بصيغة UTC (ISO 8601). حوّل التوقيت محليًا في تطبيقك. منطقة المستخدم الزمنية متاحة في بيانات المستخدم.

ما هو حد المعدل (rate limit)؟

لم يتم توثيقها علنًا. استخدم أنماط طلب معتدلة. إذا واجهت حدودًا، طبّق تراجع أُسّي.

هل يمكنني الحصول على الحجوزات التاريخية؟

نعم، استخدم min_start_time وmax_start_time للاستعلام عن الأحداث القديمة.

كيف أختبر تدفق OAuth محليًا؟

استخدم خدمة مثل ngrok لتعريض endpoint محلي للعامة. اضبط URI إعادة التوجيه على عنوان ngrok وأكمل التدفق في المتصفح.


Top comments (0)