Appointment Guide
Learn how to create, hold, confirm, and manage appointments through the v3 API without exposing internal booking logic.
Environment tip: All examples use
v3.onsched.comfor production. Replace the host withapi-stage.onsched.comwhen calling the staging environment.
When to Use
- Build customer-facing booking flows (create holds, finalize bookings, cancel or reschedule).
- Power operational tooling that needs filtered appointment lists or per-record inspection.
- Sync appointments to downstream systems via polling or in combination with webhooks.
Prerequisites
- Authentication: Either OAuth2 client credentials (
POST /v3/oauth/token) or the dashboard trio ofAuthorization: Bearer <JWT>,x-api-key, andx-client-id. - Company context: Requests are scoped to the authenticated company. Location and service IDs must belong to that company.
- Valid availability check: Always call
GET /v3/availabilityfirst to present conflict-free slots.
Endpoint Overview
| Endpoint | Purpose |
|---|---|
GET /v3/appointments | Paginated list with optional filters (limit, page). |
GET /v3/appointments/filter | Filter by status, date range, resource, or location via query params. |
GET /v3/appointment/:id | Retrieve a single appointment (public routes only return minimal fields). |
POST /v3/appointment | Create a booked appointment immediately. |
POST /v3/appointment/hold | Create a short-lived hold (status: IN) so customers can finish intake steps. |
PUT /v3/appointment/:id/book | Convert a hold to a booked record without changing slot times. |
PUT /v3/appointment/:id | Update mutable properties (notes, metadata, CustomFields, etc.). |
PUT /v3/appointment/:id/reschedule | Move an appointment to a new slot. The request must pass availability validation. |
PUT /v3/appointment/:id/confirm | Mark the appointment as confirmed (status BK, confirmed: true). |
PUT /v3/appointment/:id/cancel | Cancel and trigger notifications + webhooks. |
DELETE /v3/appointment/:id | Hard delete; typically reserved for internal tooling. |
All write operations are validated server-side (see files under api/validations/appointment/). Invalid IDs or overlapping times return 400 errors without revealing internal scheduling rules.
Booking Flow
- Hold (optional):
POST /v3/appointment/holdwithUnavailability.startTime/endTimeand optionalCustomer,CustomFields, andResourceIds. Holds respect expiration windows defined per location (expirationDelayseconds). - Confirm / Book: Either call
PUT /v3/appointment/:id/bookto finalize a hold or skip straight toPOST /v3/appointmentto create a booked record in one step. - Resource Assignment: Provide
ResourceIdsas repeated query string parameters (e.g.,ResourceIds=id1&ResourceIds=id2). When omitted, the service’s eligible resources are auto-assigned based on the round robin mode you requested during availability. - Notifications/Webhooks: The platform automatically dispatches notifications and webhooks unless you pass
skip_notifications=truein the request body—use sparingly and send your own alerts if you opt out. - Calendar Events: When resources have connected calendars, events are created automatically after the appointment succeeds. Cancelling or rescheduling removes or updates those events.
Status Lifecycle
api/enums/models.js defines the canonical status values:
IN– Initial hold. Auto-expires if not promoted.BK– Booked appointment (most common steady state).RS– Reserved hold that is kept longer than a quickIN.RE– Rescheduled placeholder left behind when a booking is moved.CN– Cancelled appointment.
The API prevents conflicting state transitions (for example, cancelling a CN appointment returns a 400). Use holds for multi-step checkout; skip them for instant bookings.
Custom Fields & Metadata
- Send
CustomFieldsas an object. Keys must exist in your schema configuration. Customeraccepts either an existingidor the fields required to create a new record (firstName,emailat minimum).Unavailabilityrepresents the time block; padding is computed server-side when the service has non-zero padding.
Example: Create a Hold Then Book It
# Step 1: create hold (expires automatically if untouched)
curl -X POST https://v3.onsched.com/v3/appointment/hold \
-H "Authorization: Bearer <token>" \
-H "x-api-key: <company-key>" \
-H "Content-Type: application/json" \
-d '{
"LocationId": "<location-id>",
"ServiceId": "<service-id>",
"ResourceIds": ["<resource-id>"],
"Customer": {
"firstName": "Ada",
"lastName": "Lovelace",
"email": "[email protected]"
},
"Unavailability": {
"startTime": "2025-03-14T15:00:00Z",
"endTime": "2025-03-14T15:30:00Z"
}
}'
# Step 2: finalize
curl -X PUT https://v3.onsched.com/v3/appointment/<hold-id>/book \
-H "Authorization: Bearer <token>" \
-H "x-api-key: <company-key>" \
-H "Content-Type: application/json" \
-d '{"notes": "Confirmed via concierge"}'Troubleshooting
- “The selected time slot is not available.” Confirm you are passing the same
ResourceIdsthat were returned by the availability search and that no one else booked the slot in the meantime. - Invalid Customer errors: Ensure either
Customer.idexists or you include required creation fields. Holds without customer context only work whenisHoldis true. - Notifications skipped unintentionally: Avoid setting
skip_notificationsunless you intentionally handle downstream messages. - Missing fields from public routes: Public endpoints deliberately trim fields like
notes,CustomFields, and contact info. Use authenticated company routes for internal tooling.
With these patterns you can compose reliable booking flows while keeping OnSched’s scheduling engine as the source of truth.
Updated 4 days ago
