Schedly API Reference

The Schedly API is organized around REST. Our API accepts JSON-encoded request bodies, returns JSON-encoded responses, and uses standard HTTP response codes, authentication, and verbs.

You can use the Schedly API to build powerful scheduling integrations, automate booking workflows, manage services and appointments, and control your entire scheduling infrastructure programmatically.

Base URL

All API requests are made to:

https://app.schedly.io

The Schedly Services API is available at /api/services/. Routing endpoints are at /api/routing/. Billing endpoints are at /api/billing/. Booking endpoints are at /api/book/.

All endpoints require authentication via API Key or OAuth Bearer token. See the Authentication section below.

Base URL

https://app.schedly.io

API Endpoints

# Schedly Services
https://app.schedly.io/api/services/services
https://app.schedly.io/api/services/appointments
https://app.schedly.io/api/services/customers

# OAuth
https://app.schedly.io/auth/oauth2/authorize
https://app.schedly.io/api/auth/oauth/token

# Routing
https://app.schedly.io/api/routing/config
https://app.schedly.io/api/routing/execute

Authentication

Schedly uses two authentication methods. Choose the one that fits your integration:

API Key

Pass your API key in the cal-api-key header. Create API keys from Settings → Developer → API Keys at app.schedly.io.

API keys are best for server-to-server integrations where your application acts on behalf of itself.

OAuth 2.0 Bearer Token

Pass an OAuth access token in the Authorization header as a Bearer token. This is the recommended method for third-party apps that act on behalf of Schedly users.

See the OAuth 2.0 section for the complete authorization flow.

Never expose API keys or tokens in client-side code. Always keep credentials on your server.

API Key Authentication

cURL
JavaScript
Python
curl https://app.schedly.io/api/auth/oauth/me \
  -H "Authorization: Bearer sk_live_abc123def456..."
const response = await fetch(
  'https://app.schedly.io/api/auth/oauth/me',
  {
    headers: {
      'Authorization': 'Bearer sk_live_abc123def456...'
    }
  }
);
const data = await response.json();
import requests

response = requests.get(
    "https://app.schedly.io/api/auth/oauth/me",
    headers={"Authorization": "Bearer sk_live_abc123def456..."}
)
data = response.json()

OAuth Bearer Token

cURL
JavaScript
Python
curl https://app.schedly.io/api/auth/oauth/me \
  -H "Authorization: Bearer eyJhbGciOi..."
const response = await fetch(
  'https://app.schedly.io/api/auth/oauth/me',
  {
    headers: {
      'Authorization': 'Bearer eyJhbGciOi...'
    }
  }
);
import requests

response = requests.get(
    "https://app.schedly.io/api/auth/oauth/me",
    headers={"Authorization": "Bearer eyJhbGciOi..."}
)

Rate Limiting

API requests are rate limited to ensure fair usage across all clients.

PlanLimitWindow
Free60 requestsper minute
Pro120 requestsper minute
Enterprise600 requestsper minute

Rate limit information is included in response headers:

  • X-RateLimit-Limit — Maximum requests per window
  • X-RateLimit-Remaining — Remaining requests
  • X-RateLimit-Reset — Unix timestamp when window resets

When rate limited, you'll receive a 429 status code. Use exponential backoff when retrying.

Rate Limit Headers

HTTP/1.1 200 OK
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 98
X-RateLimit-Reset: 1708372800

Rate Limited Response

HTTP/1.1 429 Too Many Requests

{
  "status": "error",
  "error": "RATE_LIMITED",
  "message": "Too many requests. Please retry after 60 seconds."
}

Errors

Schedly uses conventional HTTP response codes to indicate the success or failure of an API request. Codes in the 2xx range indicate success, 4xx indicate client errors, and 5xx indicate server errors.

CodeMeaning
200OK — Request succeeded
201Created — Resource created
400Bad Request — Invalid parameters
401Unauthorized — Invalid or missing auth
403Forbidden — Insufficient permissions
404Not Found — Resource doesn't exist
409Conflict — Resource conflict
422Unprocessable — Validation failed
429Rate Limited — Too many requests
500Server Error — Something went wrong

Success Response

{
  "status": "success",
  "data": {
    "id": 42,
    "name": "Consultation"
  }
}

Error Response

{
  "status": "error",
  "error": "NOT_FOUND",
  "message": "The requested resource was not found."
}

Validation Error

{
  "status": "error",
  "error": "VALIDATION_ERROR",
  "message": "Invalid request parameters",
  "details": [
    {
      "field": "email",
      "message": "Must be a valid email address"
    }
  ]
}

OAuth 2.0

Schedly implements the OAuth 2.0 Authorization Code flow (RFC 6749) with optional PKCE extension (RFC 7636). This is the recommended authentication method for third-party integrations that act on behalf of Schedly users.

Flow Overview

  1. Create an OAuth Client in your Schedly developer settings
  2. Redirect users to the Schedly authorization page
  3. Exchange the authorization code for access & refresh tokens
  4. Use the access token to make API calls
  5. Refresh the access token when it expires
Access tokens expire in 30 minutes (1800 seconds). Refresh tokens are valid for 1 year. Always store refresh tokens securely.

Two Client Types

TypeUse CaseSecret
ConfidentialServer-side apps (Node.js, Python, etc.)Uses client_secret
Public (PKCE)Browser apps, mobile apps, SPAsUses code_challenge / code_verifier

OAuth Flow Diagram

┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│  Your App    │     │   Schedly    │     │   User       │
└──────┬───────┘     └──────┬───────┘     └──────┬───────┘
       │                    │                    │
       │ 1. Redirect to     │                    │
       │ /auth/oauth2/      │                    │
       │ authorize ─────────┼───────────────────>│
       │                    │                    │
       │                    │  2. User approves  │
       │                    │<───────────────────│
       │                    │                    │
       │ 3. Callback with   │                    │
       │ authorization code │                    │
       │<───────────────────│                    │
       │                    │                    │
       │ 4. Exchange code   │                    │
       │ for tokens ────────>                    │
       │                    │                    │
       │ 5. Access token +  │                    │
       │ refresh token      │                    │
       │<───────────────────│                    │
       │                    │                    │

Create OAuth Client

Before starting the OAuth flow, create an OAuth client in your Schedly developer settings:

  1. Navigate to app.schedly.io/settings/developer/oauth
  2. Click "Create New OAuth Client"
  3. Enter your application name and description
  4. Add one or more Redirect URIs
  5. Choose client type: Confidential or Public
  6. Save and copy your client_id and client_secret
Keep your client_secret secure. Never expose it in client-side code, public repositories, or frontend bundles.

OAuth Client Credentials

# After creating your OAuth client,
# you'll receive:

Client ID:     cl_live_a1b2c3d4e5f6...
Client Secret: cs_live_x9y8z7w6v5u4...

# Redirect URI (must match exactly)
https://yourapp.com/callback

Authorization

