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

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

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"
  }'

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

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

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

Additional billing endpoints:

POST /api/billing/toggle-branding

POST /api/billing/toggle-hipaa

POST /api/billing/start-pro-trial

POST /api/billing/update-payment-method

POST /api/billing/setup-intent

GET /api/billing/stripe-publishable-key

GET /api/billing/trial-analytics

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_PAYMENT_INITIATEDPayment initiated for booking
MEETING_STARTEDVideo meeting started
MEETING_ENDEDVideo meeting ended
RECORDING_READYMeeting recording available

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

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."
}