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.
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/.
https://app.schedly.io
# 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
Schedly uses two authentication methods. Choose the one that fits your integration:
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.
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.
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()
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..."} )
API requests are rate limited to ensure fair usage across all clients.
| Plan | Limit | Window |
|---|---|---|
| Free | 60 requests | per minute |
| Pro | 120 requests | per minute |
| Enterprise | 600 requests | per minute |
Rate limit information is included in response headers:
X-RateLimit-Limit — Maximum requests per windowX-RateLimit-Remaining — Remaining requestsX-RateLimit-Reset — Unix timestamp when window resetsWhen rate limited, you'll receive a 429 status code. Use exponential backoff when retrying.
HTTP/1.1 200 OK X-RateLimit-Limit: 120 X-RateLimit-Remaining: 98 X-RateLimit-Reset: 1708372800
HTTP/1.1 429 Too Many Requests
{
"status": "error",
"error": "RATE_LIMITED",
"message": "Too many requests. Please retry after 60 seconds."
}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.
| Code | Meaning |
|---|---|
| 200 | OK — Request succeeded |
| 201 | Created — Resource created |
| 400 | Bad Request — Invalid parameters |
| 401 | Unauthorized — Invalid or missing auth |
| 403 | Forbidden — Insufficient permissions |
| 404 | Not Found — Resource doesn't exist |
| 409 | Conflict — Resource conflict |
| 422 | Unprocessable — Validation failed |
| 429 | Rate Limited — Too many requests |
| 500 | Server Error — Something went wrong |
{
"status": "success",
"data": {
"id": 42,
"name": "Consultation"
}
}{
"status": "error",
"error": "NOT_FOUND",
"message": "The requested resource was not found."
}{
"status": "error",
"error": "VALIDATION_ERROR",
"message": "Invalid request parameters",
"details": [
{
"field": "email",
"message": "Must be a valid email address"
}
]
}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.
| Type | Use Case | Secret |
|---|---|---|
| Confidential | Server-side apps (Node.js, Python, etc.) | Uses client_secret |
| Public (PKCE) | Browser apps, mobile apps, SPAs | Uses code_challenge / code_verifier |
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ 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 │ │ │<───────────────────│ │ │ │ │
Before starting the OAuth flow, create an OAuth client in your Schedly developer settings:
client_id and client_secretclient_secret secure. Never expose it in client-side code, public repositories, or frontend bundles.# 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
Direct users to the Schedly authorization page to grant your application access.
https://app.schedly.io/auth/oauth2/authorize
| Parameter | Status | Description |
|---|---|---|
| client_id | Required | Your OAuth client ID |
| redirect_uri | Required | Must match a registered redirect URI |
| response_type | Optional | Set to code (default) |
| state | Required | Opaque value for CSRF protection, returned unchanged |
| code_challenge | Optional | PKCE: Base64url-encoded SHA-256 hash of code_verifier |
| code_challenge_method | Optional | Set to S256 when using PKCE |
After the user approves, Schedly redirects to your redirect_uri with an authorization code and your state parameter.
| Error | Description |
|---|---|
| client_not_found | The client_id doesn't match any registered client |
| redirect_uri_mismatch | The redirect_uri doesn't match any registered URI |
| not_approved | The OAuth client has not been approved |
| access_denied | The user denied the authorization request |
# 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}"
# 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
POST /api/auth/oauth/token
Exchange the authorization code for access and refresh tokens.
| Parameter | Status | Description |
|---|---|---|
| grant_type | Required | Set to authorization_code |
| code | Required | The authorization code from the callback |
| client_id | Required | Your OAuth client ID |
| client_secret | Conditional | Required for confidential clients |
| redirect_uri | Required | Must match the URI used in authorization |
| code_verifier | Conditional | Required for PKCE flow |
Returns an access token, refresh token, token type, and expiration time.
| Error | Description |
|---|---|
| invalid_grant | The authorization code is invalid or expired |
| invalid_client | Client authentication failed |
| invalid_request | Missing required parameters |
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()
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"refresh_token": "rt_live_m4n5o6p7q8r9...",
"token_type": "Bearer",
"expires_in": 1800
}POST /api/auth/oauth/refreshToken
Use a refresh token to obtain a new access token when the current one expires.
| Parameter | Status | Description |
|---|---|---|
| grant_type | Required | Set to refresh_token |
| refresh_token | Required | The refresh token from the initial exchange |
| client_id | Required | Your OAuth client ID |
| client_secret | Conditional | Required for confidential clients |
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"
}
){
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"refresh_token": "rt_live_newtoken123...",
"token_type": "Bearer",
"expires_in": 1800
}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.
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(){
"username": "sarahj"
}Legacy booking endpoints for direct event booking.
POST /api/book/event
POST /api/book/recurring-event
POST /api/book/instant-event
POST /api/cancel
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" }'
curl -X POST https://app.schedly.io/api/cancel \ -H "Content-Type: application/json" \ -d '{ "uid": "bk_2f8a9c3e", "reason": "Schedule conflict" }'
The Services API powers Schedly's full business management platform — services, appointments, customers, employees, invoices, products, and more.
cal-api-key header or session cookie.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
| Parameter | Status | Description |
|---|---|---|
| name | Required | Service name |
| duration | Optional | Duration in minutes (default: 60) |
| price | Optional | Price in cents (default: 0) |
| currency | Optional | Currency code (default: usd) |
| description | Optional | Service description |
| categoryId | Optional | Category ID |
| locationId | Optional | Location ID |
| bufferTimeBefore | Optional | Buffer time before (minutes) |
| bufferTimeAfter | Optional | Buffer time after (minutes) |
| maxCapacity | Optional | Max booking capacity |
| depositType | Optional | none, fixed, or percentage |
| depositAmount | Optional | Deposit amount |
| enableRecurring | Optional | Enable recurring bookings |
| enableGroupBooking | Optional | Enable group bookings |
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"
}
){
"id": 1,
"name": "Haircut",
"slug": "haircut",
"duration": 30,
"price": 3500,
"currency": "usd",
"description": "Professional haircut service",
"active": true,
"category": null,
"extras": []
}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
| Parameter | Status | Description |
|---|---|---|
| serviceId | Required | Service ID |
| startTime | Required | Start time (ISO 8601) |
| endTime | Required | End time (ISO 8601) |
| customerId | Optional | Customer ID |
| employeeId | Optional | Assigned employee ID |
| locationId | Optional | Location ID |
| status | Optional | pending, approved, canceled, completed |
| price | Optional | Price in cents |
| recurrenceRule | Optional | weekly, biweekly, or monthly |
| recurrenceCount | Optional | Number of recurring instances |
| isGroupBooking | Optional | Enable group booking |
| attendees | Optional | Array of attendee objects |
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
}
){
"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" }
}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
| Parameter | Status | Description |
|---|---|---|
| name | Required | Customer name |
| Optional | Email address | |
| phone | Optional | Phone number |
| notes | Optional | Internal notes |
| gender | Optional | Gender |
| dateOfBirth | Optional | Date of birth (ISO date) |
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" }'
{
"id": 5,
"name": "Emily Chen",
"email": "emily@example.com",
"phone": "+1-555-0102",
"notes": "Prefers morning appointments",
"totalBookings": 0,
"totalSpent": 0
}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
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] }'
GET POST PATCH DELETE /api/services/employee-role
GET POST PATCH DELETE /api/services/invoices
GET /api/services/invoices/pdf — Download PDF
POST /api/services/invoices/send — Send invoice
GET POST PATCH DELETE /api/services/products
GET /api/services/products/export
POST /api/services/products/import
GET POST /api/services/product-orders
POST /api/services/product-orders/charge — Charge order
POST /api/services/product-orders/terminal — Terminal payment
GET POST PATCH DELETE /api/services/categories
GET POST PATCH DELETE /api/services/product-categories
GET POST PATCH DELETE /api/services/extras
GET POST PATCH DELETE /api/services/coupons
POST /api/services/coupons/validate — Validate coupon code
GET POST PATCH DELETE /api/services/gift-cards
POST /api/services/gift-cards/validate — Validate gift card
GET POST PATCH DELETE /api/services/custom-fields
GET /api/services/custom-fields/public — Public fields
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
GET POST PATCH DELETE /api/services/packages
POST /api/services/packages/purchase
POST /api/services/packages/redeem
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" }'
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"}'
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 }'
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
GET POST DELETE /api/services/reviews
GET POST /api/services/notifications/templates
POST /api/services/notifications/send — Send notification
POST /api/services/notifications/reminders — Setup reminders
GET POST /api/services/commissions
GET POST PATCH DELETE /api/services/commissions/rules
GET POST PATCH /api/services/waitlist
GET /api/services/availability
GET /api/services/insights — Business insights
GET /api/services/stats — Dashboard stats
GET /api/services/product-analytics — Product analytics
GET POST PATCH DELETE /api/services/locations
GET POST PATCH DELETE /api/services/taxes
GET PATCH /api/services/company-settings
GET POST /api/services/sms-credits
POST /api/services/ai-voice — AI voice assistant
POST /api/services/ai-image — AI image generation
POST /api/services/book — Public service booking
POST /api/services/start-trial — Start trial
POST /api/services/confirm-appointment — Confirm pending appointment (email link)
GET /api/services/barcode-lookup — Barcode lookup
POST /api/services/barcode-session — Create barcode session
GET /api/services/image-search — Image search
GET /api/services/catalog/export
POST /api/services/catalog/import
GET POST /api/services/booking-products
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
| Parameter | Status | Description |
|---|---|---|
| country | Required | ISO country code (e.g., US, CA, GB) |
| areaCode | Optional | Filter by area code |
| contains | Optional | Filter by number pattern |
| city | Optional | Filter by city |
| type | Optional | Number type (local, tollFree, mobile) |
| limit | Optional | Max results (default: 20) |
POST /api/services/phone-numbers/purchase — Purchase a number
| Parameter | Status | Description |
|---|---|---|
| phoneNumber | Required | E.164 phone number to purchase |
| country | Required | ISO 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)
| Parameter | Status | Description |
|---|---|---|
| businessName | Required | Legal business name |
| businessType | Required | sole_proprietorship, partnership, corporation, or nonprofit |
| contactEmail | Required | Contact email address |
| contactPhone | Required | Contact phone number |
| ein | Optional | Business EIN |
| website | Optional | Business website URL |
| industry | Optional | Business industry |
| address | Optional | Business address |
GET /api/services/phone-numbers/a2p-status — Check A2P registration status
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)
| Parameter | Status | Description |
|---|---|---|
| areaCode | Optional | Filter by area code |
| contains | Optional | Filter by number pattern |
| limit | Optional | Max results (default: 20, max: 30) |
POST /api/services/voice-numbers/purchase — Purchase a voice number
| Parameter | Status | Description |
|---|---|---|
| phoneNumber | Optional | Specific E.164 number to purchase |
| areaCode | Optional | Area 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
Manage shared resources (rooms, equipment, stations) and check their availability.
GET /api/services/resources — List resources
| Parameter | Status | Description |
|---|---|---|
| locationId | Optional | Filter by location |
| type | Optional | Filter by type (room, equipment, station) |
| active | Optional | Filter by active status |
POST /api/services/resources — Create resource
| Parameter | Status | Description |
|---|---|---|
| locationId | Required | Location ID |
| name | Required | Resource name |
| type | Optional | Type: equipment (default), room, station |
| description | Optional | Description |
| quantity | Optional | Total quantity available (default: 1) |
| active | Optional | Active 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
| Parameter | Status | Description |
|---|---|---|
| resourceId | Required | Resource ID |
| startTime | Required | ISO 8601 start time |
| endTime | Required | ISO 8601 end time |
| quantity | Optional | Requested quantity (default: 1) |
Organize locations into districts/regions for multi-location businesses.
GET /api/services/districts — List districts
POST /api/services/districts — Create district
| Parameter | Status | Description |
|---|---|---|
| name | Required | District name |
| region | Optional | Region identifier |
| description | Optional | Description |
PUT /api/services/districts?id=:id — Update district
DELETE /api/services/districts?id=:id — Delete district
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)
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
| Parameter | Status | Description |
|---|---|---|
| provider | Required | CRM provider ID (hubspot, salesforce, zohocrm, pipedrive-crm, closecom) |
| objectName | Optional | CRM object name (omit to list available objects) |
| fields | Optional | Comma-separated field names to fetch |
| limit | Optional | Max records (default: 10, max: 50) |
POST /api/services/crm-import/pull — Pull records from CRM
| Parameter | Status | Description |
|---|---|---|
| provider | Required | CRM provider ID |
| objectName | Required | CRM object to import from |
| fieldMapping | Required | Object mapping CRM fields to Schedly fields |
| category | Required | Target category (customers, services, etc.) |
| crmFields | Optional | Array of CRM field names to fetch |
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
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.
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
GET /api/services/customers/detail?id=:id — Full customer profile with appointments, invoices, and reviews
Submit a review for a completed appointment (no auth required, uses appointment UID).
POST /api/services/public-review
| Parameter | Status | Description |
|---|---|---|
| uid | Required | Appointment UID |
| rating | Required | Rating 1–5 |
| comment | Optional | Review comment |
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" }'
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 }'
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" }'
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 }'
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..."
{
"available": true,
"totalQuantity": 3,
"bookedQuantity": 1,
"availableQuantity": 2,
"requestedQuantity": 1
}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" }'
curl https://app.schedly.io/api/services/stripe-connect/status \
-H "cal-api-key: sk_live_abc123..."{
"connected": true,
"stripeAccountId": "acct_1234567890",
"chargesEnabled": true,
"payoutsEnabled": true
}curl "https://app.schedly.io/api/services/crm-import/preview?\ provider=hubspot&\ objectName=contacts&\ limit=5" \ -H "cal-api-key: sk_live_abc123..."
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" } }'
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!" }'
curl "https://app.schedly.io/api/services/insights?\ from=2026-01-01&to=2026-02-28" \ -H "cal-api-key: sk_live_abc123..."
{
"totalRevenue": 128500,
"totalBookings": 47,
"averageBookingValue": 2734,
"topServices": [
{ "name": "Haircut", "count": 22 },
{ "name": "Color Treatment", "count": 15 }
],
"newCustomers": 12
}Enable your customers to manage their own appointments through a self-service portal.
POST /api/services/portal/auth — Send magic link
GET /api/services/portal/verify — Verify portal token
GET /api/services/portal/appointments
GET /api/services/portal/purchases
GET /api/services/portal/invoices — List customer invoices
GET /api/services/portal/invoices?id=:id&format=html — Get invoice as rendered HTML
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
GET /api/services/portal/email-preferences — Get email subscription status
PUT /api/services/portal/email-preferences — Update email opt-in/opt-out
| Parameter | Status | Description |
|---|---|---|
| emailUnsubscribed | Required | true to unsubscribe, false to resubscribe |
GET /api/services/portal/sms-preferences — Get SMS consent status
PUT /api/services/portal/sms-preferences — Update SMS consent
| Parameter | Status | Description |
|---|---|---|
| smsConsent | Required | true to opt in, false to opt out |
curl -X POST https://app.schedly.io/api/services/portal/auth \ -H "Content-Type: application/json" \ -d '{ "email": "emily@example.com", "subdomain": "sarahssalon" }'
curl "https://app.schedly.io/api/services/portal/verify?\
token=ptk_abc123xyz789"Intelligent lead routing, territory management, qualification, and SLA tracking.
GET POST /api/routing/config
POST /api/routing/execute — Execute routing engine
POST /api/routing/book — Route lead and book
POST /api/routing/preview — Test/preview routing
POST /api/routing/handoff
GET POST /api/routing/qualification
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
GET POST /api/routing/sla
POST /api/routing/sla/check — SLA check (cron)
GET /api/routing/snippet — Get JS embed snippet
GET /api/routing/analytics
GET /api/services/analytics/routing-funnel — Routing funnel metrics
| Parameter | Status | Description |
|---|---|---|
| from | Optional | ISO 8601 start date |
| to | Optional | ISO 8601 end date |
GET /api/services/analytics/locations — Location analytics
| Parameter | Status | Description |
|---|---|---|
| from | Optional | ISO 8601 start date |
| to | Optional | ISO 8601 end date |
| districtId | Optional | Filter by district |
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
GET /api/routing/targets — List routing targets
POST /api/routing/targets — Create/update targets
GET /api/routing/forms — List routing forms
POST /api/routing/forms — Create a routing form
GET /api/routing/team-members — List team members for routing
POST /api/routing/public-execute — Execute routing without authentication (public embed)
GET /api/routing/audit-trail — List routing decision logs
| Parameter | Status | Description |
|---|---|---|
| from | Optional | ISO 8601 start date |
| to | Optional | ISO 8601 end date |
| limit | Optional | Max results (default: 50) |
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
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"
}
){
"routedTo": {
"userId": 12345,
"name": "Sarah Johnson",
"bookingUrl": "https://app.schedly.io/sarahj/demo"
},
"matchedTerritory": "North America",
"qualified": true,
"score": 85
}Manage subscriptions, checkout, and billing information.
POST /api/billing/checkout
| Parameter | Status | Description |
|---|---|---|
| plan | Required | pro, services, branding, or hipaa |
| period | Optional | monthly or yearly (default: monthly) |
GET /api/billing/summary
GET /api/billing/invoices
GET /api/billing/seats
POST /api/billing/cancel
POST /api/billing/resume — Resume a canceled subscription before period end
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
POST /api/billing/webhook — Stripe webhook handler
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" }'
{
"clientSecret": "cs_test_a1b2c3d4..."
}curl https://app.schedly.io/api/billing/summary \
-H "cal-api-key: sk_live_abc123..."GET /api/extension/event-types
GET /api/extension/slots
curl https://app.schedly.io/api/extension/event-types \
-H "cal-api-key: sk_live_abc123..."Integration endpoints for Google Reserve booking flow.
GET /api/reserve-with-google/availability
POST /api/reserve-with-google/booking
GET /api/reserve-with-google/feeds
GET /api/reserve-with-google/health
curl https://app.schedly.io/api/reserve-with-google/availability \
-H "cal-api-key: sk_live_abc123..."POST /api/compliance/baa — Sign Business Associate Agreement
POST /api/compliance/dpa — Sign Data Processing Agreement
GET /api/compliance/download — Download compliance docs
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" }'
Connect Schedly with thousands of apps using Zapier.
# 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.
| Event | Description |
|---|---|
| BOOKING_CREATED | New booking confirmed |
| BOOKING_RESCHEDULED | Booking time changed |
| BOOKING_CANCELLED | Booking cancelled |
| BOOKING_CONFIRMED | Pending booking confirmed |
| BOOKING_REJECTED | Pending booking rejected |
| BOOKING_PAID | Booking payment completed |
| BOOKING_PAYMENT_INITIATED | Payment initiated for booking |
| BOOKING_NO_SHOW_UPDATED | No-show status updated for attendee |
| BOOKING_REQUESTED | Booking approval requested |
| MEETING_STARTED | Video meeting started |
| MEETING_ENDED | Video meeting ended |
| MEETING_URL_CHANGED | Video meeting URL changed |
| RECORDING_READY | Meeting recording available |
| RECORDING_TRANSCRIPTION_GENERATED | Recording transcription completed |
| INSTANT_MEETING | Instant meeting created |
| FORM_SUBMITTED | Routing form submitted |
| OOO_CREATED | Out-of-office entry created |
| SERVICE_BOOKING_CREATED | Service appointment created |
| SERVICE_BOOKING_UPDATED | Service appointment updated |
| SERVICE_BOOKING_CANCELLED | Service appointment cancelled |
| SERVICE_BOOKING_COMPLETED | Service appointment completed |
All webhook payloads include the event type, timestamp, and event-specific data.
If you set a webhook secret, Schedly includes a X-Schedly-Signature header with an HMAC-SHA256 signature of the payload body.
{
"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"
}
]
}
}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) ); }
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.
https://app.schedly.io/api/mcp/sse
JSON-RPC 2.0 over HTTP POST (stateless) or SSE (streaming). Supports batch requests.
| Method | Description |
|---|---|
initialize | Handshake — returns server info, capabilities, and protocol version |
tools/list | List all 71 available tools with schemas |
tools/call | Execute a tool by name with arguments |
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": {} }'
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2024-11-05",
"capabilities": {
"tools": { "listChanged": false }
},
"serverInfo": {
"name": "schedly-mcp",
"version": "2.0.0"
}
}
}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.
Authorization: Bearer cal_YOUR_API_KEY
cal_ prefix is stripped before validationApiKey.hashedKey in the databaseuserId scopes all tool operations to that user's dataInvalid or expired keys return a JSON error (not a JSON-RPC response):
{
"error": "Invalid or missing API key. Use Authorization: Bearer cal_YOUR_KEY"
}The MCP server supports two transport modes on the same endpoint:
Send a JSON-RPC request and receive a JSON-RPC response. Best for simple integrations and testing.
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.
Send an array of JSON-RPC requests in a single POST. The server processes them sequentially and returns an array of responses.
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
[
{
"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": {}
}
}
]All tools are organized into 12 domain modules. Each tool accepts a JSON arguments object and returns structured data.
| Tool | Description |
|---|---|
get_current_user | Get profile, timezone, booking counts |
update_user_profile | Update name, bio, timezone, locale, time format |
list_api_keys | List API keys (without revealing values) |
get_out_of_office | Get current out-of-office entries |
| Tool | Description |
|---|---|
list_event_types | List all event types with optional booking counts |
get_event_type | Get full event type details by ID |
create_event_type | Create a new event type |
update_event_type | Update title, duration, description, locations, etc. |
delete_event_type | Delete an event type |
duplicate_event_type | Clone an event type with a new slug |
| Tool | Description |
|---|---|
list_bookings | List bookings with status/date filters |
get_booking | Get booking details by ID or UID |
cancel_booking | Cancel a booking with optional reason |
confirm_booking | Confirm a pending booking |
reject_booking | Reject a pending booking |
list_upcoming_bookings | Get next N upcoming bookings |
search_bookings | Search bookings by attendee name/email |
mark_no_show | Mark a booking attendee as no-show |
| Tool | Description |
|---|---|
list_schedules | List all availability schedules |
get_schedule | Get schedule details by ID |
create_schedule | Create a new availability schedule |
delete_schedule | Delete a schedule |
get_available_slots | Get open time slots for an event type + date range |
| Tool | Description |
|---|---|
list_appointments | List service appointments with filters |
get_appointment | Get appointment details by ID |
create_appointment | Create a new service appointment |
update_appointment | Update appointment details |
cancel_appointment | Cancel a service appointment |
check_availability | Check staff/service availability |
bulk_update_appointments | Update multiple appointments at once |
| Tool | Description |
|---|---|
search_customers | Search by name, email, or phone |
get_customer | Get full customer profile |
create_customer | Create a new customer record |
update_customer | Update customer details |
delete_customer | Delete a customer |
list_top_customers | Get top customers by booking count or revenue |
| Tool | Description |
|---|---|
list_locations | List all service locations |
get_location | Get location details |
list_services | List services with pricing |
get_service | Get service details |
list_staff | List staff members |
get_staff | Get staff details and assignments |
list_products | List products with stock levels |
update_product_stock | Update product inventory quantity |
list_invoices | List invoices with status filters |
list_service_categories | List service categories |
| Tool | Description |
|---|---|
list_workflows | List all automation workflows |
get_workflow | Get workflow details and steps |
create_workflow | Create a new workflow |
update_workflow | Update workflow name or trigger |
delete_workflow | Delete a workflow |
| Tool | Description |
|---|---|
list_webhooks | List configured webhooks |
get_webhook | Get webhook details |
create_webhook | Create a new webhook subscription |
update_webhook | Update webhook URL or events |
delete_webhook | Delete a webhook |
test_webhook | Send a test payload to a webhook |
| Tool | Description |
|---|---|
list_routing_forms | List all routing forms |
get_routing_form | Get form details with fields and routes |
list_routing_form_responses | Get form responses |
| Tool | Description |
|---|---|
list_teams | List teams you belong to |
get_team | Get team details |
list_team_members | List team members with roles |
| Tool | Description |
|---|---|
get_schedule_stats | Get scheduling statistics over a period |
dashboard_summary | Full operational snapshot |
booking_analytics | Booking trends and breakdowns |
popular_event_types | Most-booked event types |
busiest_days | Day-of-week booking distribution |
no_show_report | No-show rates and patterns |
revenue_report | Revenue breakdown by period |
staff_performance | Per-staff booking and completion stats |
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 } } }'
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\"count\":3, ...}"
}
]
}
}{
"serviceAppointments": {
"today": { "total": 12, "pending": 3 },
"thisWeek": { "total": 47, "revenue": "$2,340.00" }
},
"calBookings": { "today": 5, "thisWeek": 18 },
"totalCustomers": 284,
"activeLocations": 5
}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.
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).
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.
Any language or framework can call the MCP server directly via HTTP POST with JSON-RPC 2.0. No SDK required.
{
"mcpServers": {
"schedly": {
"url": "https://app.schedly.io/api/mcp/sse",
"headers": {
"Authorization": "Bearer cal_YOUR_API_KEY"
}
}
}
}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?" )
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] )] ) )
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" }'
All errors follow a consistent format with an error code, human-readable message, and HTTP status code.
| Status | Error Code | Description |
|---|---|---|
| 400 | BAD_REQUEST | The request was malformed or missing required parameters |
| 401 | UNAUTHORIZED | Authentication credentials are missing or invalid |
| 403 | FORBIDDEN | You don't have permission to access this resource |
| 404 | NOT_FOUND | The requested resource does not exist |
| 409 | CONFLICT | The request conflicts with the current state (e.g., double booking) |
| 422 | VALIDATION_ERROR | The request body failed validation |
| 429 | RATE_LIMITED | Too many requests; slow down and retry |
| 500 | INTERNAL_ERROR | An unexpected error occurred on the server |
{
"status": "error",
"error": "UNAUTHORIZED",
"message": "Invalid API key provided."
}{
"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"
}
]
}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."
}