Direct users to the Schedly authorization page to grant your application access.

Authorization URL

https://app.schedly.io/auth/oauth2/authorize

ParameterStatusDescription
client_idRequiredYour OAuth client ID
redirect_uriRequiredMust match a registered redirect URI
response_typeOptionalSet to code (default)
stateRequiredOpaque value for CSRF protection, returned unchanged
code_challengeOptionalPKCE: Base64url-encoded SHA-256 hash of code_verifier
code_challenge_methodOptionalSet to S256 when using PKCE

Callback

After the user approves, Schedly redirects to your redirect_uri with an authorization code and your state parameter.

Error Responses

ErrorDescription
client_not_foundThe client_id doesn't match any registered client
redirect_uri_mismatchThe redirect_uri doesn't match any registered URI
not_approvedThe OAuth client has not been approved
access_deniedThe user denied the authorization request

Authorization URL

cURL
JavaScript
Python
# Redirect the user's browser to:
https://app.schedly.io/auth/oauth2/authorize?\
  client_id=cl_live_a1b2c3d4e5f6&\
  redirect_uri=https://yourapp.com/callback&\
  response_type=code&\
  state=random_csrf_token_xyz
const params = new URLSearchParams({
  client_id: 'cl_live_a1b2c3d4e5f6',
  redirect_uri: 'https://yourapp.com/callback',
  response_type: 'code',
  state: crypto.randomUUID()
});

window.location.href =
  `https://app.schedly.io/auth/oauth2/authorize?${params}`;
import secrets
from urllib.parse import urlencode

params = urlencode({
    "client_id": "cl_live_a1b2c3d4e5f6",
    "redirect_uri": "https://yourapp.com/callback",
    "response_type": "code",
    "state": secrets.token_urlsafe(32)
})

auth_url = f"https://app.schedly.io/auth/oauth2/authorize?{params}"

Callback Response

# User approves → redirect to:
https://yourapp.com/callback?
  code=auth_code_abc123xyz789&
  state=random_csrf_token_xyz

# User denies → redirect to:
https://yourapp.com/callback?
  error=access_denied&
  error_description=User+denied+access

Token Exchange

POST /api/auth/oauth/token

Exchange the authorization code for access and refresh tokens.

Request Body

ParameterStatusDescription
grant_typeRequiredSet to authorization_code
codeRequiredThe authorization code from the callback
client_idRequiredYour OAuth client ID
client_secretConditionalRequired for confidential clients
redirect_uriRequiredMust match the URI used in authorization
code_verifierConditionalRequired for PKCE flow

Response

Returns an access token, refresh token, token type, and expiration time.

Error Responses

ErrorDescription
invalid_grantThe authorization code is invalid or expired
invalid_clientClient authentication failed
invalid_requestMissing required parameters

Exchange Code for Tokens

cURL
JavaScript
Python
curl -X POST https://app.schedly.io/api/auth/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "code=auth_code_abc123xyz789" \
  -d "client_id=cl_live_a1b2c3d4e5f6" \
  -d "client_secret=cs_live_x9y8z7w6v5u4" \
  -d "redirect_uri=https://yourapp.com/callback"
const response = await fetch(
  'https://app.schedly.io/api/auth/oauth/token',
  {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: new URLSearchParams({
      grant_type: 'authorization_code',
      code: 'auth_code_abc123xyz789',
      client_id: 'cl_live_a1b2c3d4e5f6',
      client_secret: 'cs_live_x9y8z7w6v5u4',
      redirect_uri: 'https://yourapp.com/callback'
    })
  }
);
const tokens = await response.json();
import requests

response = requests.post(
    "https://app.schedly.io/api/auth/oauth/token",
    data={
        "grant_type": "authorization_code",
        "code": "auth_code_abc123xyz789",
        "client_id": "cl_live_a1b2c3d4e5f6",
        "client_secret": "cs_live_x9y8z7w6v5u4",
        "redirect_uri": "https://yourapp.com/callback"
    }
)
tokens = response.json()

Response

{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "refresh_token": "rt_live_m4n5o6p7q8r9...",
  "token_type": "Bearer",
  "expires_in": 1800
}

Refresh Token

POST /api/auth/oauth/refreshToken

Use a refresh token to obtain a new access token when the current one expires.

ParameterStatusDescription
grant_typeRequiredSet to refresh_token
refresh_tokenRequiredThe refresh token from the initial exchange
client_idRequiredYour OAuth client ID
client_secretConditionalRequired for confidential clients

Refresh Access Token

cURL
JavaScript
Python
curl -X POST https://app.schedly.io/api/auth/oauth/refreshToken \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=refresh_token" \
  -d "refresh_token=rt_live_m4n5o6p7q8r9..." \
  -d "client_id=cl_live_a1b2c3d4e5f6" \
  -d "client_secret=cs_live_x9y8z7w6v5u4"
const response = await fetch(
  'https://app.schedly.io/api/auth/oauth/refreshToken',
  {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      grant_type: 'refresh_token',
      refresh_token: 'rt_live_m4n5o6p7q8r9...',
      client_id: 'cl_live_a1b2c3d4e5f6',
      client_secret: 'cs_live_x9y8z7w6v5u4'
    })
  }
);
response = requests.post(
    "https://app.schedly.io/api/auth/oauth/refreshToken",
    data={
        "grant_type": "refresh_token",
        "refresh_token": "rt_live_m4n5o6p7q8r9...",
        "client_id": "cl_live_a1b2c3d4e5f6",
        "client_secret": "cs_live_x9y8z7w6v5u4"
    }
)

Response

{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "refresh_token": "rt_live_newtoken123...",
  "token_type": "Bearer",
  "expires_in": 1800
}

Verify Token

GET /api/auth/oauth/me

Verify an access token and retrieve the authenticated user's information.

Returns the user profile if the token is valid, or a 401 error if the token is invalid or expired.

Verify Token

cURL
JavaScript
Python
curl https://app.schedly.io/api/auth/oauth/me \
  -H "Authorization: Bearer eyJhbGciOi..."
const response = await fetch(
  'https://app.schedly.io/api/auth/oauth/me',
  { headers: { 'Authorization': 'Bearer eyJhbGciOi...' } }
);
const user = await response.json();
response = requests.get(
    "https://app.schedly.io/api/auth/oauth/me",
    headers={"Authorization": "Bearer eyJhbGciOi..."}
)
user = response.json()

Response

{
  "username": "sarahj"
}

Booking & Events

Legacy booking endpoints for direct event booking.

Book Event

POST /api/book/event

Book Recurring Event

POST /api/book/recurring-event

Instant Booking

POST /api/book/instant-event

Cancel Booking

POST /api/cancel

Book Event

curl -X POST https://app.schedly.io/api/book/event \
  -H "Content-Type: application/json" \
  -d '{
    "eventTypeId": 42,
    "start": "2026-02-25T14:00:00Z",
    "end": "2026-02-25T14:30:00Z",
    "responses": {
      "name": "Maria Garcia",
      "email": "maria@example.com"
    },
    "timeZone": "America/Los_Angeles",
    "language": "en"
  }'

