{"openapi":"3.1.0","info":{"title":"AutoSync API","version":"1.0.0","summary":"Public REST + MCP API for AI agents and integrations.","description":"AutoSync exposes a unified API across Mindbody, Booker, and HighLevel calendars.\nAI agents (Claude, ChatGPT, Cursor) connect via the MCP server at `https://autosync.io/mcp`\nusing the same bearer token. Other tools call REST directly.\n\n## Authentication\nSend `Authorization: Bearer asyk_live_<43-char-base64url>` on every request.\nMint keys from the AutoSync settings page in your HighLevel Custom Page.\n\n## Rate limiting\nDefault 60 req/min per key. Headers `X-RateLimit-Limit`, `X-RateLimit-Remaining`,\n`X-RateLimit-Reset` are returned on every response. 429s include a `Retry-After`\nheader in seconds.\n\n## Idempotency\nSend `Idempotency-Key: <uuid>` on POST/PATCH/DELETE to make retries safe.\nStored 24 hours; replays of the same key + body return the cached response.\nDifferent body + same key returns 409 (`idempotency.key_reused`).\n\n## Errors\nAll errors share the `{ error: { code, message, retryable, ... } }` envelope.\nMatch on `code` (stable) rather than `message` (subject to wording changes).","contact":{"name":"AutoSync support","email":"support@autosync.io","url":"https://autosync.io"},"license":{"name":"Commercial — see your AutoSync subscription terms"}},"servers":[{"url":"https://autosync.io","description":"Production"},{"url":"https://dev.autosync.io","description":"Development (use for testing)"}],"tags":[{"name":"Discovery","description":"Read-only endpoints for service catalogs, staff, rooms. Backed by a 6-hour cache (`service_catalog`); for live availability use the Availability tag."},{"name":"Availability","description":"Live free-slot lookups. Always passes through to the platform — no caching at this layer."},{"name":"Bookings","description":"Create, read, update, and cancel appointments and class registrations. All write methods accept `Idempotency-Key` headers for replay protection."},{"name":"Meta","description":"Health checks, catalog discovery, and the OpenAPI spec itself."}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"asyk_<env>_<43-char-base64url>","description":"Mint keys via the AutoSync settings page in your HighLevel Custom Page. Pro tier or active API access subscription required."}},"schemas":{"ErrorResponse":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","retryable"],"properties":{"code":{"type":"string","description":"Stable, dotted error code (e.g. `auth.invalid_key`, `bookings.not_found`). Bots should match on this rather than the human-readable message, which may change.","example":"validation.failed"},"message":{"type":"string","description":"Human-readable explanation. Subject to wording changes; do not parse."},"retryable":{"type":"boolean","description":"Whether the request might succeed if retried. Generally `true` for 5xx and 429; `false` for 4xx (except 429)."}},"additionalProperties":true}}},"Contact":{"type":"object","description":"Contact identity for booking creation. At least one of `ghlContactId`, `platformContactId`, `email`, or `phone` is required. AutoSync will attempt to find a matching contact across both GHL and the booking platform; if none exists, a new one is created when `autoCreateContact` is true.","properties":{"ghlContactId":{"type":"string","description":"HighLevel contact ID."},"platformContactId":{"type":"string","description":"Mindbody ClientId / Booker CustomerID."},"email":{"type":"string","format":"email"},"phone":{"type":"string","description":"E.164 format preferred. Trailing-digit matching is used for cross-platform lookup."},"firstName":{"type":"string"},"lastName":{"type":"string"}}},"Booking":{"type":"object","description":"Normalized booking record. Field shapes are consistent across platforms; platform-specific fields land under `meta`.","properties":{"id":{"type":"string","description":"Platform-side booking ID."},"status":{"type":"string","enum":["booked","confirmed","arrived","completed","cancelled","noshow","late_cancel"]},"startDateTime":{"type":"string","format":"date-time"},"endDateTime":{"type":"string","format":"date-time"},"serviceName":{"type":"string","nullable":true},"staffName":{"type":"string","nullable":true},"resourceName":{"type":"string","nullable":true},"contactId":{"type":"string","nullable":true},"meta":{"type":"object","additionalProperties":true,"description":"Platform-specific extras the bot may want."}}},"AvailabilitySlot":{"type":"object","properties":{"startDateTime":{"type":"string","format":"date-time"},"endDateTime":{"type":"string","format":"date-time"},"durationMin":{"type":"integer"},"staffId":{"type":"string","nullable":true},"staffName":{"type":"string","nullable":true},"resourceId":{"type":"string","nullable":true},"resourceName":{"type":"string","nullable":true},"capacity":{"type":"integer","nullable":true,"description":"For class slots; null for 1:1."},"bookedCount":{"type":"integer","nullable":true}}},"CatalogEntry":{"type":"object","description":"Cached catalog entry — service, class description, staff member, or room. Refreshed every 6 hours.","properties":{"id":{"type":"string","description":"Platform-side ID."},"name":{"type":"string"},"durationMin":{"type":"integer","nullable":true},"capacity":{"type":"integer","nullable":true},"priceCents":{"type":"integer","nullable":true},"currency":{"type":"string","nullable":true},"active":{"type":"boolean"},"meta":{"type":"object","additionalProperties":true}}}}},"paths":{"/api/v1/":{"get":{"summary":"API catalog","description":"Lists the canonical endpoint set + supported platforms. No auth required — bots can discover the surface before they have a key.","tags":["Meta"],"security":[],"responses":{"200":{"description":"Catalog descriptor.","content":{"application/json":{"schema":{"type":"object","properties":{"service":{"type":"string","example":"AutoSync API"},"version":{"type":"string","example":"1"},"docs":{"type":"string","format":"uri"},"endpoints":{"type":"array","items":{"type":"object"}},"platforms":{"type":"array","items":{"type":"string","enum":["mindbody","booker","ghl"]}},"authNote":{"type":"string"}}}}}}}}},"/api/v1/openapi.json":{"get":{"summary":"OpenAPI 3.1 specification","description":"Returns this document. No auth required.","tags":["Meta"],"security":[],"responses":{"200":{"description":"OpenAPI spec as JSON.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/api/v1/ping":{"get":{"summary":"Auth smoke test","description":"Returns the resolved auth context — confirm a key works end-to-end before integrating. Counts toward your rate limit.","tags":["Meta"],"responses":{"200":{"description":"Auth context echo.","content":{"application/json":{"schema":{"type":"object","properties":{"pong":{"type":"boolean"},"location":{"type":"object","properties":{"ghlLocationId":{"type":"string"},"platform":{"type":"string","enum":["mindbody","booker","pow","ghl"]},"tier":{"type":"string","enum":["standard","pro"]},"marketplaceApp":{"type":"string","nullable":true}}},"key":{"type":"object","properties":{"name":{"type":"string"},"keyPrefix":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"createdAt":{"type":"string","format":"date-time"}}},"timestamp":{"type":"string","format":"date-time"}}}}}},"401":{"description":"Bearer token missing, malformed, revoked, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Authenticated but lacks permission. Common causes: scope missing on the API key (`auth.insufficient_scope`), API access not enabled for the location (`auth.api_access_disabled`), or platform mismatch (`platform.mismatch`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded. Retry after `Retry-After` seconds. Default ceiling is 60 req/min — contact support to raise it.","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until the limit resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"}},"X-RateLimit-Remaining":{"schema":{"type":"integer"}},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix epoch (seconds)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/{platform}/services":{"get":{"summary":"List services","description":"Returns the cached catalog of bookable services. Mindbody returns SessionTypes; Booker returns treatments; GHL returns calendars. Refreshed every 6 hours via the catalog-sync service.","tags":["Discovery"],"parameters":[{"name":"platform","in":"path","required":true,"schema":{"type":"string","enum":["mindbody","booker","ghl"]},"description":"Booking platform. Must match the platform configured on the location for the API key — mismatched calls 403 with `platform.mismatch`. POW is not exposed via the public API and returns 501."},{"name":"activeOnly","in":"query","required":false,"schema":{"type":"string","enum":["true","false"],"default":"true"},"description":"Default `true` — return only currently-active catalog entries. Pass `false` to include archived/deleted entries."}],"responses":{"200":{"description":"Service catalog.","content":{"application/json":{"schema":{"type":"object","properties":{"platform":{"type":"string"},"count":{"type":"integer"},"services":{"type":"array","items":{"$ref":"#/components/schemas/CatalogEntry"}}}}}}},"401":{"description":"Bearer token missing, malformed, revoked, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Authenticated but lacks permission. Common causes: scope missing on the API key (`auth.insufficient_scope`), API access not enabled for the location (`auth.api_access_disabled`), or platform mismatch (`platform.mismatch`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded. Retry after `Retry-After` seconds. Default ceiling is 60 req/min — contact support to raise it.","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until the limit resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"}},"X-RateLimit-Remaining":{"schema":{"type":"integer"}},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix epoch (seconds)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"501":{"description":"Operation not supported on this platform. Examples: classes on Booker, POW endpoints (POW is not exposed via the public API).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/{platform}/classes":{"get":{"summary":"List class descriptions (Mindbody only)","description":"Returns the cached catalog of Mindbody class descriptions (yoga / pilates / etc). Booker has no class concept and returns 501.","tags":["Discovery"],"parameters":[{"name":"platform","in":"path","required":true,"schema":{"type":"string","enum":["mindbody","booker","ghl"]},"description":"Booking platform. Must match the platform configured on the location for the API key — mismatched calls 403 with `platform.mismatch`. POW is not exposed via the public API and returns 501."},{"name":"activeOnly","in":"query","required":false,"schema":{"type":"string","enum":["true","false"],"default":"true"},"description":"Default `true` — return only currently-active catalog entries. Pass `false` to include archived/deleted entries."}],"responses":{"200":{"description":"Class description catalog.","content":{"application/json":{"schema":{"type":"object","properties":{"platform":{"type":"string"},"count":{"type":"integer"},"classes":{"type":"array","items":{"$ref":"#/components/schemas/CatalogEntry"}}}}}}},"401":{"description":"Bearer token missing, malformed, revoked, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Authenticated but lacks permission. Common causes: scope missing on the API key (`auth.insufficient_scope`), API access not enabled for the location (`auth.api_access_disabled`), or platform mismatch (`platform.mismatch`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded. Retry after `Retry-After` seconds. Default ceiling is 60 req/min — contact support to raise it.","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until the limit resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"}},"X-RateLimit-Remaining":{"schema":{"type":"integer"}},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix epoch (seconds)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"501":{"description":"Operation not supported on this platform. Examples: classes on Booker, POW endpoints (POW is not exposed via the public API).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/{platform}/staff":{"get":{"summary":"List staff members","description":"Returns the cached staff list for the location.","tags":["Discovery"],"parameters":[{"name":"platform","in":"path","required":true,"schema":{"type":"string","enum":["mindbody","booker","ghl"]},"description":"Booking platform. Must match the platform configured on the location for the API key — mismatched calls 403 with `platform.mismatch`. POW is not exposed via the public API and returns 501."},{"name":"activeOnly","in":"query","required":false,"schema":{"type":"string","enum":["true","false"],"default":"true"},"description":"Default `true` — return only currently-active catalog entries. Pass `false` to include archived/deleted entries."}],"responses":{"200":{"description":"Staff catalog.","content":{"application/json":{"schema":{"type":"object","properties":{"platform":{"type":"string"},"count":{"type":"integer"},"staff":{"type":"array","items":{"$ref":"#/components/schemas/CatalogEntry"}}}}}}},"401":{"description":"Bearer token missing, malformed, revoked, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Authenticated but lacks permission. Common causes: scope missing on the API key (`auth.insufficient_scope`), API access not enabled for the location (`auth.api_access_disabled`), or platform mismatch (`platform.mismatch`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded. Retry after `Retry-After` seconds. Default ceiling is 60 req/min — contact support to raise it.","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until the limit resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"}},"X-RateLimit-Remaining":{"schema":{"type":"integer"}},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix epoch (seconds)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/{platform}/resources":{"get":{"summary":"List rooms / resources","description":"Returns the cached room/resource catalog for the location.","tags":["Discovery"],"parameters":[{"name":"platform","in":"path","required":true,"schema":{"type":"string","enum":["mindbody","booker","ghl"]},"description":"Booking platform. Must match the platform configured on the location for the API key — mismatched calls 403 with `platform.mismatch`. POW is not exposed via the public API and returns 501."},{"name":"activeOnly","in":"query","required":false,"schema":{"type":"string","enum":["true","false"],"default":"true"},"description":"Default `true` — return only currently-active catalog entries. Pass `false` to include archived/deleted entries."}],"responses":{"200":{"description":"Resource catalog.","content":{"application/json":{"schema":{"type":"object","properties":{"platform":{"type":"string"},"count":{"type":"integer"},"resources":{"type":"array","items":{"$ref":"#/components/schemas/CatalogEntry"}}}}}}},"401":{"description":"Bearer token missing, malformed, revoked, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Authenticated but lacks permission. Common causes: scope missing on the API key (`auth.insufficient_scope`), API access not enabled for the location (`auth.api_access_disabled`), or platform mismatch (`platform.mismatch`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded. Retry after `Retry-After` seconds. Default ceiling is 60 req/min — contact support to raise it.","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until the limit resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"}},"X-RateLimit-Remaining":{"schema":{"type":"integer"}},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix epoch (seconds)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/{platform}/availability":{"get":{"summary":"List available time slots","description":"Returns free slots for a service / class / GHL calendar within a date window. Always live — no caching. Window must be ≤ 31 days.","tags":["Availability"],"parameters":[{"name":"platform","in":"path","required":true,"schema":{"type":"string","enum":["mindbody","booker","ghl"]},"description":"Booking platform. Must match the platform configured on the location for the API key — mismatched calls 403 with `platform.mismatch`. POW is not exposed via the public API and returns 501."},{"name":"serviceId","in":"query","schema":{"type":"string"},"description":"Required for Mindbody/Booker service availability. Mutually exclusive with `classDescriptionId` and `ghlCalendarId`."},{"name":"classDescriptionId","in":"query","schema":{"type":"string"},"description":"Mindbody only — class description ID (e.g. for yoga / pilates)."},{"name":"ghlCalendarId","in":"query","schema":{"type":"string"},"description":"Required when `platform=ghl`. GHL calendar ID from `/services`."},{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date-time"},"description":"Window start (ISO 8601 UTC)."},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date-time"},"description":"Window end (ISO 8601 UTC). Must be after `from` and ≤ 31 days later."},{"name":"staffId","in":"query","schema":{"type":"string"},"description":"Optional — restrict to slots with a specific staff member."},{"name":"resourceId","in":"query","schema":{"type":"string"},"description":"Optional — restrict to slots in a specific room."},{"name":"durationMin","in":"query","schema":{"type":"integer"},"description":"Optional override for slot duration. Defaults to the service definition."}],"responses":{"200":{"description":"Available slots.","content":{"application/json":{"schema":{"type":"object","properties":{"platform":{"type":"string"},"kind":{"type":"string","enum":["service","class"]},"externalId":{"type":"string"},"count":{"type":"integer"},"slots":{"type":"array","items":{"$ref":"#/components/schemas/AvailabilitySlot"}}}}}}},"400":{"description":"Validation failure — required field missing, body shape wrong, or platform mismatch (e.g. `classDescriptionId` on a Booker URL).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Bearer token missing, malformed, revoked, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Authenticated but lacks permission. Common causes: scope missing on the API key (`auth.insufficient_scope`), API access not enabled for the location (`auth.api_access_disabled`), or platform mismatch (`platform.mismatch`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded. Retry after `Retry-After` seconds. Default ceiling is 60 req/min — contact support to raise it.","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until the limit resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"}},"X-RateLimit-Remaining":{"schema":{"type":"integer"}},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix epoch (seconds)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"501":{"description":"Operation not supported on this platform. Examples: classes on Booker, POW endpoints (POW is not exposed via the public API).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/{platform}/bookings":{"post":{"summary":"Create a booking","description":"Books a slot for a contact. Resolves the contact across email/phone/IDs; creates one if `autoCreateContact: true` and no match found. Dual-writes to GHL (creates a corresponding calendar event).","tags":["Bookings"],"parameters":[{"name":"platform","in":"path","required":true,"schema":{"type":"string","enum":["mindbody","booker","ghl"]},"description":"Booking platform. Must match the platform configured on the location for the API key — mismatched calls 403 with `platform.mismatch`. POW is not exposed via the public API and returns 501."},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":255},"description":"Optional client-supplied key. The first request with a given key persists its response (24h TTL); subsequent requests with the same key + body return that stored response. A retry with the same key but a different body returns 409 (`idempotency.key_reused`). 5xx responses are not cached so retries can succeed against a healthy instance."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["contact","startDateTime"],"properties":{"serviceId":{"type":"string"},"classDescriptionId":{"type":"string","description":"Mindbody only."},"classInstanceId":{"type":"string","description":"Mindbody only. If omitted alongside `classDescriptionId`, AutoSync resolves the instance from the description + start datetime."},"ghlCalendarId":{"type":"string","description":"GHL only."},"contact":{"$ref":"#/components/schemas/Contact"},"startDateTime":{"type":"string","format":"date-time"},"endDateTime":{"type":"string","format":"date-time","description":"Optional. Defaults to `startDateTime + service.durationMin`."},"staffId":{"type":"string"},"resourceId":{"type":"string"},"notes":{"type":"string"},"autoCreateContact":{"type":"boolean","default":false,"description":"When true and contact lookup fails, creates a new contact from the supplied fields. When false (default), unknown contacts return 404 (`contact.not_found`)."}}},"examples":{"mindbodyService":{"summary":"Mindbody — 1:1 service booking by email","value":{"serviceId":"12345","contact":{"email":"jane@example.com","firstName":"Jane"},"startDateTime":"2026-05-15T14:00:00Z","staffId":"67890","autoCreateContact":true}},"ghlCalendar":{"summary":"GHL — calendar appointment by GHL contact ID","value":{"ghlCalendarId":"cal_abc123","contact":{"ghlContactId":"ctc_xyz789"},"startDateTime":"2026-05-15T14:00:00Z"}}}}}},"responses":{"201":{"description":"Booking created.","content":{"application/json":{"schema":{"type":"object","properties":{"platform":{"type":"string"},"booking":{"$ref":"#/components/schemas/Booking"},"contact":{"type":"object","properties":{"ghlContactId":{"type":"string","nullable":true},"platformContactId":{"type":"string","nullable":true},"source":{"type":"string","enum":["matched_by_id","matched_by_email","matched_by_phone","created"],"description":"How AutoSync resolved the contact."}}},"ghlEventId":{"type":"string","nullable":true,"description":"If GHL dual-write succeeded, the GHL calendar event ID. Null if dual-write was skipped or failed."},"partialSuccess":{"type":"boolean","description":"True iff the platform booking succeeded but a follow-up step (GHL dual-write, trigger fire) failed."}}}}}},"400":{"description":"Validation failure — required field missing, body shape wrong, or platform mismatch (e.g. `classDescriptionId` on a Booker URL).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Bearer token missing, malformed, revoked, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Authenticated but lacks permission. Common causes: scope missing on the API key (`auth.insufficient_scope`), API access not enabled for the location (`auth.api_access_disabled`), or platform mismatch (`platform.mismatch`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Contact not found and `autoCreateContact` was false.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict — `Idempotency-Key` reused with a different body, or platform rejected as a duplicate.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded. Retry after `Retry-After` seconds. Default ceiling is 60 req/min — contact support to raise it.","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until the limit resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"}},"X-RateLimit-Remaining":{"schema":{"type":"integer"}},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix epoch (seconds)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/{platform}/bookings/{id}":{"get":{"summary":"Get a booking","tags":["Bookings"],"parameters":[{"name":"platform","in":"path","required":true,"schema":{"type":"string","enum":["mindbody","booker","ghl"]},"description":"Booking platform. Must match the platform configured on the location for the API key — mismatched calls 403 with `platform.mismatch`. POW is not exposed via the public API and returns 501."},{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Platform-side booking ID. Mindbody AppointmentId, Booker booking ID, or GHL appointment ID."}],"responses":{"200":{"description":"Booking record.","content":{"application/json":{"schema":{"type":"object","properties":{"platform":{"type":"string"},"booking":{"$ref":"#/components/schemas/Booking"}}}}}},"401":{"description":"Bearer token missing, malformed, revoked, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Authenticated but lacks permission. Common causes: scope missing on the API key (`auth.insufficient_scope`), API access not enabled for the location (`auth.api_access_disabled`), or platform mismatch (`platform.mismatch`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Booking, contact, or other resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded. Retry after `Retry-After` seconds. Default ceiling is 60 req/min — contact support to raise it.","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until the limit resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"}},"X-RateLimit-Remaining":{"schema":{"type":"integer"}},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix epoch (seconds)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"summary":"Reschedule / update a booking","description":"PATCH semantics — only fields you include are updated. Empty bodies return 400 (`validation.empty_patch`). Updates dual-write to the corresponding GHL calendar event when one is mapped.","tags":["Bookings"],"parameters":[{"name":"platform","in":"path","required":true,"schema":{"type":"string","enum":["mindbody","booker","ghl"]},"description":"Booking platform. Must match the platform configured on the location for the API key — mismatched calls 403 with `platform.mismatch`. POW is not exposed via the public API and returns 501."},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":255},"description":"Optional client-supplied key. The first request with a given key persists its response (24h TTL); subsequent requests with the same key + body return that stored response. A retry with the same key but a different body returns 409 (`idempotency.key_reused`). 5xx responses are not cached so retries can succeed against a healthy instance."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"startDateTime":{"type":"string","format":"date-time"},"endDateTime":{"type":"string","format":"date-time"},"staffId":{"type":"string"},"resourceId":{"type":"string"},"notes":{"type":"string"}}}}}},"responses":{"200":{"description":"Updated booking.","content":{"application/json":{"schema":{"type":"object","properties":{"platform":{"type":"string"},"booking":{"$ref":"#/components/schemas/Booking"},"ghlEventId":{"type":"string","nullable":true}}}}}},"400":{"description":"Validation failure — required field missing, body shape wrong, or platform mismatch (e.g. `classDescriptionId` on a Booker URL).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Bearer token missing, malformed, revoked, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Authenticated but lacks permission. Common causes: scope missing on the API key (`auth.insufficient_scope`), API access not enabled for the location (`auth.api_access_disabled`), or platform mismatch (`platform.mismatch`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Booking, contact, or other resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"`Idempotency-Key` reused with a different body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded. Retry after `Retry-After` seconds. Default ceiling is 60 req/min — contact support to raise it.","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until the limit resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"}},"X-RateLimit-Remaining":{"schema":{"type":"integer"}},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix epoch (seconds)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"summary":"Cancel a booking","description":"Cancels the booking on the platform and updates the linked GHL event status to `cancelled`. Late-cancel detection is forward-compat: pass `?lateCancel=true` to acknowledge fees may apply (the parameter is accepted but doesn't alter platform behavior in v1).","tags":["Bookings"],"parameters":[{"name":"platform","in":"path","required":true,"schema":{"type":"string","enum":["mindbody","booker","ghl"]},"description":"Booking platform. Must match the platform configured on the location for the API key — mismatched calls 403 with `platform.mismatch`. POW is not exposed via the public API and returns 501."},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"lateCancel","in":"query","schema":{"type":"string","enum":["true","false"]},"description":"Forward-compat flag for explicit late-cancel acknowledgement. Reserved for the late-cancel policy work in a future release."},{"name":"reason","in":"query","schema":{"type":"string"},"description":"Optional cancellation reason. Stored on the platform record where supported."},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":255},"description":"Optional client-supplied key. The first request with a given key persists its response (24h TTL); subsequent requests with the same key + body return that stored response. A retry with the same key but a different body returns 409 (`idempotency.key_reused`). 5xx responses are not cached so retries can succeed against a healthy instance."}],"responses":{"204":{"description":"Booking cancelled."},"401":{"description":"Bearer token missing, malformed, revoked, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Authenticated but lacks permission. Common causes: scope missing on the API key (`auth.insufficient_scope`), API access not enabled for the location (`auth.api_access_disabled`), or platform mismatch (`platform.mismatch`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Booking, contact, or other resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded. Retry after `Retry-After` seconds. Default ceiling is 60 req/min — contact support to raise it.","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until the limit resets."},"X-RateLimit-Limit":{"schema":{"type":"integer"}},"X-RateLimit-Remaining":{"schema":{"type":"integer"}},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix epoch (seconds)."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}}}