موصل Calendly
حالة الموصل: مخطط له لم يُبنَ بعد. لا يشغّل Tajo هذا الموصل اليوم.
رسم توضيحي · بيانات تجريبية
مثال على المزامنة
المصدر
Calendly
حالة الموصل: مخطط له
الوجهة
أداتك الأخرى
حالة الموصل: أداة مخصصة
ربط الحقول
| حقل المصدر | حقل الوجهة | قيمة نموذجية |
|---|---|---|
record.id | source_id | record_1042 |
record.updated_at | updated_at | 2026-10-18T08:00:00Z |
record.value | value | sample_value |
- الصياغة
- راجع سجلاً واحداً
- اعتمد هذه النسخة
تصف الحالات كل موصل على حدة، ولا تضمن جاهزية هذه المزامنة. حقول توضيحية. راجع المخطط الفعلي والأذونات قبل تشغيل المزامنة.
اربط Calendly بـ Brevo عبر Tajo لمزامنة المدعوين إلى الاجتماعات تلقائيًا كجهات اتصال، وتشغيل تسلسلات البريد الإلكتروني بناءً على أحداث الحجز، وتبسيط سير عمل المبيعات والتأهيل.
نظرة عامة
| الخاصية | القيمة |
|---|---|
| المنصة | Calendly |
| الفئة | الجدولة (مخصص) |
| تعقيد الإعداد | سهل |
| تكامل رسمي | لا |
| البيانات المتزامنة | الأحداث، جهات الاتصال، الحجوزات، الإلغاءات |
| طريقة المصادقة | OAuth 2.0 / Personal Access Token |
الميزات
- مزامنة المدعوين - أنشئ جهات اتصال Brevo تلقائيًا من المدعوين إلى الاجتماعات
- مشغلات الحجز - أطلق أتمتة Brevo عند حجز الاجتماعات
- معالجة الإلغاء - شغّل تدفقات إعادة التفاعل عند الإلغاءات
- اكتشاف عدم الحضور - حدّث حالة جهة الاتصال عندما يفوّت المدعوون الاجتماعات
- ربط أنواع الأحداث - اربط أنواع أحداث Calendly المختلفة بقوائم Brevo
- API الجدولة - ابنِ الجدولة مباشرة في تطبيقك دون إعادة توجيه
المتطلبات المسبقة
قبل أن تبدأ، تأكد من توفر ما يلي:
- حساب Calendly (خطة Professional أو أعلى للوصول إلى API)
- Personal Access Token من Calendly Integrations
- حساب Brevo مع وصول API
- حساب Tajo مع أذونات الموصلات
المصادقة
Personal Access Token
# Generate at https://calendly.com/integrations/api_webhooksexport CALENDLY_ACCESS_TOKEN=your_personal_access_tokenexport TAJO_API_KEY=your_tajo_api_keyexport BREVO_API_KEY=your_brevo_api_keyOAuth 2.0
// OAuth 2.0 Authorization Code Flowconst authUrl = 'https://auth.calendly.com/oauth/authorize?' + new URLSearchParams({ client_id: process.env.CALENDLY_CLIENT_ID, redirect_uri: 'https://your-app.com/callback', response_type: 'code' });
// Exchange code for tokenconst tokenResponse = await fetch('https://auth.calendly.com/oauth/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: 'authorization_code', code: authorizationCode, client_id: process.env.CALENDLY_CLIENT_ID, client_secret: process.env.CALENDLY_CLIENT_SECRET, redirect_uri: 'https://your-app.com/callback' })});الإعداد
الإعداد الأساسي
connectors: calendly: enabled: true access_token: "${CALENDLY_ACCESS_TOKEN}"
sync: contacts: true events: true cancellations: true
event_mapping: discovery_call: list_id: 10 event_type_uri: "https://api.calendly.com/event_types/abc123" demo: list_id: 11 event_type_uri: "https://api.calendly.com/event_types/xyz789"
webhook: signing_key: "${CALENDLY_WEBHOOK_SIGNING_KEY}"ربط الحقول
field_mapping: email: email name: FIRSTNAME questions_and_answers: company: COMPANY role: JOB_TITLE phone: SMS event_type_name: CALENDLY_EVENT_TYPE scheduled_at: MEETING_DATE status: BOOKING_STATUSنقاط نهاية API
| نقطة النهاية | الطريقة | الوصف |
|---|---|---|
https://api.calendly.com/users/me | GET | الحصول على المستخدم الحالي |
https://api.calendly.com/event_types | GET | عرض أنواع الأحداث |
https://api.calendly.com/scheduled_events | GET | عرض الأحداث المجدولة |
https://api.calendly.com/scheduled_events/{uuid} | GET | الحصول على حدث مجدول |
https://api.calendly.com/scheduled_events/{uuid}/invitees | GET | عرض المدعوين |
https://api.calendly.com/scheduling_links | POST | إنشاء رابط جدولة |
https://api.calendly.com/webhook_subscriptions | POST | إنشاء webhook |
https://api.calendly.com/webhook_subscriptions | GET | عرض webhooks |
https://api.calendly.com/invitee_no_shows/{uuid} | GET | الحصول على حالة عدم الحضور |
أمثلة البرمجة
تهيئة الموصل
import { TajoClient } from '@tajo/sdk';
const tajo = new TajoClient({ apiKey: process.env.TAJO_API_KEY, brevoApiKey: process.env.BREVO_API_KEY});
await tajo.connectors.connect('calendly', { accessToken: process.env.CALENDLY_ACCESS_TOKEN});عرض الأحداث المجدولة
// Retrieve scheduled eventsconst response = await fetch( 'https://api.calendly.com/scheduled_events?' + new URLSearchParams({ user: 'https://api.calendly.com/users/YOUR_USER_ID', min_start_time: '2024-01-01T00:00:00Z', max_start_time: '2024-12-31T23:59:59Z', status: 'active', count: 100 }), { headers: { 'Authorization': `Bearer ${process.env.CALENDLY_ACCESS_TOKEN}`, 'Content-Type': 'application/json' } });
const events = await response.json();مزامنة المدعوين إلى Brevo
// Get invitees for a scheduled event and sync to Brevoconst inviteesResponse = await fetch( `https://api.calendly.com/scheduled_events/${eventUuid}/invitees`, { headers: { 'Authorization': `Bearer ${process.env.CALENDLY_ACCESS_TOKEN}` } });
const { collection } = await inviteesResponse.json();
for (const invitee of collection) { await tajo.contacts.sync({ email: invitee.email, attributes: { FIRSTNAME: invitee.name, CALENDLY_EVENT_TYPE: invitee.event, MEETING_DATE: invitee.created_at, BOOKING_STATUS: invitee.status }, listIds: [10] });}إعداد اشتراكات Webhook
// Subscribe to Calendly eventsconst webhook = await fetch( 'https://api.calendly.com/webhook_subscriptions', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.CALENDLY_ACCESS_TOKEN}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ url: 'https://api.example.com/webhooks/calendly', events: [ 'invitee.created', 'invitee.canceled', 'invitee_no_show.created' ], organization: 'https://api.calendly.com/organizations/YOUR_ORG_ID', scope: 'organization', signing_key: process.env.CALENDLY_WEBHOOK_SIGNING_KEY }) });Caution
عنوان example.com أعلاه عنوان مؤقت إلى أن تصبح نقطة نهاية Tajo متاحة.
معالجة أحداث Webhook
app.post('/webhooks/calendly', async (req, res) => { // Verify webhook signature const signature = req.headers['calendly-webhook-signature']; const isValid = verifyCalendlySignature( req.rawBody, signature, process.env.CALENDLY_WEBHOOK_SIGNING_KEY );
if (!isValid) return res.status(401).send('Unauthorized');
const { event, payload } = req.body;
switch (event) { case 'invitee.created': await tajo.contacts.sync({ email: payload.email, attributes: { BOOKING_STATUS: 'booked' }, listIds: [10] }); break; case 'invitee.canceled': await tajo.contacts.update(payload.email, { attributes: { BOOKING_STATUS: 'cancelled' } }); break; case 'invitee_no_show.created': await tajo.contacts.update(payload.email, { attributes: { BOOKING_STATUS: 'no_show' } }); break; }
res.status(200).send('OK');});حدود المعدل
| المورد | الحد | ملاحظات |
|---|---|---|
| طلبات API | 6,000/دقيقة | حد على مستوى المؤسسة |
| اشتراكات Webhook | 30 لكل مؤسسة | عبر جميع أنواع الأحداث |
| روابط الجدولة | غير محدود | لا يوجد حد لكل دقيقة |
الترقيم
تستخدم استجابات Calendly API الترقيم القائم على المؤشر. استخدم next_page_token من كائن pagination لاسترجاع النتائج الإضافية. حجم الصفحة الافتراضي هو 20 عنصرًا، بحد أقصى 100.
استكشاف الأخطاء
| المشكلة | السبب | الحل |
|---|---|---|
| Webhook لم يُستلم | نطاق خاطئ | استخدم نطاق organization لـ webhooks |
| 401 Unauthorized | انتهت صلاحية الرمز | ولّد رمزًا جديدًا أو جدد رمز OAuth |
| بيانات المدعو مفقودة | الأسئلة غير مُعدة | أضف أسئلة مخصصة إلى نوع الحدث |
| جهات اتصال مكررة | لا يوجد منطق إزالة تكرار | استخدم البريد الإلكتروني كمعرف فريد للـ upserts |
| حد معدل 429 | عدد كبير من الطلبات | نفّذ التراجع وتجميع الطلبات |
وضع التصحيح
connectors: calendly: debug: true log_level: verbose log_webhooks: trueأفضل الممارسات
- استخدم webhooks - اشترك في
invitee.createdوinvitee.canceledللمزامنة في الوقت الفعلي - أضف أسئلة مخصصة - اجمع بيانات الشركة والدور والهاتف لملفات جهات اتصال أغنى
- اربط أنواع الأحداث - عيّن قوائم Brevo مختلفة لكل نوع حدث Calendly
- تعامل مع عدم الحضور - تتبع حالات عدم الحضور لضبط تسجيل العملاء المحتملين وتسلسلات المتابعة
- استخدم روابط الجدولة - ولّد روابط جدولة فريدة لتجارب حجز مخصصة
- اضبط نطاق المؤسسة - استخدم webhooks على مستوى المؤسسة لالتقاط الأحداث من جميع أعضاء الفريق
الأمان
- OAuth 2.0 - مصادقة قائمة على الرمز محددة النطاق
- توقيعات Webhook - التحقق من توقيع HMAC للـ webhooks الواردة
- HTTPS فقط - جميع نقاط نهاية API تتطلب تشفير TLS
- انتهاء صلاحية الرمز - رموز OAuth تنتهي صلاحيتها وتتطلب تدفقات تحديث
- الحد الأدنى من النطاقات - اطلب فقط نطاقات OAuth المطلوبة
- التخزين الآمن - خزّن الرموز في متغيرات البيئة أو مديري الأسرار