Cancel Booking

curl -X POST https://app.schedly.io/api/cancel \
  -H "Content-Type: application/json" \
  -d '{
    "uid": "bk_2f8a9c3e",
    "reason": "Schedule conflict"
  }'

Schedly Services API

The Services API powers Schedly's full business management platform — services, appointments, customers, employees, invoices, products, and more.

All Services API endpoints require authentication via cal-api-key header or session cookie.

Services

GET /api/services/services — List all services

POST /api/services/services — Create a service

PATCH /api/services/services?id=:id — Update a service

DELETE /api/services/services?id=:id — Delete a service

Create Service Parameters

ParameterStatusDescription
nameRequiredService name
durationOptionalDuration in minutes (default: 60)
priceOptionalPrice in cents (default: 0)
currencyOptionalCurrency code (default: usd)
descriptionOptionalService description
categoryIdOptionalCategory ID
locationIdOptionalLocation ID
bufferTimeBeforeOptionalBuffer time before (minutes)
bufferTimeAfterOptionalBuffer time after (minutes)
maxCapacityOptionalMax booking capacity
depositTypeOptionalnone, fixed, or percentage
depositAmountOptionalDeposit amount
enableRecurringOptionalEnable recurring bookings
enableGroupBookingOptionalEnable group bookings

Create Service

cURL
JavaScript
Python
curl -X POST https://app.schedly.io/api/services/services \
  -H "cal-api-key: sk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Haircut",
    "duration": 30,
    "price": 3500,
    "currency": "usd",
    "description": "Professional haircut service",
    "bufferTimeAfter": 10
  }'
const res = await fetch('https://app.schedly.io/api/services/services', {
  method: 'POST',
  headers: {
    'cal-api-key': 'sk_live_abc123...',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: 'Haircut',
    duration: 30,
    price: 3500,
    currency: 'usd',
    description: 'Professional haircut service'
  })
});
res = requests.post(
    "https://app.schedly.io/api/services/services",
    headers={"cal-api-key": "sk_live_abc123..."},
    json={
        "name": "Haircut",
        "duration": 30,
        "price": 3500,
        "currency": "usd",
        "description": "Professional haircut service"
    }
)

Response

{
  "id": 1,
  "name": "Haircut",
  "slug": "haircut",
  "duration": 30,
  "price": 3500,
  "currency": "usd",
  "description": "Professional haircut service",
  "active": true,
  "category": null,
  "extras": []
}

Appointments

GET /api/services/appointments — List appointments

POST /api/services/appointments — Create appointment

PUT /api/services/appointments?id=:id — Update appointment

DELETE /api/services/appointments?id=:id — Delete appointment

GET /api/services/appointments/export — Export as CSV

POST /api/services/appointments/import — Import from CSV

Create Appointment Parameters

ParameterStatusDescription
serviceIdRequiredService ID
startTimeRequiredStart time (ISO 8601)
endTimeRequiredEnd time (ISO 8601)
customerIdOptionalCustomer ID
employeeIdOptionalAssigned employee ID
locationIdOptionalLocation ID
statusOptionalpending, approved, canceled, completed
priceOptionalPrice in cents
recurrenceRuleOptionalweekly, biweekly, or monthly
recurrenceCountOptionalNumber of recurring instances
isGroupBookingOptionalEnable group booking
attendeesOptionalArray of attendee objects

Create Appointment

cURL
JavaScript
Python
curl -X POST https://app.schedly.io/api/services/appointments \
  -H "cal-api-key: sk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "serviceId": 1,
    "startTime": "2026-02-25T10:00:00Z",
    "endTime": "2026-02-25T10:30:00Z",
    "customerId": 5,
    "employeeId": 3,
    "status": "approved",
    "price": 3500
  }'
const res = await fetch('https://app.schedly.io/api/services/appointments', {
  method: 'POST',
  headers: {
    'cal-api-key': 'sk_live_abc123...',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    serviceId: 1,
    startTime: '2026-02-25T10:00:00Z',
    endTime: '2026-02-25T10:30:00Z',
    customerId: 5,
    employeeId: 3
  })
});
res = requests.post(
    "https://app.schedly.io/api/services/appointments",
    headers={"cal-api-key": "sk_live_abc123..."},
    json={
        "serviceId": 1,
        "startTime": "2026-02-25T10:00:00Z",
        "endTime": "2026-02-25T10:30:00Z",
        "customerId": 5,
        "employeeId": 3
    }
)

Response

{
  "id": 201,
  "serviceId": 1,
  "startTime": "2026-02-25T10:00:00Z",
  "endTime": "2026-02-25T10:30:00Z",
  "status": "approved",
  "price": 3500,
  "service": { "name": "Haircut" },
  "customer": {
    "name": "Emily Chen",
    "email": "emily@example.com"
  },
  "employee": { "name": "James Wilson" }
}

Customers

GET /api/services/customers — List customers

POST /api/services/customers — Create customer

PUT /api/services/customers?id=:id — Update customer

DELETE /api/services/customers?id=:id — Delete customer

GET /api/services/customers/export — Export as CSV

POST /api/services/customers/import — Import from CSV

ParameterStatusDescription
nameRequiredCustomer name
emailOptionalEmail address
phoneOptionalPhone number
notesOptionalInternal notes
genderOptionalGender
dateOfBirthOptionalDate of birth (ISO date)

Create Customer

curl -X POST https://app.schedly.io/api/services/customers \
  -H "cal-api-key: sk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Emily Chen",
    "email": "emily@example.com",
    "phone": "+1-555-0102",
    "notes": "Prefers morning appointments"
  }'

Response

{
  "id": 5,
  "name": "Emily Chen",
  "email": "emily@example.com",
  "phone": "+1-555-0102",
  "notes": "Prefers morning appointments",
  "totalBookings": 0,
  "totalSpent": 0
}

Employees

GET /api/services/employees — List employees

POST /api/services/employees — Create employee

PUT /api/services/employees?id=:id — Update employee

DELETE /api/services/employees?id=:id — Delete employee

GET /api/services/employees/export — Export

POST /api/services/employees/import — Import

POST /api/services/employees/invite — Invite employee

GET /api/services/employees/schedule — Get schedule

PATCH /api/services/employees/schedule — Update schedule

Create Employee

curl -X POST https://app.schedly.io/api/services/employees \
  -H "cal-api-key: sk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "James Wilson",
    "email": "james@salon.com",
    "role": "stylist",
    "serviceIds": [1, 2, 3]
  }'

Employee Roles

GET POST PATCH DELETE /api/services/employee-role

Invoices

GET POST PATCH DELETE /api/services/invoices

GET /api/services/invoices/pdf — Download PDF

POST /api/services/invoices/send — Send invoice

