---
title: "Calendly Connector"
description: "Complete Calendly integration guide for syncing scheduling events, invitee data, and meeting workflows with Brevo through Tajo."
url: https://tajo.io/docs/connectors/custom/calendly/
markdown: https://tajo.io/docs/connectors/custom/calendly.md
lang: en-US
robots: noindex
---

# Calendly Connector

Connector status: Planned Not built yet. Tajo does not run this connector today.

| Source field | Destination field | Sample value |
| --- | --- | --- |
| `record.id` | `source_id` | `record_1042` |
| `record.updated_at` | `updated_at` | `2026-10-18T08:00:00Z` |
| `record.value` | `value` | `sample_value` |

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

| Property | Value |
| --- | --- |
| **Platform** | Calendly |
| **Category** | Scheduling (Custom) |
| **Setup Complexity** | Easy |
| **Official Integration** | No |
| **Data Synced** | Events, Contacts, Bookings, Cancellations |
| **Auth Method** | OAuth 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](https://calendly.com/integrations)
3. A Brevo account with API access
4. A Tajo account with connector permissions

## Authentication

### Personal Access Token

```bash
# 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

```javascript
// 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

```yaml
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

```yaml
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

| Endpoint | Method | Description |
| --- | --- | --- |
| `https://api.calendly.com/users/me` | GET | Get current user |
| `https://api.calendly.com/event_types` | GET | List event types |
| `https://api.calendly.com/scheduled_events` | GET | List scheduled events |
| `https://api.calendly.com/scheduled_events/{uuid}` | GET | Get a scheduled event |
| `https://api.calendly.com/scheduled_events/{uuid}/invitees` | GET | List invitees |
| `https://api.calendly.com/scheduling_links` | POST | Create scheduling link |
| `https://api.calendly.com/webhook_subscriptions` | POST | Create webhook |
| `https://api.calendly.com/webhook_subscriptions` | GET | List webhooks |
| `https://api.calendly.com/invitee_no_shows/{uuid}` | GET | Get no-show status |

## Code Examples

### Initialize Connector

```javascript
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

```javascript
// 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

```javascript
// 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

```javascript
// 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.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

The `example.com` address above is a placeholder until the Tajo endpoint is available.

### Handle Webhook Events

```javascript
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

| Resource | Limit | Notes |
| --- | --- | --- |
| API requests | 6,000/min | Organization-wide limit |
| Webhook subscriptions | 30 per organization | Across all event types |
| Scheduling links | Unlimited | No 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

| Issue | Cause | Solution |
| --- | --- | --- |
| Webhook not received | Wrong scope | Use `organization` scope for webhooks |
| 401 Unauthorized | Token expired | Generate new token or refresh OAuth token |
| Missing invitee data | Questions not configured | Add custom questions to event type |
| Duplicate contacts | No dedup logic | Use email as unique identifier for upserts |
| Rate limit 429 | Too many requests | Implement backoff and request batching |

### Debug Mode

```yaml
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

## Related Resources

- [Typeform Connector](https://tajo.io/docs/connectors/custom/typeform)
- [Zoom Connector](https://tajo.io/docs/connectors/custom/zoom)
- [Customer Sync Skill](https://tajo.io/docs/skills/data-sync/customer-sync)
- [Calendly Developer API Documentation](https://developer.calendly.com/api-docs)

## 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.
