Appointment Integration
Overview
Orchestra's Calendar API lets you push appointments into Orchestra from another system, build a customer-facing booking portal, or do both at once. The API has two parallel surfaces that differ in audience, authentication, and what they expose:
- Internal API (
/calendar-backend/api/v1/...) — full CRUD over appointments, customers, branches, services, resources, and capacity. Authenticated with an Orchestra user. Used by third-party systems that own the master record and push their changes into Orchestra. - Public API (
/calendar-backend/public/api/v1/...and/v2/...) — narrower, customer-safe surface designed for public booking journeys. Branches and appointments are addressed bypublicId(an opaque 64-char string), not by internal numeric IDs. Typically fronted by the API Gateway.
The general pattern: the third party holds the master record, Orchestra holds the queue/operational view, and the API keeps them in sync. Appointments from a contact centre, an online portal, and walk-in customers all land in the same Orchestra branch and get blended into one queue.
Use cases
Third-party booking system → Orchestra A hospital, bank, or government office books appointments in their existing scheduling platform (e.g. an EMR, contact-centre booker, partner portal). Each new booking, reschedule, or cancel is pushed to Orchestra so kiosks, screens, and staff terminals see one unified queue.
Public booking portal You build a self-service booking website or mobile app. The customer picks a branch and service, sees real available time slots, and books. Behind the scenes, calls flow through the public API (and usually the API Gateway).
Mixed flow Both at once — partner systems push appointments, your own portal also lets customers self-book against the same calendar. Orchestra is the source of truth for what's actually scheduled.
Daily sync / migration Bulk-importing from a legacy scheduling system. Use the sync endpoint to move large appointment sets in a single transaction.
When not to use this integration
Walk-in-only deployments — Orchestra's queue and ticketing endpoints (Common API) are simpler and don't require Calendar setup.
Internal Orchestra-to-Orchestra communication — talk to Central directly rather than through Calendar.
Architecture
Third-party booking Internal API
system / partner ─────────────────────────► ┌──────────────────────────┐
│ Orchestra Calendar │
│ Backend │
Public booking Public API + API Gateway │ • appointments │
website / mobile app ─────────────────────────► │ • customers │
│ • branches / services │
│ • resources / capacity │
Webhook callbacks ◄───────────────────────── │ • settings │
(status updates) └──────────┬───────────────┘
│
▼
┌──────────────────────────┐
│ Orchestra Central │
│ (visits, queues, │
│ workstations) │
└──────────────────────────┘
Components
- Calendar backend — separate Orchestra service that owns appointments, customers, branches, services, and capacity. Hosted at
/calendar-backend/.... - Internal endpoints — addressed by numeric
id(e.g.branch.id = 7). Authenticated. Full read/write. - Public endpoints — addressed by
publicId(e.g.branchPublicId,servicePublicId,publicAppointmentId). Designed for end-customer flows; some endpoints are anonymous, others require a token. - Callback endpoint — Orchestra can call back to your system when an appointment changes status. See Receiving updates.
Prerequisites
- Orchestra version: 6.1+ (calendar-backend module enabled).
- Branch and service configured in Orchestra's Business Configuration application, with
Mobile enabledticked if the appointment will surface on Mobile Ticket. - For internal API: an Orchestra user with the Calendar connector role enabled.
- For public API: an API Gateway in front, with an API token. See API Gateway guide.
- Time zones: every branch in Orchestra is configured with a TZ. Send timestamps in ISO-8601 with a TZ-aware offset where you can.
Authentication
Internal API — Basic Auth
Every internal call uses HTTP Basic Auth with an Orchestra user. The user must have the Calendar connector enabled in its role.
GET /calendar-backend/api/v1/appointments
Host: orchestra.example.com
Authorization: Basic <base64(user:password)>
In curl:
curl -u user:password "https://orchestra.example.com/calendar-backend/api/v1/appointments"
Public API — through the gateway
Public endpoints are typically exposed behind the API Gateway. Calls carry an auth-token header (a UUID issued by api-token-generator). The Gateway authenticates the request, applies rate limiting and caching, and forwards to the Orchestra calendar backend.
POST /rest/calendar-backend/public/api/v1/branches/{branchPublicId}/services/{servicePublicId}/dates/{date}/times/{time}/book
Host: api.example.com
auth-token: <gateway-token>
Content-Type: application/json
Booking flows
Flow A — Third-party pushes appointments (internal API)
This is the classic "third-party system holds the master record" pattern. The third party creates appointments in its own UI, then mirrors each change into Orchestra.
1. Create an appointment
POST /calendar-backend/api/v1/appointments
Authorization: Basic <creds>
Content-Type: application/json
{
"start": "2026-05-01T09:00:00",
"end": "2026-05-01T09:30:00",
"branch": {"id": 1},
"services": [{"id": 12}],
"customers": [{
"firstName": "Charles",
"lastName": "Ryder",
"email": "charles.ryder@example.com",
"dateOfBirth": "1903-09-21",
"externalId": "EXT-44721"
}],
"title": "Annual review",
"notes": "First-time visitor"
}
Response: 201 Created with the full appointment object including the new id (internal) and publicId (opaque). Store both — id for future internal API calls, publicId for any URL you'd embed in an email or SMS.
Reference: POST /calendar-backend/api/v1/appointments under AppointmentEndpoint.
2. Update the appointment
PUT /calendar-backend/api/v1/appointments/{id}
Send the full appointment body — fields you don't include get cleared. To reschedule, change start and end. To change the assigned service or resource, update those nested objects.
3. Cancel the appointment
DELETE /calendar-backend/api/v1/appointments/{id}
Returns 204 No Content on success.
4. Bulk daily sync
If you need to push hundreds of appointments at once (initial migration or daily batch), use the sync endpoint:
POST /calendar-backend/api/v1/appointments/sync
The request body carries a list of appointments. Orchestra processes them transactionally and returns per-record status.
5. Audit trail
Every change to an appointment is recorded. Read it via:
GET /calendar-backend/api/v1/appointments/{id}/history
GET /calendar-backend/api/v1/appointments/history?start=2026-05-01&end=2026-05-31&branch=1
Flow B — Customer self-books (public API)
This is the booking-website pattern: walk the customer through branch → service → date → time → confirm.
1. List branches
GET /calendar-backend/public/api/v1/branches
auth-token: <gateway-token>
Response is a list of branch summaries with publicId, name, address, lat/lon. Use this to populate a branch picker.
2. Show services available at the chosen branch
GET /calendar-backend/public/api/v1/branches/{branchPublicId}/services
3. Show available dates
GET /calendar-backend/public/api/v1/branches/{branchPublicId}/services/{servicePublicId}/dates
Returns dates within the next booking window where capacity exists.
4. Show available time slots for a chosen date
GET /calendar-backend/public/api/v1/branches/{branchPublicId}/services/{servicePublicId}/dates/{date}/times
The response is a list of time strings (e.g. 09:00, 09:30, ...) representing slots that have at least one resource free.
5. Reserve and confirm (two-step)
For flows where you collect customer details after picking a slot — recommended because it stops two customers grabbing the same slot:
POST /calendar-backend/public/api/v1/branches/{branchPublicId}/services/{servicePublicId}/dates/{date}/times/{time}/reserve
The slot is now held under a temporary appointment in status 10 (reservation). The response includes a publicAppointmentId. Show the customer the booking form and on submit, confirm:
POST /calendar-backend/public/api/v1/branches/appointments/{publicAppointmentId}/confirm
Content-Type: application/json
{
"customer": {
"firstName": "Charles",
"lastName": "Ryder",
"email": "charles.ryder@example.com",
"phone": "+44 20 7946 0958"
},
"title": "Annual review",
"notes": ""
}
If the customer abandons the form, the reservation expires (default ~5 minutes) and the slot becomes bookable again.
6. Or book directly (single-step)
If you have all customer data already, skip the reserve step:
POST /calendar-backend/public/api/v1/branches/{branchPublicId}/services/{servicePublicId}/dates/{date}/times/{time}/book
Content-Type: application/json
{
"title": "Annual review",
"notes": "",
"customer": {
"firstName": "Charles",
"lastName": "Ryder",
"email": "charles.ryder@example.com",
"phone": "+44 20 7946 0958",
"dateOfBirth": "1903-09-21"
}
}
Response: 201 Created with the booked appointment, including its publicId for use in confirmation emails.
Flow C — Multi-customer / multi-service (public API v2)
For appointments where multiple customers share a slot, or one slot covers several services, use the v2 endpoints:
POST /calendar-backend/public/api/v2/branches/{branchPublicId}/dates/{date}/times/{time}/book
Body carries services: [...] and customers: [...] arrays. Useful for family appointments, multi-service consultations, or any scenario where the slot's not 1:1.
The v2 surface also has helpful helpers:
GET /branches/available— branches that can serve a given set of services.GET /branches/nextAvailableSlot— first free slot across a list of branches.POST /branches/{branchPublicId}/reserveFirstAvailable— reserve the next available slot without picking a date manually.POST /branches/{branchPublicId}/reserveRandomAvailable— reserve one of the first 10 available, useful for load-spreading.PUT /branches/appointments/{publicAppointmentId}/extend— extend an active reservation by N minutes.
Customer management
Customers are first-class objects in the calendar backend. The internal endpoints (/api/v1/customers) let third parties manage customer records the same way they manage appointments.
POST /calendar-backend/api/v1/customers create
GET /calendar-backend/api/v1/customers list
GET /calendar-backend/api/v1/customers/{id} get by internal id
PUT /calendar-backend/api/v1/customers/{id} update
DELETE /calendar-backend/api/v1/customers/{id} delete
GET /calendar-backend/api/v1/customers/search search by criteria
GET /calendar-backend/api/v1/customers/searchcustomer free-text search
Customer body:
{
"id": 4521,
"publicId": "abc123…64chars",
"externalId": "EXT-44721",
"firstName": "Charles",
"lastName": "Ryder",
"name": "Charles Ryder",
"email": "charles.ryder@example.com",
"phone": "+44 20 7946 0958",
"dateOfBirth": "1903-09-21",
"identificationNumber": "AB123456C",
"addressLine1": "31 Marchmain House",
"addressLine2": "",
"addressCity": "London",
"addressZip": "SW1A 1AA",
"addressCountry": "GB",
"addressState": "",
"custom": "{\"loyaltyTier\":\"gold\"}",
"created": "2026-04-22T10:00:00.000Z",
"updated": "2026-04-22T10:00:00.000Z"
}
externalId is the field to use when mapping to your master record. publicId is what you embed in customer-facing URLs. custom is a free-form string — most customers JSON-encode it.
For the public API, customer endpoints are read-only and addressed by publicId:
GET /calendar-backend/public/api/v1/customers/identify?… lookup by personal details
GET /calendar-backend/public/api/v1/customers/{publicId} get
GET /calendar-backend/public/api/v1/customers/{publicId}/appointments upcoming appointments
GET /calendar-backend/public/api/v1/customers/externalId/{externalId}/appointments by mapping ID
Use customers/identify to locate a returning customer by partial details (name + DOB + last 4 of card etc.) so they don't have to retype everything.
Receiving updates (webhook callback)
When an appointment changes status in Orchestra (booked, called, ended, cancelled), Orchestra can POST a callback to your system so the third-party master record stays current.
The callback endpoint shape is defined by the public API:
PUT /calendar-backend/public/api/v1/appointments/callback
Configure the callback URL and authentication in Orchestra's calendar settings. Each callback delivers a status-update payload — appointmentId, publicId, new status, timestamp.
To poll instead of receive callbacks, fetch by status:
GET /calendar-backend/api/v1/appointments/search?status=…&from=…&to=…
Reschedule and cancel from the public API
Two patterns the customer-facing portal will need:
Reschedule — moves an appointment to a new slot, keeping the same publicId:
PUT /calendar-backend/public/api/v1/appointments/{publicId}/reschedule
Content-Type: application/json
{ "newDate": "2026-05-08", "newTime": "11:00" }
Cancel — deletes the appointment:
DELETE /calendar-backend/public/api/v1/appointments/{publicId}
Both endpoints accept anonymous calls if you've configured the calendar to allow customer self-service cancel/reschedule by publicId. If not, the customer needs to authenticate first.
Locking (concurrent edits)
When two staff members might edit the same appointment, the API offers an optimistic lock:
PUT /calendar-backend/api/v1/appointments/{id}/lock acquire lock
DELETE /calendar-backend/api/v1/appointments/{id}/lock release lock
Acquire before editing, release after — or it auto-expires after a few minutes.
Troubleshooting
| Symptom | Likely cause | Resolution |
|---|---|---|
401 Unauthorized on internal endpoints | Wrong Basic Auth or user lacks Calendar role | Check the user has the Calendar connector enabled in its role. |
403 Forbidden on public endpoints | API token is invalid or revoked at the Gateway | Re-issue with api-token-generator; check the Gateway logs. |
404 Not Found when booking | publicId for branch or service is wrong, or the branch isn't Mobile enabled | Confirm the IDs from GET /public/api/v1/branches and that the branch is mobile-enabled in Business Configuration. |
409 Conflict on book or reserve | Two callers raced for the same slot, or the slot's been taken since you fetched the times list | Refresh /times and retry. Use the reserve→confirm flow for high-contention slots. |
| Reservation expires before customer can confirm | Default 5-minute hold is too short | Either lengthen the reservation timeout in calendar settings, or use PUT /v2/branches/appointments/{id}/extend. |
| Times look wrong by an hour | Server is interpreting wall-clock vs. UTC differently than your client | Always send ISO-8601 with TZ offset; the branch has its own configured TZ in GET /api/v1/branches/timezones. |
| Customer info not flowing back to staff terminal | Custom field mapping in Orchestra not configured | Map calendar customer fields to Orchestra Central customer fields in System Administration. |
FAQ
Which version of the API should I use — v1 or v2? For new public booking flows that need multi-service or multi-customer support, use v2. For everything else, v1 is fine and stable. The endpoints aren't deprecated — they coexist.
How do I attach a custom ID from my system to an Orchestra appointment?
Use the customer's externalId field (most common) or the appointment's notes/custom-parameter fields. externalId is indexed and queryable via GET /public/api/v1/customers/externalId/{externalId}/appointments.
How do I block out a time slot without booking a real appointment?
Use the blocking appointment search and create endpoints under /api/v1/appointments/blocking/....
Can I see what's in the calendar cache?
Yes — GET /api/v1/cachestatistics and the related cache-key lookup endpoints expose internal cache state for debugging hot-path performance.
Related resources
- Calendar API reference — every Calendar endpoint with parameters, schemas, and a "Try it" panel.
- Common API reference — public appointment endpoints under
/rest/appointment/...(legacy alongside calendar-backend). - API Gateway guide — the recommended way to expose public endpoints to external clients.
- Mobile App Integration guide — surface appointments and tickets in your mobile app.
- HL7 Integration guide — integrate appointments via HL7 messaging from a Hospital Information System.
- Personal Data API reference — retention policies for customer data once it's in Orchestra.