Products

GET POST PATCH DELETE /api/services/products

GET /api/services/products/export

POST /api/services/products/import

Product Orders

GET POST /api/services/product-orders

POST /api/services/product-orders/charge — Charge order

POST /api/services/product-orders/terminal — Terminal payment

Categories & Product Categories

GET POST PATCH DELETE /api/services/categories

GET POST PATCH DELETE /api/services/product-categories

Extras

GET POST PATCH DELETE /api/services/extras

Coupons

GET POST PATCH DELETE /api/services/coupons

POST /api/services/coupons/validate — Validate coupon code

Gift Cards

GET POST PATCH DELETE /api/services/gift-cards

POST /api/services/gift-cards/validate — Validate gift card

Custom Fields

GET POST PATCH DELETE /api/services/custom-fields

GET /api/services/custom-fields/public — Public fields

Forms

GET POST PATCH DELETE /api/services/forms

GET /api/services/forms/public

POST /api/services/forms/submit

GET /api/services/forms/templates

POST /api/services/forms/upload

Packages

GET POST PATCH DELETE /api/services/packages

POST /api/services/packages/purchase

POST /api/services/packages/redeem

Create Invoice

curl -X POST https://app.schedly.io/api/services/invoices \
  -H "cal-api-key: sk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "customerId": 5,
    "items": [
      {
        "description": "Haircut",
        "quantity": 1,
        "unitPrice": 3500
      },
      {
        "description": "Deep Conditioning",
        "quantity": 1,
        "unitPrice": 2000
      }
    ],
    "dueDate": "2026-03-01"
  }'

Validate Coupon

curl -X POST https://app.schedly.io/api/services/coupons/validate \
  -H "cal-api-key: sk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{"code": "WELCOME20"}'

Create Product

curl -X POST https://app.schedly.io/api/services/products \
  -H "cal-api-key: sk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Premium Shampoo",
    "price": 2499,
    "sku": "SHMP-001",
    "stock": 50
  }'

Payments

POST /api/services/payments/create — Create payment

GET /api/services/payments/success — Payment success callback

POST /api/services/payments/cancel — Cancel payment

POST /api/services/payments/webhook — Payment webhook

GET /api/services/payment-apps — List payment apps

GET POST /api/services/payment-links

Reviews

GET POST DELETE /api/services/reviews

Notifications

GET POST /api/services/notifications/templates

POST /api/services/notifications/send — Send notification

POST /api/services/notifications/reminders — Setup reminders

Commissions

GET POST /api/services/commissions

GET POST PATCH DELETE /api/services/commissions/rules

Waitlist

GET POST PATCH /api/services/waitlist

Availability

GET /api/services/availability

Insights & Analytics

GET /api/services/insights — Business insights

GET /api/services/stats — Dashboard stats

GET /api/services/product-analytics — Product analytics

Locations

GET POST PATCH DELETE /api/services/locations

Taxes

GET POST PATCH DELETE /api/services/taxes

Company Settings

GET PATCH /api/services/company-settings

SMS Credits

GET POST /api/services/sms-credits

AI Voice & Image

POST /api/services/ai-voice — AI voice assistant

POST /api/services/ai-image — AI image generation

Service Booking

POST /api/services/book — Public service booking

POST /api/services/start-trial — Start trial

POST /api/services/confirm-appointment — Confirm pending appointment (email link)

Barcode

GET /api/services/barcode-lookup — Barcode lookup

POST /api/services/barcode-session — Create barcode session

GET /api/services/image-search — Image search

Catalog Import/Export

GET /api/services/catalog/export

POST /api/services/catalog/import

GET POST /api/services/booking-products

Phone Numbers (SMS)

Manage dedicated SMS phone numbers via Twilio. Requires an active paid Services subscription.

GET /api/services/phone-numbers — List your phone numbers

POST /api/services/phone-numbers/search — Search available numbers

ParameterStatusDescription
countryRequiredISO country code (e.g., US, CA, GB)
areaCodeOptionalFilter by area code
containsOptionalFilter by number pattern
cityOptionalFilter by city
typeOptionalNumber type (local, tollFree, mobile)
limitOptionalMax results (default: 20)

POST /api/services/phone-numbers/purchase — Purchase a number

ParameterStatusDescription
phoneNumberRequiredE.164 phone number to purchase
countryRequiredISO country code

DELETE /api/services/phone-numbers/:id — Release a number

PUT /api/services/phone-numbers/:id/primary — Set as primary number

GET /api/services/phone-numbers/pricing?country=:code — Get pricing by country

GET /api/services/phone-numbers/billing-check — Check billing readiness

POST /api/services/phone-numbers/a2p-registration — A2P brand registration (US)

ParameterStatusDescription
businessNameRequiredLegal business name
businessTypeRequiredsole_proprietorship, partnership, corporation, or nonprofit
contactEmailRequiredContact email address
contactPhoneRequiredContact phone number
einOptionalBusiness EIN
websiteOptionalBusiness website URL
industryOptionalBusiness industry
addressOptionalBusiness address

GET /api/services/phone-numbers/a2p-status — Check A2P registration status

Voice Numbers (AI)

Manage AI voice agent phone numbers via Retell AI. Requires an active paid Services subscription.

GET /api/services/voice-numbers — List your voice numbers

POST /api/services/voice-numbers/search — Search available numbers (US only)

ParameterStatusDescription
areaCodeOptionalFilter by area code
containsOptionalFilter by number pattern
limitOptionalMax results (default: 20, max: 30)

POST /api/services/voice-numbers/purchase — Purchase a voice number

ParameterStatusDescription
phoneNumberOptionalSpecific E.164 number to purchase
areaCodeOptionalArea code (if not specifying a number)

DELETE /api/services/voice-numbers/:id — Release a voice number

PUT /api/services/voice-numbers/:id/primary — Set as primary voice number

Resources

Manage shared resources (rooms, equipment, stations) and check their availability.

GET /api/services/resources — List resources

ParameterStatusDescription
locationIdOptionalFilter by location
typeOptionalFilter by type (room, equipment, station)
activeOptionalFilter by active status

POST /api/services/resources — Create resource

ParameterStatusDescription
locationIdRequiredLocation ID
nameRequiredResource name
typeOptionalType: equipment (default), room, station
descriptionOptionalDescription
quantityOptionalTotal quantity available (default: 1)
activeOptionalActive status (default: true)

PUT /api/services/resources?id=:id — Update resource

DELETE /api/services/resources?id=:id — Delete resource

GET /api/services/resource-availability — Check resource availability

ParameterStatusDescription
resourceIdRequiredResource ID
startTimeRequiredISO 8601 start time
endTimeRequiredISO 8601 end time
quantityOptionalRequested quantity (default: 1)

Districts

Organize locations into districts/regions for multi-location businesses.

GET /api/services/districts — List districts

POST /api/services/districts — Create district

