Acuity Scheduling
Access the Acuity Scheduling API with managed OAuth authentication. Manage appointments, calendars, clients, availability, and more.
Quick Start
# List appointments
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://gateway.maton.ai/acuity-scheduling/appointments?max=10')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Base URL
https://gateway.maton.ai/acuity-scheduling/{resource}
Replace {resource} with the Acuity API resource path. The gateway proxies requests to acuityscheduling.com/api/v1 and automatically injects your OAuth token.
Authentication
All requests require the Maton API key in the Authorization header:
Authorization: Bearer $MATON_API_KEY
Environment Variable: Set your API key as MATON_API_KEY:
export MATON_API_KEY="YOUR_API_KEY"
Getting Your API Key
- Sign in or create an account at maton.ai
- Go to maton.ai/settings
- Copy your API key
Connection Management
Manage your Acuity Scheduling OAuth connections at https://ctrl.maton.ai.
List Connections
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections?app=acuity-scheduling&status=ACTIVE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Create Connection
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'app': 'acuity-scheduling'}).encode()
req = urllib.request.Request('https://ctrl.maton.ai/connections', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Get Connection
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
{
"connection": {
"connection_id": "21fd90f9-5935-43cd-b6c8-bde9d915ca80",
"status": "ACTIVE",
"creation_time": "2025-12-08T07:20:53.488460Z",
"last_updated_time": "2026-01-31T20:03:32.593153Z",
"url": "https://connect.maton.ai/?session_token=...",
"app": "acuity-scheduling",
"metadata": {}
}
}
Open the returned url in a browser to complete OAuth authorization.
Delete Connection
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}', method='DELETE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Specifying Connection
If you have multiple Acuity Scheduling connections, specify which one to use with the Maton-Connection header:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://gateway.maton.ai/acuity-scheduling/appointments')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Maton-Connection', '21fd90f9-5935-43cd-b6c8-bde9d915ca80')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
If omitted, the gateway uses the default (oldest) active connection.
API Reference
Account Information
Get Account Info
GET /acuity-scheduling/me
Returns account information including timezone, scheduling page URL, and plan details.
Response:
{
"id": 12345,
"email": "user@example.com",
"timezone": "America/Los_Angeles",
"name": "My Business",
"schedulingPage": "https://app.acuityscheduling.com/schedule.php?owner=12345",
"plan": "Professional",
"currency": "USD"
}
Appointments
List Appointments
GET /acuity-scheduling/appointments
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
max |
integer | Maximum results (default: 100) |
minDate |
date | Appointments on or after this date |
maxDate |
date | Appointments on or before this date |
calendarID |
integer | Filter by calendar |
appointmentTypeID |
integer | Filter by appointment type |
canceled |
boolean | Include canceled appointments (default: false) |
firstName |
string | Filter by client first name |
lastName |
string | Filter by client last name |
email |
string | Filter by client email |
excludeForms |
boolean | Omit intake forms for faster response |
direction |
string | Sort order: ASC or DESC (default: DESC) |
Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://gateway.maton.ai/acuity-scheduling/appointments?max=10&minDate=2026-02-01')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
[
{
"id": 1630290133,
"firstName": "Jane",
"lastName": "McTest",
"phone": "1235550101",
"email": "jane.mctest@example.com",
"date": "February 4, 2026",
"time": "9:30am",
"endTime": "10:20am",
"datetime": "2026-02-04T09:30:00-0800",
"type": "Consultation",
"appointmentTypeID": 88791369,
"duration": "50",
"calendar": "Chris",
"calendarID": 13499175,
"canceled": false,
"confirmationPage": "https://app.acuityscheduling.com/schedule.php?..."
}
]
Get Appointment
GET /acuity-scheduling/appointments/{id}
Create Appointment
POST /acuity-scheduling/appointments
Content-Type: application/json
{
"datetime": "2026-02-15T09:00",
"appointmentTypeID": 123,
"firstName": "John",
"lastName": "Doe",
"email": "john.doe@example.com",
"phone": "555-123-4567",
"timezone": "America/New_York"
}
Required Fields:
datetime- Date and time (parseable by PHP's strtotime)appointmentTypeID- Appointment type IDfirstName- Client's first namelastName- Client's last nameemail- Client's email
Optional Fields:
phone- Client phone numbercalendarID- Specific calendar (auto-selected if omitted)timezone- Client's timezonecertificate- Package or coupon codenotes- Admin notesaddonIDs- Array of addon IDsfields- Array of form field values
Example:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({
'datetime': '2026-02-15T09:00',
'appointmentTypeID': 123,
'firstName': 'John',
'lastName': 'Doe',
'email': 'john.doe@example.com'
}).encode()
req = urllib.request.Request('https://gateway.maton.ai/acuity-scheduling/appointments', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Update Appointment
PUT /acuity-scheduling/appointments/{id}
Content-Type: application/json
{
"firstName": "Jane",
"lastName": "Smith",
"email": "jane.smith@example.com"
}
Cancel Appointment
PUT /acuity-scheduling/appointments/{id}/cancel
Returns the canceled appointment with canceled: true.
Reschedule Appointment
PUT /acuity-scheduling/appointments/{id}/reschedule
Content-Type: application/json
{
"datetime": "2026-02-20T10:00"
}
Note: The new datetime must be an available time slot.
Calendars
List Calendars
GET /acuity-scheduling/calendars
Response:
[
{
"id": 13499175,
"name": "Chris",
"email": "",
"replyTo": "chris@example.com",
"description": "",
"location": "",
"timezone": "America/Los_Angeles"
}
]
Appointment Types
List Appointment Types
GET /acuity-scheduling/appointment-types
Query Parameters:
includeDeleted(boolean) - Include deleted types
Response:
[
{
"id": 88791369,
"name": "Consultation",
"active": true,
"description": "",
"duration": 50,
"price": "45.00",
"category": "",
"color": "#ED7087",
"private": false,
"type": "service",
"calendarIDs": [13499175],
"schedulingUrl": "https://app.acuityscheduling.com/schedule.php?..."
}
]
Availability
Get Available Dates
GET /acuity-scheduling/availability/dates?month=2026-02&appointmentTypeID=123
Required Parameters:
month- Month to check (e.g., "2026-02")appointmentTypeID- Appointment type ID
Optional Parameters:
calendarID- Specific calendartimezone- Timezone for results (e.g., "America/New_York")
Response:
[
{"date": "2026-02-09"},
{"date": "2026-02-10"},
{"date": "2026-02-11"}
]
Get Available Times
GET /acuity-scheduling/availability/times?date=2026-02-10&appointmentTypeID=123
Required Parameters:
date- Date to checkappointmentTypeID- Appointment type ID
Optional Parameters:
calendarID- Specific calendartimezone- Timezone for results
Response:
[
{"time": "2026-02-10T09:00:00-0800", "slotsAvailable": 1},
{"time": "2026-02-10T09:50:00-0800", "slotsAvailable": 1},
{"time": "2026-02-10T10:40:00-0800", "slotsAvailable": 1}
]
Clients
List Clients
GET /acuity-scheduling/clients
Query Parameters:
search- Filter by first name, last name, or phone
Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://gateway.maton.ai/acuity-scheduling/clients?search=John')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
[
{
"firstName": "Jane",
"lastName": "McTest",
"email": "jane.mctest@example.com",
"phone": "(123) 555-0101",
"notes": ""
}
]
Create Client
POST /acuity-scheduling/clients
Content-Type: application/json
{
"firstName": "John",
"lastName": "Doe",
"email": "john@example.com",
"phone": "555-123-4567"
}
Update Client
PUT /acuity-scheduling/clients
Content-Type: application/json
{
"firstName": "John",
"lastName": "Doe",
"email": "john.updated@example.com"
}
Note: Client update/delete only works for clients with existing appointments.
Delete Client
DELETE /acuity-scheduling/clients
Content-Type: application/json
{
"firstName": "John",
"lastName": "Doe"
}
Blocks
List Blocks
GET /acuity-scheduling/blocks
Query Parameters:
max- Maximum results (default: 100)minDate- Blocks on or after this datemaxDate- Blocks on or before this datecalendarID- Filter by calendar
Get Block
GET /acuity-scheduling/blocks/{id}
Create Block
POST /acuity-scheduling/blocks
Content-Type: application/json
{
"start": "2026-02-15T12:00",
"end": "2026-02-15T13:00",
"calendarID": 1234,
"notes": "Lunch break"
}
Response:
{
"id": 9589304654,
"calendarID": 13499175,
"start": "2026-02-15T12:00:00-0800",
"end": "2026-02-15T13:00:00-0800",
"notes": "Lunch break",
"description": "Sunday, February 15, 2026 12:00pm - 1:00pm"
}
Delete Block
DELETE /acuity-scheduling/blocks/{id}
Returns 204 No Content on success.
Forms
List Forms
GET /acuity-scheduling/forms
Response:
[
{
"id": 123,
"name": "Client Intake Form",
"appointmentTypeIDs": [456, 789],
"fields": [
{
"id": 1,
"name": "How did you hear about us?",
"type": "dropdown",
"options": ["Google", "Friend", "Social Media"],
"required": true
}
]
}
]
Labels
List Labels
GET /acuity-scheduling/labels
Response:
[
{"id": 23116714, "name": "Checked In", "color": "green"},
{"id": 23116715, "name": "Completed", "color": "pink"},
{"id": 23116713, "name": "Confirmed", "color": "yellow"}
]
Pagination
Acuity Scheduling uses the max parameter to limit results. Use minDate and maxDate to paginate through date ranges:
# First page
GET /acuity-scheduling/appointments?max=100&minDate=2026-01-01&maxDate=2026-01-31
# Next page
GET /acuity-scheduling/appointments?max=100&minDate=2026-02-01&maxDate=2026-02-28
Code Examples
JavaScript
const response = await fetch(
'https://gateway.maton.ai/acuity-scheduling/appointments?max=10',
{
headers: {
'Authorization': `Bearer ${process.env.MATON_API_KEY}`
}
}
);
const appointments = await response.json();
Python
import os
import requests
response = requests.get(
'https://gateway.maton.ai/acuity-scheduling/appointments',
headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'},
params={'max': 10}
)
appointments = response.json()
Notes
- Datetime values must be parseable by PHP's
strtotime()function - Timezones use IANA format (e.g., "America/New_York", "America/Los_Angeles")
- Client update/delete requires clients to have existing appointments
- Rescheduling requires the new datetime to be an available time slot
- Use
excludeForms=truefor faster appointment list responses - IMPORTANT: When using curl commands, use
curl -gwhen URLs contain brackets to disable glob parsing - IMPORTANT: When piping curl output to
jqor other commands, environment variables like$MATON_API_KEYmay not expand correctly in some shell environments. You may get "Invalid API key" errors when piping.
Error Handling
| Status | Meaning |
|---|---|
| 400 | Invalid request (e.g., time not available, client not found) |
| 401 | Invalid or missing Maton API key |
| 404 | Resource not found |
| 429 | Rate limited |
| 4xx/5xx | Passthrough error from Acuity API |
Troubleshooting: Invalid API Key
When you receive a "Invalid API key" error, ALWAYS follow these steps before concluding there is an issue:
- Check that the
MATON_API_KEYenvironment variable is set:
echo $MATON_API_KEY
- Verify the API key is valid by listing connections:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF