Claude
Skills
Sign in
Back

calendly-api

Included with Lifetime
$97 forever

Calendly scheduling automation using REST API v2 for managing event types, availability, bookings, webhooks, and scheduling workflows

communicationcalendlyschedulingcalendarbookingmeetingsapiautomationwebhooks

What this skill does


# Calendly API Skill

Master Calendly scheduling automation using the REST API v2. This skill covers event type management, availability configuration, booking workflows, webhook integrations, and automated scheduling patterns for building seamless meeting coordination.

## When to Use This Skill

### USE when:
- Automating interview scheduling workflows
- Building meeting booking integrations
- Creating round-robin scheduling systems
- Tracking scheduled events programmatically
- Integrating calendars with CRM systems
- Building appointment reminders
- Creating custom booking confirmation flows
- Automating follow-up sequences after meetings
- Syncing Calendly with external calendars
- Building scheduling analytics dashboards

### DON'T USE when:
- Simple calendar display (use Google Calendar API)
- Real-time video calls (use Zoom/Teams API)
- Complex resource scheduling (use specialized tools)
- Internal meeting coordination only (use calendar apps)
- One-off manual scheduling (use Calendly UI directly)

## Prerequisites

### Calendly API Setup

```bash
# 1. Get API credentials at https://calendly.com/integrations/api_webhooks
# 2. Create a Personal Access Token or OAuth app
# 3. Note: API v2 requires organization-level access for some endpoints

# Personal Access Token:
# - Go to Integrations > API & Webhooks
# - Generate a new token
# - Copy the token (shown only once)

# OAuth 2.0 App:
# - Go to https://developer.calendly.com/
# - Create an OAuth application
# - Configure redirect URIs
# - Note client_id and client_secret

# Required OAuth Scopes:
# - default                    - Basic access
# - organization:read          - Read organization data
# - organization:write         - Manage organization
# - user:read                  - Read user profiles
# - scheduling_link:read       - Read scheduling links
# - event_type:read            - Read event types
# - event_type:write           - Manage event types
# - scheduled_event:read       - Read scheduled events
# - scheduled_event:write      - Manage scheduled events
# - invitee:read               - Read invitee data
# - webhook:read               - Read webhooks
# - webhook:write              - Manage webhooks
```

### Python Environment Setup

```bash
# Create virtual environment
python -m venv calendly-env
source calendly-env/bin/activate  # Linux/macOS
# calendly-env\Scripts\activate   # Windows

# Install dependencies
pip install requests python-dotenv httpx aiohttp

# Create requirements.txt
cat > requirements.txt << 'EOF'
requests>=2.31.0
python-dotenv>=1.0.0
httpx>=0.25.0
aiohttp>=3.9.0
pydantic>=2.5.0
EOF

# Environment variables
cat > .env << 'EOF'
CALENDLY_API_KEY=your-personal-access-token
CALENDLY_CLIENT_ID=your-oauth-client-id
CALENDLY_CLIENT_SECRET=your-oauth-client-secret
CALENDLY_WEBHOOK_SECRET=your-webhook-signing-secret
EOF
```

### API Client Setup

```python
# client.py
# ABOUTME: Calendly API client with authentication
# ABOUTME: Handles requests, pagination, and error handling

import os
import requests
from typing import Optional, Dict, Any, List
from dotenv import load_dotenv

load_dotenv()


class CalendlyClient:
    """Calendly API v2 client"""

    BASE_URL = "https://api.calendly.com"

    def __init__(self, api_key: str = None):
        self.api_key = api_key or os.environ.get("CALENDLY_API_KEY")
        self.session = requests.Session()
        self.session.headers.update({
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json",
        })
        self._current_user = None
        self._organization = None

    def _request(
        self,
        method: str,
        endpoint: str,
        params: Dict = None,
        json: Dict = None,
    ) -> Dict:
        """Make an API request"""
        url = f"{self.BASE_URL}{endpoint}"

        response = self.session.request(
            method=method,
            url=url,
            params=params,
            json=json,
        )

        if response.status_code == 429:
            # Rate limited
            retry_after = int(response.headers.get("Retry-After", 60))
            raise Exception(f"Rate limited. Retry after {retry_after}s")

        response.raise_for_status()

        if response.status_code == 204:
            return {}

        return response.json()

    def get(self, endpoint: str, params: Dict = None) -> Dict:
        """GET request"""
        return self._request("GET", endpoint, params=params)

    def post(self, endpoint: str, json: Dict = None) -> Dict:
        """POST request"""
        return self._request("POST", endpoint, json=json)

    def delete(self, endpoint: str) -> Dict:
        """DELETE request"""
        return self._request("DELETE", endpoint)

    def paginate(
        self,
        endpoint: str,
        params: Dict = None,
        key: str = "collection",
        limit: int = None,
    ) -> List[Dict]:
        """Paginate through results"""
        params = params or {}
        params["count"] = 100
        results = []

        while True:
            response = self.get(endpoint, params)
            items = response.get(key, [])
            results.extend(items)

            if limit and len(results) >= limit:
                return results[:limit]

            pagination = response.get("pagination", {})
            next_page_token = pagination.get("next_page_token")

            if not next_page_token:
                break

            params["page_token"] = next_page_token

        return results

    @property
    def current_user(self) -> Dict:
        """Get current user info (cached)"""
        if not self._current_user:
            response = self.get("/users/me")
            self._current_user = response.get("resource")
        return self._current_user

    @property
    def user_uri(self) -> str:
        """Get current user URI"""
        return self.current_user["uri"]

    @property
    def organization_uri(self) -> str:
        """Get current organization URI"""
        return self.current_user["current_organization"]


# Global client instance
client = CalendlyClient()
```

## Core Capabilities

### 1. User and Organization Management

```python
# users.py
# ABOUTME: User and organization management
# ABOUTME: Retrieve user profiles, organization info, memberships

from client import client


def get_current_user() -> dict:
    """Get the current authenticated user"""
    response = client.get("/users/me")
    user = response.get("resource", {})

    return {
        "uri": user.get("uri"),
        "name": user.get("name"),
        "email": user.get("email"),
        "slug": user.get("slug"),
        "scheduling_url": user.get("scheduling_url"),
        "timezone": user.get("timezone"),
        "organization": user.get("current_organization"),
    }


def get_user_by_uri(user_uri: str) -> dict:
    """Get a user by their URI"""
    # Extract UUID from URI
    uuid = user_uri.split("/")[-1]
    response = client.get(f"/users/{uuid}")
    return response.get("resource", {})


def get_organization(organization_uri: str = None) -> dict:
    """Get organization details"""
    org_uri = organization_uri or client.organization_uri
    uuid = org_uri.split("/")[-1]
    response = client.get(f"/organizations/{uuid}")
    return response.get("resource", {})


def list_organization_memberships(
    organization_uri: str = None,
    email: str = None,
) -> list:
    """List organization memberships"""
    org_uri = organization_uri or client.organization_uri

    params = {"organization": org_uri}
    if email:
        params["email"] = email

    return client.paginate("/organization_memberships", params=params)


def get_user_availability_schedules(user_uri: str = None) -> list:
    """Get user's availability schedules"""
    user = user_uri or client.user_uri
    params = {"user": user}
    return client.paginate("/user_availability_schedules", params=params)


def get_user_busy_times(
    user_uri: str = None,
    start_tim

Related in communication