ParameterStatusDescription
nameRequiredDistrict name
regionOptionalRegion identifier
descriptionOptionalDescription

PUT /api/services/districts?id=:id — Update district

DELETE /api/services/districts?id=:id — Delete district

Stripe Connect

Connect your Stripe account to accept payments for services.

GET /api/services/stripe-connect/initiate — Get Stripe Connect onboarding URL

GET /api/services/stripe-connect/status — Check connection status

POST /api/services/stripe-connect/disconnect — Disconnect Stripe (owner/admin only)

CRM Import

Import records from your connected CRM (HubSpot, Salesforce, Zoho, Pipedrive, Close).

GET /api/services/crm-import/status — Check CRM connection status

GET /api/services/crm-import/preview — Preview CRM objects and records

ParameterStatusDescription
providerRequiredCRM provider ID (hubspot, salesforce, zohocrm, pipedrive-crm, closecom)
objectNameOptionalCRM object name (omit to list available objects)
fieldsOptionalComma-separated field names to fetch
limitOptionalMax records (default: 10, max: 50)

POST /api/services/crm-import/pull — Pull records from CRM

ParameterStatusDescription
providerRequiredCRM provider ID
objectNameRequiredCRM object to import from
fieldMappingRequiredObject mapping CRM fields to Schedly fields
categoryRequiredTarget category (customers, services, etc.)
crmFieldsOptionalArray of CRM field names to fetch

Terminal / POS

Manage card readers and process in-person terminal payments via Stripe, Square, or Mollie.

GET /api/services/terminal-readers — List terminal readers across all providers

POST /api/services/terminal-readers — Register a new terminal reader

DELETE /api/services/terminal-readers — Remove a terminal reader

POST /api/services/appointments/terminal — Process terminal payment for an appointment

Connected Apps

GET /api/services/connected-apps — List available app integrations and their connection status

Returns connection status for Calendar, Video, CRM, Payment, and Automation app categories.

Data Import/Export

Import and export individual entity types (separate from full catalog import/export).

POST /api/services/customers/import — Import customers from CSV

POST /api/services/locations/import — Import locations from CSV

GET /api/services/locations/export — Export locations

POST /api/services/employees/import — Import employees from CSV

POST /api/services/appointments/import — Import appointments from CSV

POST /api/services/products/import — Import products from CSV

GET /api/services/products/export — Export products

Customer Detail

GET /api/services/customers/detail?id=:id — Full customer profile with appointments, invoices, and reviews

Public Review

Submit a review for a completed appointment (no auth required, uses appointment UID).

POST /api/services/public-review

ParameterStatusDescription
uidRequiredAppointment UID
ratingRequiredRating 1–5
commentOptionalReview comment

Create Payment

curl -X POST https://app.schedly.io/api/services/payments/create \
  -H "cal-api-key: sk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "appointmentId": 201,
    "amount": 3500,
    "currency": "usd"
  }'

Search Phone Numbers

curl -X POST https://app.schedly.io/api/services/phone-numbers/search \
  -H "cal-api-key: sk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "country": "US",
    "areaCode": "512",
    "type": "local",
    "limit": 5
  }'

Purchase Voice Number

curl -X POST https://app.schedly.io/api/services/voice-numbers/purchase \
  -H "cal-api-key: sk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "areaCode": "512"
  }'

Create Resource

curl -X POST https://app.schedly.io/api/services/resources \
  -H "cal-api-key: sk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "locationId": 1,
    "name": "Treatment Room A",
    "type": "room",
    "quantity": 1
  }'

Check Resource Availability

curl "https://app.schedly.io/api/services/resource-availability?\
resourceId=5&\
startTime=2026-03-15T09:00:00Z&\
endTime=2026-03-15T10:00:00Z" \
  -H "cal-api-key: sk_live_abc123..."

Availability Response

{
  "available": true,
  "totalQuantity": 3,
  "bookedQuantity": 1,
  "availableQuantity": 2,
  "requestedQuantity": 1
}

Create District

curl -X POST https://app.schedly.io/api/services/districts \
  -H "cal-api-key: sk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Downtown",
    "region": "Central",
    "description": "Downtown metro area locations"
  }'

Stripe Connect Status

curl https://app.schedly.io/api/services/stripe-connect/status \
  -H "cal-api-key: sk_live_abc123..."

Response

{
  "connected": true,
  "stripeAccountId": "acct_1234567890",
  "chargesEnabled": true,
  "payoutsEnabled": true
}

CRM Import Preview

curl "https://app.schedly.io/api/services/crm-import/preview?\
provider=hubspot&\
objectName=contacts&\
limit=5" \
  -H "cal-api-key: sk_live_abc123..."

Pull CRM Records

curl -X POST https://app.schedly.io/api/services/crm-import/pull \
  -H "cal-api-key: sk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "hubspot",
    "objectName": "contacts",
    "category": "customers",
    "fieldMapping": {
      "firstname": "name",
      "email": "email",
      "phone": "phone"
    }
  }'

Submit Public Review

curl -X POST https://app.schedly.io/api/services/public-review \
  -H "Content-Type: application/json" \
  -d '{
    "uid": "apt_7f3b2a1c",
    "rating": 5,
    "comment": "Excellent service!"
  }'

Get Insights

curl "https://app.schedly.io/api/services/insights?\
from=2026-01-01&to=2026-02-28" \
  -H "cal-api-key: sk_live_abc123..."

Insights Response

{
  "totalRevenue": 128500,
  "totalBookings": 47,
  "averageBookingValue": 2734,
  "topServices": [
    { "name": "Haircut", "count": 22 },
    { "name": "Color Treatment", "count": 15 }
  ],
  "newCustomers": 12
}

Customer Portal API

Enable your customers to manage their own appointments through a self-service portal.

Portal Authentication

POST /api/services/portal/auth — Send magic link

Verify Portal Token

GET /api/services/portal/verify — Verify portal token

Customer Appointments

GET /api/services/portal/appointments

Customer Purchases

GET /api/services/portal/purchases

Customer Invoices

GET /api/services/portal/invoices — List customer invoices

GET /api/services/portal/invoices?id=:id&format=html — Get invoice as rendered HTML

Payment Methods

GET /api/services/portal/payment-methods — List saved payment methods

POST /api/services/portal/payment-methods — Add payment method (returns Stripe setup intent)

DELETE /api/services/portal/payment-methods?id=:paymentMethodId — Remove payment method

Email Preferences

GET /api/services/portal/email-preferences — Get email subscription status

PUT /api/services/portal/email-preferences — Update email opt-in/opt-out

ParameterStatusDescription
emailUnsubscribedRequiredtrue to unsubscribe, false to resubscribe

SMS Preferences

GET /api/services/portal/sms-preferences — Get SMS consent status

PUT /api/services/portal/sms-preferences — Update SMS consent

ParameterStatusDescription
smsConsentRequiredtrue to opt in, false to opt out

Portal Magic Link

