Calendly Connector

Connect Calendly to Brevo through Tajo to automatically sync meeting invitees as contacts, trigger email sequences based on booking events, and streamline your sales and onboarding workflows.

Overview

PropertyValue
PlatformCalendly
CategoryScheduling (Custom)
Setup ComplexityEasy
Official IntegrationNo
Data SyncedEvents, Contacts, Bookings, Cancellations
Auth MethodOAuth 2.0 / Personal Access Token

Features

  • Invitee sync - Automatically create Brevo contacts from meeting invitees
  • Booking triggers - Fire Brevo automations when meetings are booked
  • Cancellation handling - Trigger re-engagement flows on cancellations
  • No-show detection - Update contact status when invitees miss meetings
  • Event type mapping - Map different Calendly event types to Brevo lists
  • Scheduling API - Build scheduling directly into your app without redirects

Prerequisites

Before you begin, ensure you have:

  1. A Calendly account (Professional plan or above for API access)
  2. A Personal Access Token from Calendly Integrations
  3. A Brevo account with API access
  4. A Tajo account with connector permissions

Authentication

Personal Access Token

Terminal window
# Generate at https://calendly.com/integrations/api_webhooks
export CALENDLY_ACCESS_TOKEN=your_personal_access_token
export TAJO_API_KEY=your_tajo_api_key
export BREVO_API_KEY=your_brevo_api_key

OAuth 2.0

// OAuth 2.0 Authorization Code Flow
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'
});
// Exchange code for token
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'
})
});

Configuration

Basic Setup

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

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 Endpoints

EndpointMethodDescription
https://api.calendly.com/users/meGETGet current user
https://api.calendly.com/event_typesGETList event types
https://api.calendly.com/scheduled_eventsGETList scheduled events
https://api.calendly.com/scheduled_events/{uuid}GETGet a scheduled event
https://api.calendly.com/scheduled_events/{uuid}/inviteesGETList invitees
https://api.calendly.com/scheduling_linksPOSTCreate scheduling link
https://api.calendly.com/webhook_subscriptionsPOSTCreate webhook
https://api.calendly.com/webhook_subscriptionsGETList webhooks
https://api.calendly.com/invitee_no_shows/{uuid}GETGet no-show status

Code Examples

Initialize Connector

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
});

List Scheduled Events

// Retrieve scheduled events
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();

Sync Invitees to Brevo

// Get invitees for a scheduled event and sync to 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]
});
}

Set Up Webhook Subscriptions

// Subscribe to Calendly events
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
})
}
);

Handle Webhook Events

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');
});

Rate Limits

ResourceLimitNotes
API requests6,000/minOrganization-wide limit
Webhook subscriptions30 per organizationAcross all event types
Scheduling linksUnlimitedNo per-minute limit

Pagination

Calendly API responses use cursor-based pagination. Use the next_page_token from the pagination object to retrieve additional results. The default page size is 20 items, with a maximum of 100.

Troubleshooting

IssueCauseSolution
Webhook not receivedWrong scopeUse organization scope for webhooks
401 UnauthorizedToken expiredGenerate new token or refresh OAuth token
Missing invitee dataQuestions not configuredAdd custom questions to event type
Duplicate contactsNo dedup logicUse email as unique identifier for upserts
Rate limit 429Too many requestsImplement backoff and request batching

Debug Mode

connectors:
calendly:
debug: true
log_level: verbose
log_webhooks: true

Best Practices

  1. Use webhooks - Subscribe to invitee.created and invitee.canceled for real-time sync
  2. Add custom questions - Collect company, role, and phone data for richer contact profiles
  3. Map event types - Assign different Brevo lists per Calendly event type
  4. Handle no-shows - Track no-shows to adjust lead scoring and follow-up sequences
  5. Use scheduling links - Generate unique scheduling links for personalized booking experiences
  6. Set organization scope - Use org-level webhooks to capture events from all team members

Security

  • OAuth 2.0 - Scoped token-based authentication
  • Webhook signatures - HMAC signature validation for incoming webhooks
  • HTTPS only - All API endpoints require TLS encryption
  • Token expiry - OAuth tokens expire and require refresh flows
  • Minimal scopes - Request only required OAuth scopes
  • Secure storage - Store tokens in environment variables or secret managers

Open-Source Implementation Map

No official open-source repository was found in the current Tajo connector catalog for Calendly. Keep this page focused on the verified public API contract and vendor documentation until an official schema, SDK, MCP server, or public integration repository is available.

Tajo Revamp Checklist

  • Verify authentication and scope requirements against the vendor documentation before each connector release.
  • Document primary sync objects, external IDs, pagination strategy, and rate limits explicitly.
  • Add smoke tests from public API examples rather than undocumented behavior.
  • Capture webhook signature verification and replay protection when the vendor supports webhooks.
  • Record gaps where no official public repository or schema exists so future maintainers know what still needs source-backed validation.

Subscribe to updates

developer-docs

Drop your email or phone number — we'll send you what matters next.

auto-detect
AI Assistant

Hi! Ask me anything about the docs.