Calendly コネクタ
Tajo を介して Calendly を Brevo に接続し、ミーティング招待者を連絡先として自動的に同期し、予約イベントに基づいてメールシーケンスをトリガーし、営業およびオンボーディングワークフローを効率化します。
概要
| プロパティ | 値 |
|---|---|
| プラットフォーム | Calendly |
| カテゴリ | スケジューリング(カスタム) |
| セットアップの複雑さ | 簡単 |
| 公式統合 | いいえ |
| 同期データ | イベント、連絡先、予約、キャンセル |
| 認証方式 | OAuth 2.0 / Personal Access Token |
機能
- 招待者の同期 - ミーティング招待者から Brevo 連絡先を自動作成
- 予約トリガー - ミーティングが予約されたときに Brevo 自動化を起動
- キャンセル処理 - キャンセル時に再エンゲージメントフローをトリガー
- ノーショー検出 - 招待者がミーティングに参加しなかった場合に連絡先ステータスを更新
- イベントタイプマッピング - 異なる Calendly イベントタイプを Brevo リストにマッピング
- スケジューリング API - リダイレクトなしでアプリに直接スケジューリングを構築
前提条件
開始する前に、以下を準備してください。
- Calendly アカウント(API アクセスには Professional プラン以上が必要)
- Calendly Integrations からの Personal Access Token
- API アクセス可能な Brevo アカウント
- コネクタ権限を持つ Tajo アカウント
認証
Personal Access Token
# https://calendly.com/integrations/api_webhooks で生成export 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 認可コードフローconst 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' });
// コードをトークンと交換const 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_STATUSAPI エンドポイント
| エンドポイント | メソッド | 説明 |
|---|---|---|
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 | Webhook を一覧取得 |
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});スケジュール済みイベントの一覧取得
// スケジュール済みイベントを取得const 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 に同期
// スケジュール済みイベントの招待者を取得して Brevo に同期const 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 サブスクリプションの設定
// Calendly イベントを購読const 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.tajo.io/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 }) });Webhook イベントの処理
app.post('/webhooks/calendly', async (req, res) => { // Webhook 署名を検証 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 のレスポンスはカーソルベースのページネーションを使用します。追加の結果を取得するには、pagination オブジェクトの next_page_token を使用してください。デフォルトのページサイズは 20 アイテム、最大 100 です。
トラブルシューティング
| 問題 | 原因 | 解決策 |
|---|---|---|
| Webhook を受信できない | 間違ったスコープ | Webhook には organization スコープを使用 |
| 401 Unauthorized | トークンの期限切れ | 新しいトークンを生成するか OAuth トークンをリフレッシュ |
| 招待者データが欠落 | 質問が未設定 | イベントタイプにカスタム質問を追加 |
| 重複した連絡先 | 重複排除ロジックなし | アップサートにメールを一意識別子として使用 |
| レート制限 429 | リクエスト過多 | バックオフとリクエストバッチ処理を実装 |
デバッグモード
connectors: calendly: debug: true log_level: verbose log_webhooks: trueベストプラクティス
- Webhook を使用する - リアルタイム同期のため
invitee.createdとinvitee.canceledを購読 - カスタム質問を追加する - より豊かな連絡先プロファイルのために会社、役職、電話データを収集
- イベントタイプをマッピングする - Calendly イベントタイプごとに異なる Brevo リストを割り当て
- ノーショーを処理する - リードスコアリングとフォローアップシーケンスを調整するためにノーショーをトラッキング
- スケジューリングリンクを使用する - パーソナライズされた予約体験のために一意のスケジューリングリンクを生成
- 組織スコープを設定する - すべてのチームメンバーのイベントをキャプチャするため組織レベルの Webhook を使用
セキュリティ
- OAuth 2.0 - スコープ付きトークンベース認証
- Webhook 署名 - 受信 Webhook の HMAC 署名検証
- HTTPS のみ - すべての API エンドポイントに TLS 暗号化が必要
- トークンの有効期限 - OAuth トークンは期限切れになり、リフレッシュフローが必要
- 最小限のスコープ - 必要な OAuth スコープのみリクエスト
- 安全なストレージ - トークンを環境変数またはシークレットマネージャーに保存