curl -X POST https://app.schedly.io/api/services/portal/auth \
  -H "Content-Type: application/json" \
  -d '{
    "email": "emily@example.com",
    "subdomain": "sarahssalon"
  }'

Verify Token

curl "https://app.schedly.io/api/services/portal/verify?\
token=ptk_abc123xyz789"

Schedly Connect (Routing) API

Intelligent lead routing, territory management, qualification, and SLA tracking.

Routing Configuration

GET POST /api/routing/config

Execute Routing

POST /api/routing/execute — Execute routing engine

Route & Book

POST /api/routing/book — Route lead and book

Preview Routing

POST /api/routing/preview — Test/preview routing

Lead Handoff

POST /api/routing/handoff

Lead Qualification

GET POST /api/routing/qualification

Territories

GET POST /api/routing/territories

GET /api/routing/territories/export

POST /api/routing/territories/import

GET POST /api/routing/smart-links

POST /api/routing/smart-links/resolve

SLA Configuration

GET POST /api/routing/sla

POST /api/routing/sla/check — SLA check (cron)

GET /api/routing/snippet — Get JS embed snippet

Routing Analytics

GET /api/routing/analytics

GET /api/services/analytics/routing-funnel — Routing funnel metrics

ParameterStatusDescription
fromOptionalISO 8601 start date
toOptionalISO 8601 end date

GET /api/services/analytics/locations — Location analytics

ParameterStatusDescription
fromOptionalISO 8601 start date
toOptionalISO 8601 end date
districtIdOptionalFilter by district

Routing Groups

GET /api/routing/groups — List routing groups

POST /api/routing/groups — Create routing group

POST /api/routing/groups/add-members — Add members to a group

Routing Targets

GET /api/routing/targets — List routing targets

POST /api/routing/targets — Create/update targets

Routing Forms

GET /api/routing/forms — List routing forms

POST /api/routing/forms — Create a routing form

Team Members

GET /api/routing/team-members — List team members for routing

Public Execute

POST /api/routing/public-execute — Execute routing without authentication (public embed)

Audit Trail

GET /api/routing/audit-trail — List routing decision logs

ParameterStatusDescription
fromOptionalISO 8601 start date
toOptionalISO 8601 end date
limitOptionalMax results (default: 50)

CRM Integration

CRM-related routing endpoints for lead enrichment and field mapping.

POST /api/routing/crm-lookup — Look up a lead in connected CRM

POST /api/routing/crm-enrich — Enrich lead data from CRM

GET /api/routing/crm-fields — List available CRM fields

GET POST /api/routing/crm-field-mapping — Get/set CRM field mapping

GET /api/routing/crm-status — Check CRM connection status for routing

GET /api/routing/territories/presets — List territory presets

POST /api/routing/territories/overrides — Create territory override

Execute Routing

cURL
JavaScript
Python
curl -X POST https://app.schedly.io/api/routing/execute \
  -H "cal-api-key: sk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "email": "lead@company.com",
    "name": "John Doe",
    "company": "Acme Corp",
    "formData": {
      "companySize": "50-100",
      "industry": "technology"
    }
  }'
const res = await fetch('https://app.schedly.io/api/routing/execute', {
  method: 'POST',
  headers: {
    'cal-api-key': 'sk_live_abc123...',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    email: 'lead@company.com',
    name: 'John Doe',
    company: 'Acme Corp',
    formData: { companySize: '50-100' }
  })
});
res = requests.post(
    "https://app.schedly.io/api/routing/execute",
    headers={"cal-api-key": "sk_live_abc123..."},
    json={
        "email": "lead@company.com",
        "name": "John Doe",
        "company": "Acme Corp"
    }
)

Response

{
  "routedTo": {
    "userId": 12345,
    "name": "Sarah Johnson",
    "bookingUrl": "https://app.schedly.io/sarahj/demo"
  },
  "matchedTerritory": "North America",
  "qualified": true,
  "score": 85
}

Billing API

Manage subscriptions, checkout, and billing information.

Create Checkout

POST /api/billing/checkout

ParameterStatusDescription
planRequiredpro, services, branding, or hipaa
periodOptionalmonthly or yearly (default: monthly)

Billing Summary

GET /api/billing/summary

Billing Invoices

GET /api/billing/invoices

Seat Count

GET /api/billing/seats

Cancel Subscription

POST /api/billing/cancel

Resume Subscription

POST /api/billing/resume — Resume a canceled subscription before period end

Additional Billing Endpoints

POST /api/billing/toggle-branding — Toggle custom branding add-on

POST /api/billing/toggle-hipaa — Toggle HIPAA compliance add-on

POST /api/billing/start-pro-trial — Start a Pro plan trial

POST /api/billing/update-payment-method — Update default payment method

POST /api/billing/setup-intent — Create Stripe SetupIntent for card collection

GET /api/billing/stripe-publishable-key — Get Stripe publishable key

GET /api/billing/trial-analytics — Trial conversion analytics

GET /api/billing/expiring-cards — Check for expiring payment methods

POST /api/billing/log-conversion — Log trial-to-paid conversion event

POST /api/billing/trial-lifecycle — Process trial lifecycle transitions

POST /api/billing/team-lifecycle — Process team billing lifecycle transitions

GET /api/billing/team-lock-status — Check team feature lock status

Stripe Webhook

POST /api/billing/webhook — Stripe webhook handler

Create Checkout Session

curl -X POST https://app.schedly.io/api/billing/checkout \
  -H "cal-api-key: sk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "plan": "pro",
    "period": "yearly"
  }'

Response

{
  "clientSecret": "cs_test_a1b2c3d4..."
}

Get Billing Summary

curl https://app.schedly.io/api/billing/summary \
  -H "cal-api-key: sk_live_abc123..."

Extension API

Event Types for Extensions

GET /api/extension/event-types

Slots for Extensions

GET /api/extension/slots

Extension Event Types

curl https://app.schedly.io/api/extension/event-types \
  -H "cal-api-key: sk_live_abc123..."

Reserve with Google

Integration endpoints for Google Reserve booking flow.

Availability

GET /api/reserve-with-google/availability

Booking

POST /api/reserve-with-google/booking

Feeds

GET /api/reserve-with-google/feeds

Health Check

GET /api/reserve-with-google/health

Check Availability

curl https://app.schedly.io/api/reserve-with-google/availability \
  -H "cal-api-key: sk_live_abc123..."

Compliance API

Sign BAA

POST /api/compliance/baa — Sign Business Associate Agreement

Sign DPA

POST /api/compliance/dpa — Sign Data Processing Agreement

Download Documents

GET /api/compliance/download — Download compliance docs

Sign BAA

curl -X POST https://app.schedly.io/api/compliance/baa \
  -H "cal-api-key: sk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "signerName": "Sarah Johnson",
    "signerTitle": "CEO",
    "companyName": "Wellness Clinic LLC"
  }'

Zapier Integration

Connect Schedly with thousands of apps using Zapier.

Setup

  1. Search for "Schedly" in the Zapier app directory
  2. Click "Connect" to initiate OAuth 2.0 authorization
  3. Authorize Schedly access in the OAuth consent screen
  4. Select your triggers and actions

Available Triggers

  • Booking Created — Fires when a new booking is made
  • Booking Cancelled — Fires when a booking is cancelled
  • Booking Rescheduled — Fires when a booking is rescheduled

Available Actions

  • Create Booking — Create a new booking
  • Cancel Booking — Cancel an existing booking
  • Get Availability — Check available time slots

Zapier OAuth Flow

# Zapier uses Schedly's OAuth 2.0 flow:

Authorization URL:
  https://app.schedly.io/auth/oauth2/authorize

Token URL:
  https://app.schedly.io/api/auth/oauth/token

Refresh URL:
  https://app.schedly.io/api/auth/oauth/refreshToken

# Zapier automatically handles token
# refresh and stores credentials securely.

Webhook Events Reference

Event Types

EventDescription
BOOKING_CREATEDNew booking confirmed
BOOKING_RESCHEDULEDBooking time changed
BOOKING_CANCELLEDBooking cancelled
BOOKING_CONFIRMEDPending booking confirmed
BOOKING_REJECTEDPending booking rejected
BOOKING_PAIDBooking payment completed
BOOKING_PAYMENT_INITIATEDPayment initiated for booking
BOOKING_NO_SHOW_UPDATEDNo-show status updated for attendee
BOOKING_REQUESTEDBooking approval requested
MEETING_STARTEDVideo meeting started
MEETING_ENDEDVideo meeting ended
MEETING_URL_CHANGEDVideo meeting URL changed
RECORDING_READYMeeting recording available
RECORDING_TRANSCRIPTION_GENERATEDRecording transcription completed
INSTANT_MEETINGInstant meeting created
FORM_SUBMITTEDRouting form submitted
OOO_CREATEDOut-of-office entry created
SERVICE_BOOKING_CREATEDService appointment created
SERVICE_BOOKING_UPDATEDService appointment updated
SERVICE_BOOKING_CANCELLEDService appointment cancelled
SERVICE_BOOKING_COMPLETEDService appointment completed

Payload Format

All webhook payloads include the event type, timestamp, and event-specific data.

Signature Verification

If you set a webhook secret, Schedly includes a X-Schedly-Signature header with an HMAC-SHA256 signature of the payload body.

Webhook Payload

{
  "triggerEvent": "BOOKING_CREATED",
  "createdAt": "2026-02-19T14:30:00Z",
  "payload": {
    "id": 501,
    "uid": "bk_2f8a9c3e",
    "title": "30-Min Consultation",
    "type": "consultation",
    "startTime": "2026-02-20T10:00:00Z",
    "endTime": "2026-02-20T10:30:00Z",
    "organizer": {
      "name": "Sarah Johnson",
      "email": "sarah@example.com"
    },
    "attendees": [
      {
        "name": "Alex Rivera",
        "email": "alex@example.com",
        "timeZone": "America/Chicago"
      }
    ]
  }
}

Verify Signature (Node.js)

const crypto = require('crypto');

function verifyWebhook(body, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(body)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

MCP Server

The Schedly MCP (Model Context Protocol) server lets external AI agents, copilots, and developer tools connect to your scheduling system. With 71 tools across 12 domains, it's the most comprehensive scheduling MCP server available — covering Cal.com scheduling, Schedly Services, analytics, workflows, webhooks, routing forms, and team management.

Compatible with Claude Desktop, Cursor, Windsurf, Continue, and any MCP-compatible client.

Base URL

https://app.schedly.io/api/mcp/sse

Protocol

JSON-RPC 2.0 over HTTP POST (stateless) or SSE (streaming). Supports batch requests.

Supported Methods

MethodDescription
initializeHandshake — returns server info, capabilities, and protocol version
tools/listList all 71 available tools with schemas
tools/callExecute a tool by name with arguments

Quick Test

curl -X POST https://app.schedly.io/api/mcp/sse \
  -H "Authorization: Bearer cal_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {}
  }'

Response

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2024-11-05",
    "capabilities": {
      "tools": { "listChanged": false }
    },
    "serverInfo": {
      "name": "schedly-mcp",
      "version": "2.0.0"
    }
  }
}

MCP Authentication

All MCP requests require a valid API key sent via the Authorization header. Keys are the same ones used for the REST API — generate one at Settings → Developer → API Keys.

Header Format

Authorization: Bearer cal_YOUR_API_KEY

How It Works

  • The cal_ prefix is stripped before validation
  • The raw key is hashed with SHA-256
  • The hash is matched against ApiKey.hashedKey in the database
  • The associated userId scopes all tool operations to that user's data

Invalid or expired keys return a JSON error (not a JSON-RPC response):

Auth Error

{
  "error": "Invalid or missing API key. Use Authorization: Bearer cal_YOUR_KEY"
}

MCP Transport

The MCP server supports two transport modes on the same endpoint:

HTTP POST (Stateless)

Send a JSON-RPC request and receive a JSON-RPC response. Best for simple integrations and testing.

SSE (Streaming)

Send a GET request to establish a Server-Sent Events stream. The server returns a session-specific endpoint URL for sending messages. Best for long-lived agent connections.

Batch Requests

Send an array of JSON-RPC requests in a single POST. The server processes them sequentially and returns an array of responses.

SSE Session

GET /api/mcp/sse
Authorization: Bearer cal_YOUR_KEY
Accept: text/event-stream

Response:
event: endpoint
data: https://app.schedly.io/api/mcp/sse
  ?sessionId=abc-123-def

Batch Request

[
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "get_current_user",
      "arguments": {}
    }
  },
  {
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/call",
    "params": {
      "name": "dashboard_summary",
      "arguments": {}
    }
  }
]

Tools Reference

All tools are organized into 12 domain modules. Each tool accepts a JSON arguments object and returns structured data.

User (4 tools)

ToolDescription
get_current_userGet profile, timezone, booking counts
update_user_profileUpdate name, bio, timezone, locale, time format
list_api_keysList API keys (without revealing values)
get_out_of_officeGet current out-of-office entries

Event Types (6 tools)

ToolDescription
list_event_typesList all event types with optional booking counts
get_event_typeGet full event type details by ID
create_event_typeCreate a new event type
update_event_typeUpdate title, duration, description, locations, etc.
delete_event_typeDelete an event type
duplicate_event_typeClone an event type with a new slug

Bookings (8 tools)

ToolDescription
list_bookingsList bookings with status/date filters
get_bookingGet booking details by ID or UID
cancel_bookingCancel a booking with optional reason
confirm_bookingConfirm a pending booking
reject_bookingReject a pending booking
list_upcoming_bookingsGet next N upcoming bookings
search_bookingsSearch bookings by attendee name/email
mark_no_showMark a booking attendee as no-show

