Acuity Scheduling
Acuity Scheduling API integration for appointment booking, availability management, and calendar sync
You are an expert in integrating Acuity Scheduling (by Squarespace) for appointment booking and calendar management.
## Key Points
- Always specify timezone when checking availability and creating appointments to avoid off-by-one day errors.
- Use appointment type IDs rather than names for reliability; names can be changed by account owners.
- Validate availability before booking — the API will reject appointments at unavailable times, but checking first provides a better user experience.
- Cache appointment types and calendar lists since they change infrequently; availability data should always be fetched fresh.
- Use webhook events instead of polling for appointment status changes.
- Acuity's API has rate limits (not publicly documented); implement exponential backoff on 429 responses to avoid disruption.
- The `datetime` field in appointment creation must match an available slot exactly — partial-hour offsets or arbitrary times will be rejected.
- Webhook payloads only contain the appointment ID and action, not the full appointment object; you must make a follow-up GET request to fetch details.
- OAuth tokens do not expire but can be revoked; always handle 401 responses gracefully by prompting re-authorization.
- Intake form fields use numeric IDs that differ per account; query `/appointment-types/{id}` to discover the correct field IDs before submitting.
## Quick Example
```bash
# Acuity uses HTTP Basic Auth: userId:apiKey
# Store credentials securely
export ACUITY_USER_ID="your-user-id"
export ACUITY_API_KEY="your-api-key"
```skilldb get scheduling-services-skills/Acuity SchedulingFull skill: 259 linesAcuity Scheduling — Scheduling Integration
You are an expert in integrating Acuity Scheduling (by Squarespace) for appointment booking and calendar management.
Core Philosophy
Overview
Acuity Scheduling is a client-facing appointment scheduling platform owned by Squarespace. It provides a REST API for managing appointments, availability, calendars, and clients programmatically. Acuity excels at service-based businesses where clients book specific appointment types with specific providers. The API uses HTTP Basic Auth with a user ID and API key, returning JSON responses.
Setup & Configuration
Authentication
# Acuity uses HTTP Basic Auth: userId:apiKey
# Store credentials securely
export ACUITY_USER_ID="your-user-id"
export ACUITY_API_KEY="your-api-key"
Basic Client Setup
import axios, { AxiosInstance } from "axios";
interface AcuityConfig {
userId: string;
apiKey: string;
baseUrl?: string;
}
function createAcuityClient(config: AcuityConfig): AxiosInstance {
const client = axios.create({
baseURL: config.baseUrl || "https://acuityscheduling.com/api/v1",
auth: {
username: config.userId,
password: config.apiKey,
},
headers: {
"Content-Type": "application/json",
},
});
return client;
}
const acuity = createAcuityClient({
userId: process.env.ACUITY_USER_ID!,
apiKey: process.env.ACUITY_API_KEY!,
});
OAuth2 Setup (for third-party apps)
import axios from "axios";
async function exchangeCodeForToken(
code: string,
clientId: string,
clientSecret: string,
redirectUri: string
): Promise<string> {
const response = await axios.post(
"https://acuityscheduling.com/oauth2/token",
{
grant_type: "authorization_code",
code,
client_id: clientId,
client_secret: clientSecret,
redirect_uri: redirectUri,
}
);
return response.data.access_token;
}
Core Patterns
Fetching Appointment Types
interface AppointmentType {
id: number;
name: string;
description: string;
duration: number;
price: string;
category: string;
color: string;
private: boolean;
calendarIDs: number[];
}
async function getAppointmentTypes(): Promise<AppointmentType[]> {
const { data } = await acuity.get<AppointmentType[]>("/appointment-types");
return data.filter((type) => !type.private);
}
Checking Availability
interface AvailabilitySlot {
time: string; // ISO 8601
slotsAvailable: number;
}
interface AvailabilityQuery {
appointmentTypeID: number;
date: string; // YYYY-MM-DD
calendarID?: number;
timezone?: string;
}
async function getAvailableDates(
month: string,
appointmentTypeID: number
): Promise<{ date: string }[]> {
const { data } = await acuity.get("/availability/dates", {
params: { month, appointmentTypeID },
});
return data;
}
async function getAvailableTimes(
query: AvailabilityQuery
): Promise<AvailabilitySlot[]> {
const { data } = await acuity.get<AvailabilitySlot[]>(
"/availability/times",
{ params: query }
);
return data;
}
Creating an Appointment
interface CreateAppointmentPayload {
datetime: string; // ISO 8601
appointmentTypeID: number;
firstName: string;
lastName: string;
email: string;
phone?: string;
timezone?: string;
fields?: { id: number; value: string }[];
}
interface Appointment {
id: number;
datetime: string;
endTime: string;
type: string;
firstName: string;
lastName: string;
email: string;
confirmationPage: string;
canceled: boolean;
}
async function bookAppointment(
payload: CreateAppointmentPayload
): Promise<Appointment> {
const { data } = await acuity.post<Appointment>("/appointments", payload);
return data;
}
Canceling and Rescheduling
async function cancelAppointment(appointmentId: number): Promise<Appointment> {
const { data } = await acuity.put<Appointment>(
`/appointments/${appointmentId}/cancel`
);
return data;
}
async function rescheduleAppointment(
appointmentId: number,
newDatetime: string
): Promise<Appointment> {
const { data } = await acuity.put<Appointment>(
`/appointments/${appointmentId}/reschedule`,
{ datetime: newDatetime }
);
return data;
}
Webhook Handling
import express, { Request, Response } from "express";
// Acuity sends webhooks for appointment lifecycle events
// Register webhooks at: Settings > Integrations > API > Webhooks
function setupWebhooks(app: express.Application): void {
app.post("/webhooks/acuity", (req: Request, res: Response) => {
const { action, id } = req.body;
switch (action) {
case "scheduled":
handleNewAppointment(id);
break;
case "rescheduled":
handleReschedule(id);
break;
case "canceled":
handleCancellation(id);
break;
case "changed":
handleUpdate(id);
break;
}
res.status(200).send("OK");
});
}
Best Practices
- Always specify timezone when checking availability and creating appointments to avoid off-by-one day errors.
- Use appointment type IDs rather than names for reliability; names can be changed by account owners.
- Validate availability before booking — the API will reject appointments at unavailable times, but checking first provides a better user experience.
- Cache appointment types and calendar lists since they change infrequently; availability data should always be fetched fresh.
- Use webhook events instead of polling for appointment status changes.
Common Pitfalls
- Acuity's API has rate limits (not publicly documented); implement exponential backoff on 429 responses to avoid disruption.
- The
datetimefield in appointment creation must match an available slot exactly — partial-hour offsets or arbitrary times will be rejected. - Webhook payloads only contain the appointment ID and action, not the full appointment object; you must make a follow-up GET request to fetch details.
- OAuth tokens do not expire but can be revoked; always handle 401 responses gracefully by prompting re-authorization.
- Intake form fields use numeric IDs that differ per account; query
/appointment-types/{id}to discover the correct field IDs before submitting.
Anti-Patterns
Using the service without understanding its pricing model. Cloud services bill differently — per request, per GB, per seat. Deploying without modeling expected costs leads to surprise invoices.
Hardcoding configuration instead of using environment variables. API keys, endpoints, and feature flags change between environments. Hardcoded values break deployments and leak secrets.
Ignoring the service's rate limits and quotas. Every external API has throughput limits. Failing to implement backoff, queuing, or caching results in dropped requests under load.
Treating the service as always available. External services go down. Without circuit breakers, fallbacks, or graceful degradation, a third-party outage becomes your outage.
Coupling your architecture to a single provider's API. Building directly against provider-specific interfaces makes migration painful. Wrap external services in thin adapter layers.
Install this skill directly: skilldb add scheduling-services-skills
Related Skills
Cal.com
"Cal.com: open-source scheduling, booking API, event types, availability, webhooks, embeds, self-hosted"
Calendly
"Calendly API: scheduling links, event types, invitees, webhooks, organization management, OAuth, REST API"
Cronofy
"Cronofy: calendar API, availability, scheduling, real-time sync, conferencing, UI elements, enterprise calendar integration"
Doodle
Doodle API integration for group scheduling polls, 1:1 booking pages, and meeting coordination
Microsoft Bookings
Microsoft Bookings integration via Microsoft Graph API for enterprise appointment scheduling and calendar management
Nylas
"Nylas: unified calendar/email/contacts API, scheduling, calendar CRUD, email send/read, OAuth providers, Node SDK"