Availability & Schedules (5 tools)

ToolDescription
list_schedulesList all availability schedules
get_scheduleGet schedule details by ID
create_scheduleCreate a new availability schedule
delete_scheduleDelete a schedule
get_available_slotsGet open time slots for an event type + date range

Service Appointments (7 tools)

ToolDescription
list_appointmentsList service appointments with filters
get_appointmentGet appointment details by ID
create_appointmentCreate a new service appointment
update_appointmentUpdate appointment details
cancel_appointmentCancel a service appointment
check_availabilityCheck staff/service availability
bulk_update_appointmentsUpdate multiple appointments at once

Customers (6 tools)

ToolDescription
search_customersSearch by name, email, or phone
get_customerGet full customer profile
create_customerCreate a new customer record
update_customerUpdate customer details
delete_customerDelete a customer
list_top_customersGet top customers by booking count or revenue

Resources (10 tools)

ToolDescription
list_locationsList all service locations
get_locationGet location details
list_servicesList services with pricing
get_serviceGet service details
list_staffList staff members
get_staffGet staff details and assignments
list_productsList products with stock levels
update_product_stockUpdate product inventory quantity
list_invoicesList invoices with status filters
list_service_categoriesList service categories

Workflows (5 tools)

ToolDescription
list_workflowsList all automation workflows
get_workflowGet workflow details and steps
create_workflowCreate a new workflow
update_workflowUpdate workflow name or trigger
delete_workflowDelete a workflow

Webhooks (6 tools)

ToolDescription
list_webhooksList configured webhooks
get_webhookGet webhook details
create_webhookCreate a new webhook subscription
update_webhookUpdate webhook URL or events
delete_webhookDelete a webhook
test_webhookSend a test payload to a webhook

Routing Forms (3 tools)

ToolDescription
list_routing_formsList all routing forms
get_routing_formGet form details with fields and routes
list_routing_form_responsesGet form responses

Teams (3 tools)

ToolDescription
list_teamsList teams you belong to
get_teamGet team details
list_team_membersList team members with roles

Analytics (8 tools)

ToolDescription
get_schedule_statsGet scheduling statistics over a period
dashboard_summaryFull operational snapshot
booking_analyticsBooking trends and breakdowns
popular_event_typesMost-booked event types
busiest_daysDay-of-week booking distribution
no_show_reportNo-show rates and patterns
revenue_reportRevenue breakdown by period
staff_performancePer-staff booking and completion stats

Calling a Tool

curl -X POST https://app.schedly.io/api/mcp/sse \
  -H "Authorization: Bearer cal_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "list_bookings",
      "arguments": {
        "status": "upcoming",
        "limit": 5
      }
    }
  }'

Tool Response

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"count\":3, ...}"
      }
    ]
  }
}

Dashboard Summary

{
  "serviceAppointments": {
    "today": { "total": 12, "pending": 3 },
    "thisWeek": { "total": 47, "revenue": "$2,340.00" }
  },
  "calBookings": { "today": 5, "thisWeek": 18 },
  "totalCustomers": 284,
  "activeLocations": 5
}

Client Setup

Connect your preferred AI client to Schedly's MCP server. Click Copy Config on any card below, paste it into your client's config file, and replace cal_YOUR_API_KEY with your key.

Claude Claude Desktop
claude_desktop_config.json
Click to copy config
Cursor Cursor
.cursor/mcp.json
Click to copy config
Windsurf Windsurf
.windsurf/mcp.json
Click to copy config
ChatGPT ChatGPT / OpenAI
Responses API + MCP
Click to copy config
Gemini Google Gemini
Gemini API + MCP SDK
Click to copy config
Continue Continue
config.json
Click to copy config

ChatGPT / OpenAI

OpenAI supports MCP natively via their Responses API. Add Schedly as an MCP server tool and GPT models will call all 71 tools automatically. Also works with the ChatGPT desktop app (Settings → MCP Servers → Add).

Google Gemini

Gemini supports MCP via the Google Gen AI SDK. Create an SseServerParams pointing to Schedly's SSE endpoint and attach it as a tool when creating your Gemini client.

Custom / Direct HTTP

Any language or framework can call the MCP server directly via HTTP POST with JSON-RPC 2.0. No SDK required.

Claude Desktop / Cursor / Windsurf / Continue

{
  "mcpServers": {
    "schedly": {
      "url": "https://app.schedly.io/api/mcp/sse",
      "headers": {
        "Authorization": "Bearer cal_YOUR_API_KEY"
      }
    }
  }
}

ChatGPT / OpenAI Responses API

from openai import OpenAI

client = OpenAI()

resp = client.responses.create(
  model="gpt-4.1",
  tools=[{
    "type": "mcp",
    "server_label": "schedly",
    "server_url": "https://app.schedly.io/api/mcp/sse",
    "headers": {
      "Authorization": "Bearer cal_YOUR_API_KEY"
    }
  }],
  input="What's on my schedule today?"
)

Google Gemini (Python SDK)

from google import genai
from google.genai.types import (
  HttpBasicAuthCredentials,
  SseServerParams
)

client = genai.Client()

schedly = SseServerParams(
  url="https://app.schedly.io/api/mcp/sse",
  headers={
    "Authorization": "Bearer cal_YOUR_API_KEY"
  }
)

response = client.models.generate_content(
  model="gemini-2.5-flash",
  contents="List my upcoming bookings",
  config=genai.types.GenerateContentConfig(
    tools=[genai.types.Tool(
      mcp_servers=[schedly]
    )]
  )
)

Direct HTTP (any language)

curl -X POST https://app.schedly.io/api/mcp/sse \
  -H "Authorization: Bearer cal_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/list"
  }'

Error Reference

All errors follow a consistent format with an error code, human-readable message, and HTTP status code.

StatusError CodeDescription
400BAD_REQUESTThe request was malformed or missing required parameters
401UNAUTHORIZEDAuthentication credentials are missing or invalid
403FORBIDDENYou don't have permission to access this resource
404NOT_FOUNDThe requested resource does not exist
409CONFLICTThe request conflicts with the current state (e.g., double booking)
422VALIDATION_ERRORThe request body failed validation
429RATE_LIMITEDToo many requests; slow down and retry
500INTERNAL_ERRORAn unexpected error occurred on the server

Error Response Format

{
  "status": "error",
  "error": "UNAUTHORIZED",
  "message": "Invalid API key provided."
}

Validation Error

{
  "status": "error",
  "error": "VALIDATION_ERROR",
  "message": "Invalid request parameters",
  "details": [
    {
      "field": "startTime",
      "message": "Must be a valid ISO 8601 date"
    },
    {
      "field": "eventTypeId",
      "message": "Required field"
    }
  ]
}

Rate Limit Error

HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1708372800

{
  "status": "error",
  "error": "RATE_LIMITED",
  "message": "Rate limit exceeded. Retry after 60s."
}