{"openapi":"3.1.0","info":{"title":"Astrocal API","version":"1.0.0","description":"Scheduling API for AI agents and developers"},"security":[{"BearerAuth":[]}],"tags":[{"name":"API Keys"},{"name":"Activity"},{"name":"Availability"},{"name":"Booking Pages"},{"name":"Bookings"},{"name":"Branding"},{"name":"Calendars"},{"name":"Conferencing"},{"name":"Event Type Hosts"},{"name":"Event Types"},{"name":"Organizations"},{"name":"Payments"},{"name":"Stripe"},{"name":"Subscription"},{"name":"Support"},{"name":"Team"},{"name":"Test Data"},{"name":"Usage"},{"name":"Waitlist"},{"name":"Webhooks"}],"servers":[{"url":"https://api.astrocal.dev","description":"Production"},{"url":"https://api.astrocal.dev","description":"Local development"}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","description":"API key authentication. Use your API key as the bearer token."}},"schemas":{},"parameters":{}},"paths":{"/v1/organizations":{"post":{"tags":["Organizations"],"summary":"Create organization","description":"Creates a new organization linked to a Supabase Auth user. Service key authentication required. Called by the dashboard during signup.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":255},"slug":{"type":"string","minLength":1,"maxLength":63,"pattern":"^[a-z0-9-]+$"},"owner_user_id":{"type":"string","minLength":1},"owner_email":{"type":"string","format":"email"},"onboarding_persona":{"type":"string","enum":["developer","individual","team_lead"]},"timezone":{"type":"string","maxLength":64}},"required":["name","owner_user_id"]}}}},"responses":{"201":{"description":"Organization created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"slug":{"type":"string"},"owner_user_id":{"type":"string","nullable":true},"default_calendar_connection_id":{"type":"string","nullable":true,"format":"uuid"},"onboarding_completed_at":{"type":"string","nullable":true},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","name","slug","owner_user_id","default_calendar_connection_id","onboarding_completed_at","created_at","updated_at"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"401":{"description":"Unauthorized — invalid or missing service key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"409":{"description":"Conflict — slug or owner_user_id already exists","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}},"get":{"tags":["Organizations"],"summary":"Get organization by owner","description":"Retrieves an organization by its owner's Supabase Auth user ID. Service key authentication required. Used by the dashboard to resolve the current user's org.","parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"owner_user_id","in":"query"}],"responses":{"200":{"description":"Organization found","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"slug":{"type":"string"},"owner_user_id":{"type":"string","nullable":true},"default_calendar_connection_id":{"type":"string","nullable":true,"format":"uuid"},"onboarding_completed_at":{"type":"string","nullable":true},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","name","slug","owner_user_id","default_calendar_connection_id","onboarding_completed_at","created_at","updated_at"]}}}},"401":{"description":"Unauthorized — invalid or missing service key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"No organization found for this user","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/organizations/{id}":{"patch":{"tags":["Organizations"],"summary":"Update organization","description":"Updates an organization's name, slug or default calendar. Send `complete_onboarding: true` to mark the first-login onboarding flow as done — the API sets the timestamp itself and repeat calls keep the first one. `mark_booking_link_shared`, `mark_availability_confirmed` and `dismiss_checklist` work the same way for the dashboard getting-started checklist, as does `mark_first_booking_celebrated` for the first-booking celebration. Service key authentication required. Called by the dashboard settings page.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":255},"slug":{"type":"string","minLength":1,"maxLength":63,"pattern":"^[a-z0-9-]+$"},"default_calendar_connection_id":{"type":"string","nullable":true,"format":"uuid"},"complete_onboarding":{"type":"boolean"},"mark_booking_link_shared":{"type":"boolean"},"mark_availability_confirmed":{"type":"boolean"},"dismiss_checklist":{"type":"boolean"},"mark_first_booking_celebrated":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Organization updated","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"slug":{"type":"string"},"owner_user_id":{"type":"string","nullable":true},"default_calendar_connection_id":{"type":"string","nullable":true,"format":"uuid"},"onboarding_completed_at":{"type":"string","nullable":true},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","name","slug","owner_user_id","default_calendar_connection_id","onboarding_completed_at","created_at","updated_at"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"401":{"description":"Unauthorized — invalid or missing service key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Organization not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"409":{"description":"Conflict — slug already exists","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/me/organizations":{"get":{"tags":["Organizations"],"summary":"List user organizations","description":"Returns all organizations the authenticated user belongs to, along with their role in each. Requires service key auth and X-User-Id header.","parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"x-user-id","in":"header"}],"responses":{"200":{"description":"List of organizations the user belongs to","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"slug":{"type":"string"},"role":{"type":"string","enum":["owner","admin","member","viewer"]},"default_calendar_connection_id":{"type":"string","nullable":true,"format":"uuid"},"onboarding_completed_at":{"type":"string","nullable":true},"created_at":{"type":"string"}},"required":["id","name","slug","role","default_calendar_connection_id","onboarding_completed_at","created_at"]}}},"required":["data"]}}}},"400":{"description":"Missing X-User-Id header","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"401":{"description":"Unauthorized — invalid or missing service key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/public/booking-page/{orgSlug}/{eventSlug}":{"get":{"tags":["Booking Pages"],"summary":"Get booking page data","description":"Public endpoint — no authentication required. Resolves org and event type by their URL slugs and returns the data needed to render a booking page, including branding settings. Returns { available: false } when the event type is inactive, booking page is disabled, or the org is suspended.","security":[],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":63,"pattern":"^[a-z0-9-]+$"},"required":true,"name":"orgSlug","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":63,"pattern":"^[a-z0-9-]+$"},"required":true,"name":"eventSlug","in":"path"}],"responses":{"200":{"description":"Booking page data (available or unavailable)","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"available":{"type":"boolean","enum":[true]},"org_slug":{"type":"string"},"event_type":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"slug":{"type":"string"},"description":{"type":"string","nullable":true},"duration_minutes":{"type":"integer"},"color":{"type":"string"},"timezone":{"type":"string"},"price_amount":{"type":"integer","nullable":true},"price_currency":{"type":"string"},"duration_options":{"type":"array","nullable":true,"items":{"type":"integer"}},"max_attendees":{"type":"integer"},"waitlist_enabled":{"type":"boolean"},"waitlist_max":{"type":"integer"},"waitlist_ttl_days":{"type":"integer"},"conferencing_provider":{"type":"string","nullable":true,"enum":["zoom","google_meet","microsoft_teams","custom","in_person",null]},"location":{"type":"string","nullable":true},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","title","slug","description","duration_minutes","color","timezone","price_amount","price_currency","duration_options","max_attendees","waitlist_enabled","waitlist_max","waitlist_ttl_days","conferencing_provider","location","created_at","updated_at"]},"branding":{"type":"object","properties":{"logo_url":{"type":"string","nullable":true},"logo_alt_text":{"type":"string","nullable":true},"remove_branding":{"type":"boolean"},"listing_page_enabled":{"type":"boolean"}},"required":["logo_url","logo_alt_text","remove_branding","listing_page_enabled"]}},"required":["available","org_slug","event_type","branding"]},{"type":"object","properties":{"available":{"type":"boolean","enum":[false]},"branding":{"type":"object","properties":{"logo_url":{"type":"string","nullable":true},"logo_alt_text":{"type":"string","nullable":true},"remove_branding":{"type":"boolean"},"listing_page_enabled":{"type":"boolean"}},"required":["logo_url","logo_alt_text","remove_branding","listing_page_enabled"]}},"required":["available","branding"]}]}}}},"404":{"description":"Org or event type not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/public/booking-page/{orgSlug}":{"get":{"tags":["Booking Pages"],"summary":"Get org booking page listing","description":"Public endpoint — no authentication required. Returns the org's branding and a list of all public event types (active, booking page enabled, non-test), ordered by title. Returns { available: false } when the org is suspended.","security":[],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":63,"pattern":"^[a-z0-9-]+$"},"required":true,"name":"orgSlug","in":"path"}],"responses":{"200":{"description":"Org booking page data (available or unavailable)","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"available":{"type":"boolean","enum":[true]},"org_name":{"type":"string"},"org_slug":{"type":"string"},"event_types":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"slug":{"type":"string"},"description":{"type":"string","nullable":true},"duration_minutes":{"type":"integer"},"color":{"type":"string"},"price_amount":{"type":"integer","nullable":true},"price_currency":{"type":"string"},"conferencing_provider":{"type":"string","nullable":true,"enum":["zoom","google_meet","microsoft_teams","custom","in_person",null]},"location":{"type":"string","nullable":true}},"required":["id","title","slug","description","duration_minutes","color","price_amount","price_currency","conferencing_provider","location"]}},"branding":{"type":"object","properties":{"logo_url":{"type":"string","nullable":true},"logo_alt_text":{"type":"string","nullable":true},"remove_branding":{"type":"boolean"},"listing_page_enabled":{"type":"boolean"}},"required":["logo_url","logo_alt_text","remove_branding","listing_page_enabled"]}},"required":["available","org_name","org_slug","event_types","branding"]},{"type":"object","properties":{"available":{"type":"boolean","enum":[false]},"branding":{"type":"object","properties":{"logo_url":{"type":"string","nullable":true},"logo_alt_text":{"type":"string","nullable":true},"remove_branding":{"type":"boolean"},"listing_page_enabled":{"type":"boolean"}},"required":["logo_url","logo_alt_text","remove_branding","listing_page_enabled"]}},"required":["available","branding"]}]}}}},"404":{"description":"Org not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/public/event-types/{id}":{"get":{"tags":["Event Types"],"summary":"Get a public event type","description":"Public endpoint — no authentication required. Returns event type details for embedding widgets. Only returns active, non-test event types. `custom_meeting_url` is omitted — a booker receives the link after booking. Authenticated callers should use `GET /v1/event-types/{id}`, which is scoped to their organization and includes test-mode event types.","security":[],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Event type found","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organization_id":{"type":"string","format":"uuid"},"title":{"type":"string"},"slug":{"type":"string"},"description":{"type":"string","nullable":true},"duration_minutes":{"type":"integer"},"buffer_before_minutes":{"type":"integer"},"buffer_after_minutes":{"type":"integer"},"minimum_notice_minutes":{"type":"integer"},"color":{"type":"string"},"timezone":{"type":"string"},"active":{"type":"boolean"},"is_test":{"type":"boolean"},"price_amount":{"type":"integer","nullable":true},"price_currency":{"type":"string"},"reminder_intervals":{"type":"array","items":{"type":"integer"}},"duration_options":{"type":"array","nullable":true,"items":{"type":"integer"}},"max_attendees":{"type":"integer"},"assignment_strategy":{"type":"string","enum":["none","round_robin","least_recent","least_bookings","availability_weighted"]},"booking_cap":{"type":"integer","nullable":true},"booking_cap_period":{"type":"string","nullable":true,"enum":["daily","weekly","monthly","total",null]},"bookings_remaining":{"type":"integer","nullable":true},"booking_count":{"type":"integer"},"waitlist_enabled":{"type":"boolean"},"waitlist_max":{"type":"integer"},"waitlist_ttl_days":{"type":"integer"},"booking_page_enabled":{"type":"boolean"},"conferencing_provider":{"type":"string","nullable":true},"location":{"type":"string","nullable":true},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","organization_id","title","slug","description","duration_minutes","buffer_before_minutes","buffer_after_minutes","minimum_notice_minutes","color","timezone","active","is_test","price_amount","price_currency","reminder_intervals","duration_options","max_attendees","assignment_strategy","booking_cap","booking_cap_period","bookings_remaining","booking_count","waitlist_enabled","waitlist_max","waitlist_ttl_days","booking_page_enabled","conferencing_provider","location","created_at","updated_at"]}}}},"404":{"description":"Event type not found, not active, or in test mode","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/event-types":{"post":{"tags":["Event Types"],"summary":"Create an event type","description":"Creates a new event type for the authenticated organization. A unique slug is required and will be used in booking page URLs. Returns 409 if the slug is already taken.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":255},"slug":{"type":"string","minLength":1,"maxLength":63,"pattern":"^[a-z0-9-]+$"},"description":{"type":"string","maxLength":1000},"duration_minutes":{"type":"integer","minimum":5,"maximum":480},"buffer_before_minutes":{"type":"integer","minimum":0,"maximum":120,"default":0},"buffer_after_minutes":{"type":"integer","minimum":0,"maximum":120,"default":0},"minimum_notice_minutes":{"type":"integer","minimum":0,"maximum":43200,"default":0},"color":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$","default":"#3b82f6"},"price_amount":{"type":"integer","nullable":true,"minimum":100},"price_currency":{"type":"string","minLength":3,"maxLength":3},"reminder_intervals":{"type":"array","items":{"type":"integer","minimum":1,"maximum":10080},"maxItems":5},"duration_options":{"type":"array","nullable":true,"items":{"type":"integer","minimum":5,"maximum":480},"maxItems":5},"max_attendees":{"type":"integer","minimum":1,"maximum":100,"default":1},"booking_cap":{"type":"integer","nullable":true,"minimum":1,"maximum":10000},"booking_cap_period":{"type":"string","nullable":true,"enum":["daily","weekly","monthly","total",null]},"waitlist_enabled":{"type":"boolean","default":false},"waitlist_max":{"type":"integer","minimum":1,"maximum":500,"default":50},"waitlist_ttl_days":{"type":"integer","minimum":1,"maximum":30,"default":7},"booking_page_enabled":{"type":"boolean","default":false},"conferencing_provider":{"type":"string","nullable":true,"enum":["zoom","google_meet","microsoft_teams","custom","in_person",null]},"custom_meeting_url":{"type":"string","nullable":true,"format":"uri"},"location":{"type":"string","nullable":true,"maxLength":500}},"required":["title","slug","duration_minutes"]}}}},"responses":{"201":{"description":"Event type created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organization_id":{"type":"string","format":"uuid"},"title":{"type":"string"},"slug":{"type":"string"},"description":{"type":"string","nullable":true},"duration_minutes":{"type":"integer"},"buffer_before_minutes":{"type":"integer"},"buffer_after_minutes":{"type":"integer"},"minimum_notice_minutes":{"type":"integer"},"color":{"type":"string"},"timezone":{"type":"string"},"active":{"type":"boolean"},"is_test":{"type":"boolean"},"price_amount":{"type":"integer","nullable":true},"price_currency":{"type":"string"},"reminder_intervals":{"type":"array","items":{"type":"integer"}},"duration_options":{"type":"array","nullable":true,"items":{"type":"integer"}},"max_attendees":{"type":"integer"},"assignment_strategy":{"type":"string","enum":["none","round_robin","least_recent","least_bookings","availability_weighted"]},"booking_cap":{"type":"integer","nullable":true},"booking_cap_period":{"type":"string","nullable":true,"enum":["daily","weekly","monthly","total",null]},"bookings_remaining":{"type":"integer","nullable":true},"booking_count":{"type":"integer"},"waitlist_enabled":{"type":"boolean"},"waitlist_max":{"type":"integer"},"waitlist_ttl_days":{"type":"integer"},"booking_page_enabled":{"type":"boolean"},"conferencing_provider":{"type":"string","nullable":true},"custom_meeting_url":{"type":"string","nullable":true},"location":{"type":"string","nullable":true},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","organization_id","title","slug","description","duration_minutes","buffer_before_minutes","buffer_after_minutes","minimum_notice_minutes","color","timezone","active","is_test","price_amount","price_currency","reminder_intervals","duration_options","max_attendees","assignment_strategy","booking_cap","booking_cap_period","bookings_remaining","booking_count","waitlist_enabled","waitlist_max","waitlist_ttl_days","booking_page_enabled","conferencing_provider","custom_meeting_url","location","created_at","updated_at"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"409":{"description":"Conflict — slug already exists","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}},"get":{"tags":["Event Types"],"summary":"List event types","description":"Returns a paginated list of event types for the authenticated organization. Pass `active=true` to filter to active event types only, or `active=false` for inactive. Omit the parameter to return all event types.","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","format":"uuid"},"required":false,"name":"starting_after","in":"query"},{"schema":{"type":"boolean","nullable":true},"required":false,"name":"active","in":"query"}],"responses":{"200":{"description":"Paginated list of event types","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organization_id":{"type":"string","format":"uuid"},"title":{"type":"string"},"slug":{"type":"string"},"description":{"type":"string","nullable":true},"duration_minutes":{"type":"integer"},"buffer_before_minutes":{"type":"integer"},"buffer_after_minutes":{"type":"integer"},"minimum_notice_minutes":{"type":"integer"},"color":{"type":"string"},"timezone":{"type":"string"},"active":{"type":"boolean"},"is_test":{"type":"boolean"},"price_amount":{"type":"integer","nullable":true},"price_currency":{"type":"string"},"reminder_intervals":{"type":"array","items":{"type":"integer"}},"duration_options":{"type":"array","nullable":true,"items":{"type":"integer"}},"max_attendees":{"type":"integer"},"assignment_strategy":{"type":"string","enum":["none","round_robin","least_recent","least_bookings","availability_weighted"]},"booking_cap":{"type":"integer","nullable":true},"booking_cap_period":{"type":"string","nullable":true,"enum":["daily","weekly","monthly","total",null]},"bookings_remaining":{"type":"integer","nullable":true},"booking_count":{"type":"integer"},"waitlist_enabled":{"type":"boolean"},"waitlist_max":{"type":"integer"},"waitlist_ttl_days":{"type":"integer"},"booking_page_enabled":{"type":"boolean"},"conferencing_provider":{"type":"string","nullable":true},"custom_meeting_url":{"type":"string","nullable":true},"location":{"type":"string","nullable":true},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","organization_id","title","slug","description","duration_minutes","buffer_before_minutes","buffer_after_minutes","minimum_notice_minutes","color","timezone","active","is_test","price_amount","price_currency","reminder_intervals","duration_options","max_attendees","assignment_strategy","booking_cap","booking_cap_period","bookings_remaining","booking_count","waitlist_enabled","waitlist_max","waitlist_ttl_days","booking_page_enabled","conferencing_provider","custom_meeting_url","location","created_at","updated_at"]}},"has_more":{"type":"boolean"}},"required":["data","has_more"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/event-types/{id}":{"get":{"tags":["Event Types"],"summary":"Get an event type","description":"Returns a single event type by ID. Includes all configuration: duration, pricing, booking caps, and host assignment strategy.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Event type found","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organization_id":{"type":"string","format":"uuid"},"title":{"type":"string"},"slug":{"type":"string"},"description":{"type":"string","nullable":true},"duration_minutes":{"type":"integer"},"buffer_before_minutes":{"type":"integer"},"buffer_after_minutes":{"type":"integer"},"minimum_notice_minutes":{"type":"integer"},"color":{"type":"string"},"timezone":{"type":"string"},"active":{"type":"boolean"},"is_test":{"type":"boolean"},"price_amount":{"type":"integer","nullable":true},"price_currency":{"type":"string"},"reminder_intervals":{"type":"array","items":{"type":"integer"}},"duration_options":{"type":"array","nullable":true,"items":{"type":"integer"}},"max_attendees":{"type":"integer"},"assignment_strategy":{"type":"string","enum":["none","round_robin","least_recent","least_bookings","availability_weighted"]},"booking_cap":{"type":"integer","nullable":true},"booking_cap_period":{"type":"string","nullable":true,"enum":["daily","weekly","monthly","total",null]},"bookings_remaining":{"type":"integer","nullable":true},"booking_count":{"type":"integer"},"waitlist_enabled":{"type":"boolean"},"waitlist_max":{"type":"integer"},"waitlist_ttl_days":{"type":"integer"},"booking_page_enabled":{"type":"boolean"},"conferencing_provider":{"type":"string","nullable":true},"custom_meeting_url":{"type":"string","nullable":true},"location":{"type":"string","nullable":true},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","organization_id","title","slug","description","duration_minutes","buffer_before_minutes","buffer_after_minutes","minimum_notice_minutes","color","timezone","active","is_test","price_amount","price_currency","reminder_intervals","duration_options","max_attendees","assignment_strategy","booking_cap","booking_cap_period","bookings_remaining","booking_count","waitlist_enabled","waitlist_max","waitlist_ttl_days","booking_page_enabled","conferencing_provider","custom_meeting_url","location","created_at","updated_at"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}},"patch":{"tags":["Event Types"],"summary":"Update an event type","description":"Partially updates an event type. Only provided fields are changed. Returns the full updated event type. Returns 409 if the new slug conflicts with an existing one.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":255},"description":{"type":"string","maxLength":1000},"duration_minutes":{"type":"integer","minimum":5,"maximum":480},"buffer_before_minutes":{"type":"integer","minimum":0,"maximum":120},"buffer_after_minutes":{"type":"integer","minimum":0,"maximum":120},"minimum_notice_minutes":{"type":"integer","minimum":0,"maximum":43200},"color":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"active":{"type":"boolean"},"assignment_strategy":{"type":"string","enum":["none","round_robin","least_recent","least_bookings","availability_weighted"]},"price_amount":{"type":"integer","nullable":true,"minimum":100},"price_currency":{"type":"string","minLength":3,"maxLength":3},"reminder_intervals":{"type":"array","items":{"type":"integer","minimum":1,"maximum":10080},"maxItems":5},"duration_options":{"type":"array","nullable":true,"items":{"type":"integer","minimum":5,"maximum":480},"maxItems":5},"max_attendees":{"type":"integer","minimum":1,"maximum":100},"booking_cap":{"type":"integer","nullable":true,"minimum":1,"maximum":10000},"booking_cap_period":{"type":"string","nullable":true,"enum":["daily","weekly","monthly","total",null]},"waitlist_enabled":{"type":"boolean"},"waitlist_max":{"type":"integer","minimum":1,"maximum":500},"waitlist_ttl_days":{"type":"integer","minimum":1,"maximum":30},"booking_page_enabled":{"type":"boolean"},"conferencing_provider":{"type":"string","nullable":true,"enum":["zoom","google_meet","microsoft_teams","custom","in_person",null]},"custom_meeting_url":{"type":"string","nullable":true,"format":"uri"},"location":{"type":"string","nullable":true,"maxLength":500}}}}}},"responses":{"200":{"description":"Event type updated","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organization_id":{"type":"string","format":"uuid"},"title":{"type":"string"},"slug":{"type":"string"},"description":{"type":"string","nullable":true},"duration_minutes":{"type":"integer"},"buffer_before_minutes":{"type":"integer"},"buffer_after_minutes":{"type":"integer"},"minimum_notice_minutes":{"type":"integer"},"color":{"type":"string"},"timezone":{"type":"string"},"active":{"type":"boolean"},"is_test":{"type":"boolean"},"price_amount":{"type":"integer","nullable":true},"price_currency":{"type":"string"},"reminder_intervals":{"type":"array","items":{"type":"integer"}},"duration_options":{"type":"array","nullable":true,"items":{"type":"integer"}},"max_attendees":{"type":"integer"},"assignment_strategy":{"type":"string","enum":["none","round_robin","least_recent","least_bookings","availability_weighted"]},"booking_cap":{"type":"integer","nullable":true},"booking_cap_period":{"type":"string","nullable":true,"enum":["daily","weekly","monthly","total",null]},"bookings_remaining":{"type":"integer","nullable":true},"booking_count":{"type":"integer"},"waitlist_enabled":{"type":"boolean"},"waitlist_max":{"type":"integer"},"waitlist_ttl_days":{"type":"integer"},"booking_page_enabled":{"type":"boolean"},"conferencing_provider":{"type":"string","nullable":true},"custom_meeting_url":{"type":"string","nullable":true},"location":{"type":"string","nullable":true},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","organization_id","title","slug","description","duration_minutes","buffer_before_minutes","buffer_after_minutes","minimum_notice_minutes","color","timezone","active","is_test","price_amount","price_currency","reminder_intervals","duration_options","max_attendees","assignment_strategy","booking_cap","booking_cap_period","bookings_remaining","booking_count","waitlist_enabled","waitlist_max","waitlist_ttl_days","booking_page_enabled","conferencing_provider","custom_meeting_url","location","created_at","updated_at"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}},"delete":{"tags":["Event Types"],"summary":"Delete an event type","description":"Soft-deletes an event type. The event type is marked as deleted and excluded from listings, but existing bookings are preserved.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Event type deleted (soft delete)","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"deleted":{"type":"boolean"}},"required":["id","deleted"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/event-types/{id}/availability":{"put":{"tags":["Availability"],"summary":"Set availability rules for an event type","description":"Replaces all availability rules for an event type. Rules define recurring weekly time windows when the event type can be booked. Pass an empty `rules` array to clear all availability.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"rules":{"type":"array","items":{"type":"object","properties":{"day_of_week":{"type":"integer","minimum":0,"maximum":6},"start_time":{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d$"},"end_time":{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d$"}},"required":["day_of_week","start_time","end_time"]}},"timezone":{"type":"string","minLength":1}},"required":["rules","timezone"]}}}},"responses":{"200":{"description":"Availability rules updated","content":{"application/json":{"schema":{"type":"object","properties":{"event_type_id":{"type":"string","format":"uuid"},"timezone":{"type":"string"},"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"event_type_id":{"type":"string","format":"uuid"},"day_of_week":{"type":"integer"},"start_time":{"type":"string"},"end_time":{"type":"string"},"created_at":{"type":"string"}},"required":["id","event_type_id","day_of_week","start_time","end_time","created_at"]}}},"required":["event_type_id","timezone","rules"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}},"get":{"tags":["Availability"],"summary":"Get availability rules for an event type","description":"Returns the current availability rules and timezone for an event type. Use the public availability endpoint to query computed bookable slots.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Availability rules","content":{"application/json":{"schema":{"type":"object","properties":{"event_type_id":{"type":"string","format":"uuid"},"timezone":{"type":"string"},"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"event_type_id":{"type":"string","format":"uuid"},"day_of_week":{"type":"integer"},"start_time":{"type":"string"},"end_time":{"type":"string"},"created_at":{"type":"string"}},"required":["id","event_type_id","day_of_week","start_time","end_time","created_at"]}}},"required":["event_type_id","timezone","rules"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/event-types/{id}/hosts":{"post":{"tags":["Event Type Hosts"],"summary":"Add a host to an event type","description":"Adds a team member as a host for round-robin assignment.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":"string","format":"uuid"},"priority":{"type":"integer","minimum":1,"maximum":100,"default":50}},"required":["member_id"]}}}},"responses":{"201":{"description":"Host added","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"event_type_id":{"type":"string","format":"uuid"},"member_id":{"type":"string","format":"uuid"},"member_email":{"type":"string","nullable":true},"priority":{"type":"integer"},"total_bookings":{"type":"integer"},"last_booking_at":{"type":"string","nullable":true},"has_calendar":{"type":"boolean"},"created_at":{"type":"string"}},"required":["id","event_type_id","member_id","member_email","priority","total_bookings","last_booking_at","has_calendar","created_at"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Event type not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"409":{"description":"Conflict — member is already a host","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}},"get":{"tags":["Event Type Hosts"],"summary":"List hosts for an event type","description":"Returns all hosts with booking stats and calendar connection status.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Hosts list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"event_type_id":{"type":"string","format":"uuid"},"member_id":{"type":"string","format":"uuid"},"member_email":{"type":"string","nullable":true},"priority":{"type":"integer"},"total_bookings":{"type":"integer"},"last_booking_at":{"type":"string","nullable":true},"has_calendar":{"type":"boolean"},"created_at":{"type":"string"}},"required":["id","event_type_id","member_id","member_email","priority","total_bookings","last_booking_at","has_calendar","created_at"]}}},"required":["data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Event type not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/event-types/{id}/hosts/{member_id}":{"delete":{"tags":["Event Type Hosts"],"summary":"Remove a host from an event type","description":"Removes a team member from the event type's host rotation. Future bookings will no longer be assigned to this host.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"member_id","in":"path"}],"responses":{"204":{"description":"Host removed"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Event type or host not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/availability":{"get":{"tags":["Availability"],"summary":"Query available booking slots","description":"Public endpoint — no authentication required. Returns available time slots for an event type within a date range.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"event_type_id","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"required":true,"name":"start","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"required":true,"name":"end","in":"query"},{"schema":{"type":"string","minLength":1},"required":true,"name":"timezone","in":"query"},{"schema":{"type":"string","format":"uuid"},"required":false,"name":"host_id","in":"query"},{"schema":{"type":"integer","minimum":5,"maximum":480},"required":false,"name":"duration","in":"query"}],"responses":{"200":{"description":"Available slots","content":{"application/json":{"schema":{"type":"object","properties":{"event_type_id":{"type":"string","format":"uuid"},"timezone":{"type":"string"},"start":{"type":"string"},"end":{"type":"string"},"slots":{"type":"array","items":{"type":"object","properties":{"start_time":{"type":"string"},"end_time":{"type":"string"},"spots_remaining":{"type":"integer"},"total_capacity":{"type":"integer"},"available":{"type":"boolean"},"waitlist_available":{"type":"boolean"},"waitlist_position":{"type":"integer"}},"required":["start_time","end_time"]}},"capped":{"type":"boolean"},"waitlist_available":{"type":"boolean"},"waitlist_position":{"type":"integer"}},"required":["event_type_id","timezone","start","end","slots"]}}}},"400":{"description":"Bad request (invalid timezone or date range)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Event type not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/calendars/google/callback":{"get":{"tags":["Calendars"],"summary":"Google OAuth callback","description":"Handles the OAuth callback from Google. Exchanges the authorization code for tokens, stores the encrypted connection, and redirects.","parameters":[{"schema":{"type":"string"},"required":false,"name":"code","in":"query"},{"schema":{"type":"string"},"required":false,"name":"state","in":"query"},{"schema":{"type":"string"},"required":false,"name":"error","in":"query"}],"responses":{"302":{"description":"Redirect to success or error page, including when rate limited"},"400":{"description":"Invalid state token","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/calendars/microsoft/callback":{"get":{"tags":["Calendars"],"summary":"Microsoft OAuth callback","description":"Handles the OAuth callback from Microsoft. Exchanges the authorization code for tokens with PKCE, stores the encrypted connection, and redirects.","parameters":[{"schema":{"type":"string"},"required":false,"name":"code","in":"query"},{"schema":{"type":"string"},"required":false,"name":"state","in":"query"},{"schema":{"type":"string"},"required":false,"name":"error","in":"query"},{"schema":{"type":"string"},"required":false,"name":"error_description","in":"query"}],"responses":{"302":{"description":"Redirect to success or error page, including when rate limited"},"400":{"description":"Invalid state token","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/calendars/google/connect":{"get":{"tags":["Calendars"],"summary":"Initiate Google Calendar OAuth flow","description":"Returns a Google OAuth authorization URL. This flow must be initiated from the Astrocal dashboard, which binds the authorization request to your browser session.","parameters":[{"schema":{"type":"string","format":"uuid","description":"The org member whose calendar is being connected"},"required":true,"description":"The org member whose calendar is being connected","name":"member_id","in":"query"},{"schema":{"type":"string","format":"uri"},"required":false,"name":"redirect_uri","in":"query"}],"responses":{"200":{"description":"Authorization URL generated","content":{"application/json":{"schema":{"type":"object","properties":{"authorization_url":{"type":"string"}},"required":["authorization_url"]}}}},"400":{"description":"Flow was not initiated from the Astrocal dashboard","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Member not found or does not belong to org","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/calendars/microsoft/connect":{"get":{"tags":["Calendars"],"summary":"Initiate Microsoft Calendar OAuth flow","description":"Returns a Microsoft OAuth authorization URL with PKCE. This flow must be initiated from the Astrocal dashboard, which binds the authorization request to your browser session.","parameters":[{"schema":{"type":"string","format":"uuid","description":"The org member whose calendar is being connected"},"required":true,"description":"The org member whose calendar is being connected","name":"member_id","in":"query"},{"schema":{"type":"string","format":"uri"},"required":false,"name":"redirect_uri","in":"query"}],"responses":{"200":{"description":"Authorization URL generated","content":{"application/json":{"schema":{"type":"object","properties":{"authorization_url":{"type":"string"}},"required":["authorization_url"]}}}},"400":{"description":"Flow was not initiated from the Astrocal dashboard","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Member not found or does not belong to org","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/calendars/caldav/connect":{"post":{"tags":["Calendars"],"summary":"Connect a CalDAV calendar","description":"Connects a CalDAV calendar (Apple iCloud, Fastmail, Nextcloud, or custom) using password-based authentication. Verifies credentials before storing.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":"string","format":"uuid","description":"The org member whose calendar is being connected"},"preset":{"type":"string","enum":["apple","fastmail","nextcloud","custom"]},"server_url":{"type":"string","maxLength":2048,"format":"uri"},"username":{"type":"string","minLength":1},"password":{"type":"string","minLength":1}},"required":["member_id","preset","username","password"]}}}},"responses":{"201":{"description":"CalDAV calendar connected","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"provider":{"type":"string"},"calendar_id":{"type":"string"},"account_email":{"type":"string"},"member_id":{"type":"string","format":"uuid"},"member_name":{"type":"string","nullable":true,"description":"The owning member's email address, or null when unknown"},"connected_at":{"type":"string"},"status":{"type":"string","enum":["active","needs_reauth"]},"last_sync_at":{"type":"string","nullable":true},"sync_status":{"type":"string","enum":["connected","expired","error"]},"sync_error":{"type":"string","nullable":true},"upcoming_booking_count":{"type":"integer","minimum":0},"provider_metadata":{"type":"object","nullable":true,"properties":{"preset":{"type":"string"},"server_url":{"type":"string"},"auth_method":{"type":"string"},"calendar_url":{"type":"string"}},"required":["preset","server_url","auth_method"]}},"required":["id","provider","calendar_id","account_email","member_id","member_name","connected_at","status","last_sync_at","sync_status","sync_error","upcoming_booking_count"]}}}},"400":{"description":"Invalid credentials, unreachable server, or insecure URL","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/calendars":{"get":{"tags":["Calendars"],"summary":"List connected calendars","description":"Returns all calendar connections for the authenticated organization. Tokens are never exposed.","responses":{"200":{"description":"Calendar connections","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"provider":{"type":"string"},"calendar_id":{"type":"string"},"account_email":{"type":"string"},"member_id":{"type":"string","format":"uuid"},"member_name":{"type":"string","nullable":true,"description":"The owning member's email address, or null when unknown"},"connected_at":{"type":"string"},"status":{"type":"string","enum":["active","needs_reauth"]},"last_sync_at":{"type":"string","nullable":true},"sync_status":{"type":"string","enum":["connected","expired","error"]},"sync_error":{"type":"string","nullable":true},"upcoming_booking_count":{"type":"integer","minimum":0},"provider_metadata":{"type":"object","nullable":true,"properties":{"preset":{"type":"string"},"server_url":{"type":"string"},"auth_method":{"type":"string"},"calendar_url":{"type":"string"}},"required":["preset","server_url","auth_method"]}},"required":["id","provider","calendar_id","account_email","member_id","member_name","connected_at","status","last_sync_at","sync_status","sync_error","upcoming_booking_count"]}},"has_more":{"type":"boolean"}},"required":["data","has_more"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/calendars/{id}":{"delete":{"tags":["Calendars"],"summary":"Disconnect a calendar","description":"Revokes the OAuth token at the provider and deletes the calendar connection. Dashboard users may only disconnect their own calendar unless they are an owner or admin. API keys carry organization-wide authority and may disconnect any connection.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Calendar disconnected","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"deleted":{"type":"boolean"}},"required":["id","deleted"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"403":{"description":"Caller does not own this connection and is not an admin","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/conferencing/zoom/callback":{"get":{"tags":["Conferencing"],"summary":"Zoom OAuth callback","description":"Handles the OAuth callback from Zoom. Exchanges the authorization code for tokens, stores the encrypted connection, and redirects.","parameters":[{"schema":{"type":"string"},"required":false,"name":"code","in":"query"},{"schema":{"type":"string"},"required":false,"name":"state","in":"query"},{"schema":{"type":"string"},"required":false,"name":"error","in":"query"}],"responses":{"302":{"description":"Redirect to success or error page, including when rate limited"},"400":{"description":"Invalid state token or missing parameters","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/conferencing/zoom/connect":{"get":{"tags":["Conferencing"],"summary":"Initiate Zoom OAuth flow","description":"Returns a Zoom OAuth authorization URL. This flow must be initiated from the Astrocal dashboard, which binds the authorization request to your browser session.","parameters":[{"schema":{"type":"string"},"required":false,"name":"redirect_uri","in":"query"}],"responses":{"200":{"description":"Authorization URL generated","content":{"application/json":{"schema":{"type":"object","properties":{"authorization_url":{"type":"string"}},"required":["authorization_url"]}}}},"400":{"description":"Flow was not initiated from the Astrocal dashboard","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/conferencing":{"get":{"tags":["Conferencing"],"summary":"List conferencing connections","description":"Returns all conferencing connections for the authenticated organization. Tokens are never exposed.","responses":{"200":{"description":"Conferencing connections","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"provider":{"type":"string"},"account_email":{"type":"string","nullable":true},"account_name":{"type":"string","nullable":true},"connected_at":{"type":"string"},"status":{"type":"string"}},"required":["id","provider","account_email","account_name","connected_at","status"]}},"has_more":{"type":"boolean"}},"required":["data","has_more"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/conferencing/{id}":{"delete":{"tags":["Conferencing"],"summary":"Disconnect a conferencing integration","description":"Revokes the OAuth token at the provider and deletes the conferencing connection.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Conferencing connection disconnected","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"deleted":{"type":"boolean"}},"required":["id","deleted"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/subscription/webhook":{"post":{"tags":["Subscription"],"summary":"Stripe Billing webhook endpoint","description":"Receives Stripe Billing webhook events for subscription management. Verifies the signature using a separate billing webhook signing secret.","security":[],"responses":{"200":{"description":"Webhook received","content":{"application/json":{"schema":{"type":"object","properties":{"received":{"type":"boolean"}},"required":["received"]}}}},"400":{"description":"Invalid signature","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"500":{"description":"Handler failed (Stripe will retry)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/stripe/webhooks":{"post":{"tags":["Stripe"],"summary":"Stripe webhook endpoint","description":"Receives Stripe webhook events. Verifies the signature using the webhook signing secret. Handles payment_intent.succeeded and payment_intent.payment_failed events.","security":[],"responses":{"200":{"description":"Webhook received","content":{"application/json":{"schema":{"type":"object","properties":{"received":{"type":"boolean"}},"required":["received"]}}}},"400":{"description":"Invalid signature","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/stripe/callback":{"get":{"tags":["Stripe"],"summary":"Stripe Connect OAuth callback","description":"Handles the OAuth callback from Stripe. Exchanges the authorization code for account details and stores the connection.","security":[],"parameters":[{"schema":{"type":"string"},"required":false,"name":"code","in":"query"},{"schema":{"type":"string"},"required":false,"name":"state","in":"query"},{"schema":{"type":"string"},"required":false,"name":"error","in":"query"},{"schema":{"type":"string"},"required":false,"name":"error_description","in":"query"}],"responses":{"302":{"description":"Redirect to dashboard, including when rate limited"},"400":{"description":"Invalid state token","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/subscription":{"get":{"tags":["Subscription"],"summary":"Get current subscription","description":"Returns the organization's current plan, usage, limits, and subscription status.","responses":{"200":{"description":"Subscription status","content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"type":"string","enum":["free","starter","pro","business","partner"]},"status":{"type":"string","enum":["active","past_due","cancelled"]},"interval":{"type":"string","enum":["monthly","annual"]},"period_end":{"type":"string","nullable":true},"usage":{"type":"object","properties":{"bookings_this_month":{"type":"number"},"bookings_limit":{"type":"number"},"connected_calendars":{"type":"number"},"calendars_limit":{"type":"number"}},"required":["bookings_this_month","bookings_limit","connected_calendars","calendars_limit"]},"features":{"type":"object","properties":{"paid_bookings":{"type":"boolean"},"max_api_rate":{"type":"number"},"api_calls_per_day":{"type":"number"}},"required":["paid_bookings","max_api_rate","api_calls_per_day"]}},"required":["plan","status","interval","period_end","usage","features"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/subscription/checkout":{"post":{"tags":["Subscription"],"summary":"Create checkout session","description":"Creates a Stripe Checkout Session for upgrading to a paid plan. Returns a URL to redirect the user to Stripe's hosted checkout page.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"type":"string","enum":["starter","pro","business"]},"interval":{"type":"string","enum":["monthly","annual"],"default":"monthly"}},"required":["plan"]}}}},"responses":{"200":{"description":"Checkout session created","content":{"application/json":{"schema":{"type":"object","properties":{"checkout_url":{"type":"string"}},"required":["checkout_url"]}}}},"400":{"description":"Invalid plan or price not configured","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"409":{"description":"Already subscribed to this plan","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/subscription/portal":{"post":{"tags":["Subscription"],"summary":"Create customer portal session","description":"Creates a Stripe Customer Portal Session for managing the existing subscription. The portal handles payment method updates, plan changes, cancellations, and invoice history.","responses":{"200":{"description":"Portal session created","content":{"application/json":{"schema":{"type":"object","properties":{"portal_url":{"type":"string"}},"required":["portal_url"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"No Stripe Customer exists","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/stripe/connect":{"get":{"tags":["Stripe"],"summary":"Initiate Stripe Connect OAuth flow","description":"Returns a Stripe Connect OAuth authorization URL. This flow must be initiated from the Astrocal dashboard, which binds the authorization request to your browser session.","responses":{"200":{"description":"OAuth URL generated","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"}},"required":["url"]}}}},"400":{"description":"Flow was not initiated from the Astrocal dashboard","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"409":{"description":"Stripe already connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/stripe/status":{"get":{"tags":["Stripe"],"summary":"Check Stripe connection status","description":"Returns the current Stripe Connect status including charges_enabled and payouts_enabled.","responses":{"200":{"description":"Connection status","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"connected":{"type":"boolean","enum":[true]},"stripe_account_id":{"type":"string"},"connected_at":{"type":"string"},"charges_enabled":{"type":"boolean"},"payouts_enabled":{"type":"boolean"}},"required":["connected","stripe_account_id","connected_at","charges_enabled","payouts_enabled"]},{"type":"object","properties":{"connected":{"type":"boolean","enum":[false]}},"required":["connected"]}]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/stripe/disconnect":{"delete":{"tags":["Stripe"],"summary":"Disconnect Stripe account","description":"Deauthorizes the Stripe Connect account and removes the connection.","responses":{"200":{"description":"Stripe disconnected","content":{"application/json":{"schema":{"type":"object","properties":{"disconnected":{"type":"boolean"},"message":{"type":"string"}},"required":["disconnected","message"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"No Stripe account connected","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/payments":{"get":{"tags":["Payments"],"summary":"List payments","description":"Returns a paginated list of payments for the organization. Supports optional status filtering and cursor-based pagination.","parameters":[{"schema":{"type":"string","enum":["pending","succeeded","failed","refunded"]},"required":false,"name":"status","in":"query"},{"schema":{"type":"string"},"required":false,"name":"starting_after","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Payments list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"booking_id":{"type":"string"},"stripe_payment_intent_id":{"type":"string"},"amount":{"type":"number"},"currency":{"type":"string"},"platform_fee":{"type":"number"},"status":{"type":"string","enum":["pending","succeeded","failed","refunded"]},"refund_id":{"type":"string","nullable":true},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","booking_id","stripe_payment_intent_id","amount","currency","platform_fee","status","refund_id","created_at","updated_at"]}},"has_more":{"type":"boolean"}},"required":["data","has_more"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/payments/summary":{"get":{"tags":["Payments"],"summary":"Get payment summary","description":"Returns aggregated payment metrics for the organization: total revenue, platform fees, total refunded, and pending count. All monetary amounts are in cents.","responses":{"200":{"description":"Payment summary","content":{"application/json":{"schema":{"type":"object","properties":{"total_revenue":{"type":"number"},"platform_fees":{"type":"number"},"total_refunded":{"type":"number"},"pending_count":{"type":"number"}},"required":["total_revenue","platform_fees","total_refunded","pending_count"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/public/bookings/{bookingId}/payment":{"get":{"tags":["Bookings"],"summary":"Get payment details for a pending booking","description":"Public endpoint — no authentication required. Returns payment data needed to render the Stripe Elements payment form. Only works for bookings with `pending_payment` status. Does not return the client_secret (passed via redirect URL from widget).","security":[],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"bookingId","in":"path"}],"responses":{"200":{"description":"Payment details for the pending booking","content":{"application/json":{"schema":{"type":"object","properties":{"booking_id":{"type":"string","format":"uuid"},"status":{"type":"string"},"event_type_title":{"type":"string"},"event_type_color":{"type":"string"},"start_time":{"type":"string"},"end_time":{"type":"string"},"invitee_name":{"type":"string"},"invitee_email":{"type":"string"},"amount":{"type":"integer"},"currency":{"type":"string"},"stripe_account_id":{"type":"string"}},"required":["booking_id","status","event_type_title","event_type_color","start_time","end_time","invitee_name","invitee_email","amount","currency","stripe_account_id"]}}}},"400":{"description":"Invalid booking ID format","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Booking not found or not in pending_payment status","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/bookings/{id}/invitee-view":{"get":{"tags":["Bookings"],"summary":"Get the invitee view of a booking","description":"Returns the booker-facing summary used by the self-service cancel and reschedule pages. Authenticates with the cancel token via `?token=` only — API keys are not accepted. Omits the cancel token, notes, metadata and attendee list.","security":[],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string"},"required":false,"name":"token","in":"query"}],"responses":{"200":{"description":"Invitee view of the booking","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["confirmed","cancelled","rescheduled","pending_payment"]},"start_time":{"type":"string"},"end_time":{"type":"string"},"invitee_name":{"type":"string"},"invitee_email":{"type":"string"},"invitee_timezone":{"type":"string"},"attendee_count":{"type":"integer"},"meeting_url":{"type":"string","nullable":true},"location":{"type":"string","nullable":true},"event_type":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"slug":{"type":"string"},"duration_minutes":{"type":"integer"},"color":{"type":"string"},"conferencing_provider":{"type":"string","nullable":true,"enum":["zoom","google_meet","microsoft_teams","custom","in_person",null]},"location":{"type":"string","nullable":true},"active":{"type":"boolean"}},"required":["id","title","slug","duration_minutes","color","conferencing_provider","location","active"]},"organization":{"type":"object","properties":{"name":{"type":"string"},"slug":{"type":"string"},"logo_url":{"type":"string","nullable":true},"logo_alt_text":{"type":"string","nullable":true},"remove_branding":{"type":"boolean"}},"required":["name","slug","logo_url","logo_alt_text","remove_branding"]}},"required":["id","status","start_time","end_time","invitee_name","invitee_email","invitee_timezone","attendee_count","meeting_url","location","event_type","organization"]}}}},"401":{"description":"Unauthorized — missing or invalid token","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/bookings":{"post":{"tags":["Bookings"],"summary":"Create a booking","description":"Creates a new booking. Public endpoint — no authentication required. Validates the requested slot against availability rules and existing bookings.","security":[],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"event_type_id":{"type":"string","format":"uuid"},"start_time":{"type":"string","format":"date-time"},"invitee_name":{"type":"string","minLength":1,"maxLength":200},"invitee_email":{"type":"string","format":"email"},"invitee_timezone":{"type":"string","minLength":1,"default":"UTC"},"duration":{"type":"integer","minimum":5,"maximum":480},"notes":{"type":"string","maxLength":1000},"metadata":{"type":"object","additionalProperties":{"nullable":true}}},"required":["event_type_id","start_time","invitee_name","invitee_email"]},{"type":"object","properties":{"event_type_id":{"type":"string","format":"uuid"},"start_time":{"type":"string","format":"date-time"},"attendees":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"email":{"type":"string","format":"email"},"timezone":{"type":"string","minLength":1,"default":"UTC"}},"required":["name","email"]},"minItems":1,"maxItems":100},"duration":{"type":"integer","minimum":5,"maximum":480},"notes":{"type":"string","maxLength":1000},"metadata":{"type":"object","additionalProperties":{"nullable":true}}},"required":["event_type_id","start_time","attendees"]}]}}}},"responses":{"201":{"description":"Booking created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organization_id":{"type":"string","format":"uuid"},"event_type_id":{"type":"string","format":"uuid"},"calendar_connection_id":{"type":"string","nullable":true,"format":"uuid"},"assigned_host":{"type":"object","nullable":true,"properties":{"member_id":{"type":"string","format":"uuid"},"name":{"type":"string","nullable":true},"email":{"type":"string","nullable":true}},"required":["member_id","name","email"]},"status":{"type":"string","enum":["confirmed","cancelled","rescheduled","pending_payment"]},"is_test":{"type":"boolean"},"start_time":{"type":"string"},"end_time":{"type":"string"},"invitee_name":{"type":"string"},"invitee_email":{"type":"string"},"invitee_timezone":{"type":"string"},"attendee_count":{"type":"integer"},"attendees":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"email":{"type":"string"},"timezone":{"type":"string"},"is_primary":{"type":"boolean"}},"required":["id","name","email","timezone","is_primary"]}},"notes":{"type":"string","nullable":true},"cancel_token":{"type":"string"},"calendar_event_id":{"type":"string","nullable":true},"meeting_url":{"type":"string","nullable":true},"location":{"type":"string","nullable":true},"metadata":{"type":"object","additionalProperties":{"nullable":true}},"cancelled_at":{"type":"string","nullable":true},"cancel_reason":{"type":"string","nullable":true},"created_at":{"type":"string"},"updated_at":{"type":"string"},"payment":{"type":"object","properties":{"amount":{"type":"integer"},"currency":{"type":"string"},"client_secret":{"type":"string"},"stripe_payment_intent_id":{"type":"string"}},"required":["amount","currency","client_secret","stripe_payment_intent_id"]}},"required":["id","organization_id","event_type_id","calendar_connection_id","assigned_host","status","is_test","start_time","end_time","invitee_name","invitee_email","invitee_timezone","attendee_count","attendees","notes","cancel_token","calendar_event_id","meeting_url","location","metadata","cancelled_at","cancel_reason","created_at","updated_at"]}}}},"404":{"description":"Event type not found or inactive","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"409":{"description":"Time slot unavailable, no assigned host available, or booking cap reached","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"502":{"description":"Payment gateway error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}},"get":{"tags":["Bookings"],"summary":"List bookings","description":"Returns a paginated list of bookings for the authenticated organization. `sort` accepts `id` (default), `start_time` or `-start_time`. Because `starting_after` is an id-based cursor, combining it with a start_time sort returns 400.","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","format":"uuid"},"required":false,"name":"starting_after","in":"query"},{"schema":{"type":"string","format":"uuid"},"required":false,"name":"event_type_id","in":"query"},{"schema":{"type":"string","format":"uuid"},"required":false,"name":"assigned_host_id","in":"query"},{"schema":{"type":"string","enum":["confirmed","cancelled","rescheduled","pending_payment"]},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","format":"date-time"},"required":false,"name":"start_date","in":"query"},{"schema":{"type":"string","format":"date-time"},"required":false,"name":"end_date","in":"query"},{"schema":{"type":"string","enum":["id","start_time","-start_time"],"default":"id"},"required":false,"name":"sort","in":"query"}],"responses":{"200":{"description":"Paginated list of bookings","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organization_id":{"type":"string","format":"uuid"},"event_type_id":{"type":"string","format":"uuid"},"calendar_connection_id":{"type":"string","nullable":true,"format":"uuid"},"assigned_host":{"type":"object","nullable":true,"properties":{"member_id":{"type":"string","format":"uuid"},"name":{"type":"string","nullable":true},"email":{"type":"string","nullable":true}},"required":["member_id","name","email"]},"status":{"type":"string","enum":["confirmed","cancelled","rescheduled","pending_payment"]},"is_test":{"type":"boolean"},"start_time":{"type":"string"},"end_time":{"type":"string"},"invitee_name":{"type":"string"},"invitee_email":{"type":"string"},"invitee_timezone":{"type":"string"},"attendee_count":{"type":"integer"},"attendees":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"email":{"type":"string"},"timezone":{"type":"string"},"is_primary":{"type":"boolean"}},"required":["id","name","email","timezone","is_primary"]}},"notes":{"type":"string","nullable":true},"cancel_token":{"type":"string"},"calendar_event_id":{"type":"string","nullable":true},"meeting_url":{"type":"string","nullable":true},"location":{"type":"string","nullable":true},"metadata":{"type":"object","additionalProperties":{"nullable":true}},"cancelled_at":{"type":"string","nullable":true},"cancel_reason":{"type":"string","nullable":true},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","organization_id","event_type_id","calendar_connection_id","assigned_host","status","is_test","start_time","end_time","invitee_name","invitee_email","invitee_timezone","attendee_count","attendees","notes","cancel_token","calendar_event_id","meeting_url","location","metadata","cancelled_at","cancel_reason","created_at","updated_at"]}},"has_more":{"type":"boolean"}},"required":["data","has_more"]}}}},"400":{"description":"Invalid query — for example a start_time sort combined with starting_after","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/bookings/{id}/cancel":{"post":{"tags":["Bookings"],"summary":"Cancel a booking","description":"Cancels a booking. Supports two authentication methods: a cancel token via `?token=` query param (for invitee self-service), or an API key via `Authorization: Bearer` header (for developers). Idempotent — cancelling an already-cancelled booking returns success.","security":[],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string"},"required":false,"name":"token","in":"query"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","maxLength":500}}}}}},"responses":{"200":{"description":"Booking cancelled","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["cancelled"]},"cancelled_at":{"type":"string"}},"required":["id","status","cancelled_at"]}}}},"401":{"description":"Unauthorized — invalid or missing token/API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Booking not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/bookings/{id}/reschedule":{"post":{"tags":["Bookings"],"summary":"Reschedule a booking","description":"Reschedules a booking to a new time. Supports two authentication methods: a cancel token via `?token=` query param (for invitee self-service), or an API key via `Authorization: Bearer` header (for developers). Validates the new slot against availability rules and existing bookings.","security":[],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string"},"required":false,"name":"token","in":"query"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"new_start_time":{"type":"string","format":"date-time"},"reason":{"type":"string","maxLength":500}},"required":["new_start_time"]}}}},"responses":{"200":{"description":"Booking rescheduled","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organization_id":{"type":"string","format":"uuid"},"event_type_id":{"type":"string","format":"uuid"},"calendar_connection_id":{"type":"string","nullable":true,"format":"uuid"},"assigned_host":{"type":"object","nullable":true,"properties":{"member_id":{"type":"string","format":"uuid"},"name":{"type":"string","nullable":true},"email":{"type":"string","nullable":true}},"required":["member_id","name","email"]},"status":{"type":"string","enum":["confirmed","cancelled","rescheduled","pending_payment"]},"is_test":{"type":"boolean"},"start_time":{"type":"string"},"end_time":{"type":"string"},"invitee_name":{"type":"string"},"invitee_email":{"type":"string"},"invitee_timezone":{"type":"string"},"attendee_count":{"type":"integer"},"attendees":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"email":{"type":"string"},"timezone":{"type":"string"},"is_primary":{"type":"boolean"}},"required":["id","name","email","timezone","is_primary"]}},"notes":{"type":"string","nullable":true},"cancel_token":{"type":"string"},"calendar_event_id":{"type":"string","nullable":true},"meeting_url":{"type":"string","nullable":true},"location":{"type":"string","nullable":true},"metadata":{"type":"object","additionalProperties":{"nullable":true}},"cancelled_at":{"type":"string","nullable":true},"cancel_reason":{"type":"string","nullable":true},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","organization_id","event_type_id","calendar_connection_id","assigned_host","status","is_test","start_time","end_time","invitee_name","invitee_email","invitee_timezone","attendee_count","attendees","notes","cancel_token","calendar_event_id","meeting_url","location","metadata","cancelled_at","cancel_reason","created_at","updated_at"]}}}},"401":{"description":"Unauthorized — invalid or missing token/API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Booking not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"409":{"description":"Booking is cancelled or time slot unavailable","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"422":{"description":"Validation error (e.g. past start time)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/waitlist":{"post":{"tags":["Waitlist"],"summary":"Join waitlist","description":"Creates a waitlist entry for a fully-booked slot. Public endpoint — no authentication required. Returns 400 if the waitlist is not enabled for the event type. Returns 409 if already on waitlist or waitlist is full.","security":[],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"event_type_id":{"type":"string","format":"uuid"},"start_time":{"type":"string","format":"date-time"},"duration":{"type":"integer","minimum":5,"maximum":480},"invitee_name":{"type":"string","minLength":1,"maxLength":200},"invitee_email":{"type":"string","format":"email"},"invitee_timezone":{"type":"string","minLength":1,"default":"UTC"},"notes":{"type":"string","maxLength":1000},"metadata":{"type":"object","additionalProperties":{"nullable":true}}},"required":["event_type_id","invitee_name","invitee_email"]}}}},"responses":{"201":{"description":"Waitlist entry created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"event_type_id":{"type":"string","format":"uuid"},"start_time":{"type":"string","nullable":true},"duration_minutes":{"type":"integer","nullable":true},"status":{"type":"string","enum":["waiting","promoted","expired","cancelled"]},"position":{"type":"integer"},"invitee_name":{"type":"string"},"invitee_email":{"type":"string"},"invitee_timezone":{"type":"string"},"notes":{"type":"string","nullable":true},"metadata":{"type":"object","additionalProperties":{"nullable":true}},"cancel_token":{"type":"string"},"booking_id":{"type":"string","nullable":true,"format":"uuid"},"promoted_at":{"type":"string","nullable":true},"expires_at":{"type":"string"},"is_test":{"type":"boolean"},"created_at":{"type":"string"}},"required":["id","event_type_id","start_time","duration_minutes","status","position","invitee_name","invitee_email","invitee_timezone","notes","metadata","cancel_token","booking_id","promoted_at","expires_at","is_test","created_at"]}}}},"400":{"description":"Waitlist not enabled for this event type","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"409":{"description":"Already on waitlist or waitlist full","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}},"get":{"tags":["Waitlist"],"summary":"List waitlist entries","description":"Returns a paginated list of waitlist entries for an event type. Supports filtering by status and start_time.","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","format":"uuid"},"required":false,"name":"starting_after","in":"query"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"event_type_id","in":"query"},{"schema":{"type":"string","enum":["waiting","promoted","expired","cancelled"]},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","format":"date-time"},"required":false,"name":"start_time","in":"query"}],"responses":{"200":{"description":"Paginated list of waitlist entries","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"event_type_id":{"type":"string","format":"uuid"},"start_time":{"type":"string","nullable":true},"duration_minutes":{"type":"integer","nullable":true},"status":{"type":"string","enum":["waiting","promoted","expired","cancelled"]},"position":{"type":"integer"},"invitee_name":{"type":"string"},"invitee_email":{"type":"string"},"invitee_timezone":{"type":"string"},"notes":{"type":"string","nullable":true},"metadata":{"type":"object","additionalProperties":{"nullable":true}},"cancel_token":{"type":"string"},"booking_id":{"type":"string","nullable":true,"format":"uuid"},"promoted_at":{"type":"string","nullable":true},"expires_at":{"type":"string"},"is_test":{"type":"boolean"},"created_at":{"type":"string"}},"required":["id","event_type_id","start_time","duration_minutes","status","position","invitee_name","invitee_email","invitee_timezone","notes","metadata","cancel_token","booking_id","promoted_at","expires_at","is_test","created_at"]}},"has_more":{"type":"boolean"}},"required":["data","has_more"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/waitlist/{id}":{"delete":{"tags":["Waitlist"],"summary":"Cancel waitlist entry","description":"Cancels a waitlist entry. Supports two authentication methods: a cancel token via `?token=` query param (for invitee self-service), or an API key via `Authorization: Bearer` header (for developers).","security":[],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string"},"required":false,"name":"token","in":"query"}],"responses":{"204":{"description":"Waitlist entry cancelled"},"401":{"description":"Unauthorized — invalid or missing token/API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Waitlist entry not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}},"get":{"tags":["Waitlist"],"summary":"Get a waitlist entry","description":"Returns a single waitlist entry by ID.","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Waitlist entry found","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"event_type_id":{"type":"string","format":"uuid"},"start_time":{"type":"string","nullable":true},"duration_minutes":{"type":"integer","nullable":true},"status":{"type":"string","enum":["waiting","promoted","expired","cancelled"]},"position":{"type":"integer"},"invitee_name":{"type":"string"},"invitee_email":{"type":"string"},"invitee_timezone":{"type":"string"},"notes":{"type":"string","nullable":true},"metadata":{"type":"object","additionalProperties":{"nullable":true}},"cancel_token":{"type":"string"},"booking_id":{"type":"string","nullable":true,"format":"uuid"},"promoted_at":{"type":"string","nullable":true},"expires_at":{"type":"string"},"is_test":{"type":"boolean"},"created_at":{"type":"string"}},"required":["id","event_type_id","start_time","duration_minutes","status","position","invitee_name","invitee_email","invitee_timezone","notes","metadata","cancel_token","booking_id","promoted_at","expires_at","is_test","created_at"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/support":{"post":{"tags":["Support"],"summary":"Submit a support request","description":"Submits a support request from the public contact form. Public endpoint — no authentication required. IP rate-limited. Persists the request and dispatches notification + acknowledgement emails.","security":[],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"email":{"type":"string","format":"email"},"category":{"type":"string","enum":["general","billing","technical","bug"],"default":"general"},"subject":{"type":"string","minLength":1,"maxLength":200},"message":{"type":"string","minLength":1,"maxLength":5000},"company":{"type":"string"}},"required":["name","email","subject","message"]}}}},"responses":{"201":{"description":"Support request received","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string"}},"required":["id","status"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}},"retry_after":{"type":"integer"}},"required":["code","message","retry_after"]}},"required":["error"]}}}}}}},"/v1/bookings/by-slot":{"get":{"tags":["Bookings"],"summary":"Get bookings by slot","description":"Returns all bookings for a specific event type and start time, along with capacity information. Useful for inspecting group booking slots.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"event_type_id","in":"query"},{"schema":{"type":"string","format":"date-time"},"required":true,"name":"start_time","in":"query"}],"responses":{"200":{"description":"Slot bookings and capacity info","content":{"application/json":{"schema":{"type":"object","properties":{"event_type_id":{"type":"string","format":"uuid"},"start_time":{"type":"string"},"total_capacity":{"type":"integer"},"total_attendees":{"type":"integer"},"spots_remaining":{"type":"integer"},"booking_count":{"type":"integer"}},"required":["event_type_id","start_time","total_capacity","total_attendees","spots_remaining","booking_count"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Event type not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/bookings/{id}":{"get":{"tags":["Bookings"],"summary":"Get a booking","description":"Returns a single booking by ID for the authenticated organization.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Booking found","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organization_id":{"type":"string","format":"uuid"},"event_type_id":{"type":"string","format":"uuid"},"calendar_connection_id":{"type":"string","nullable":true,"format":"uuid"},"assigned_host":{"type":"object","nullable":true,"properties":{"member_id":{"type":"string","format":"uuid"},"name":{"type":"string","nullable":true},"email":{"type":"string","nullable":true}},"required":["member_id","name","email"]},"status":{"type":"string","enum":["confirmed","cancelled","rescheduled","pending_payment"]},"is_test":{"type":"boolean"},"start_time":{"type":"string"},"end_time":{"type":"string"},"invitee_name":{"type":"string"},"invitee_email":{"type":"string"},"invitee_timezone":{"type":"string"},"attendee_count":{"type":"integer"},"attendees":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"email":{"type":"string"},"timezone":{"type":"string"},"is_primary":{"type":"boolean"}},"required":["id","name","email","timezone","is_primary"]}},"notes":{"type":"string","nullable":true},"cancel_token":{"type":"string"},"calendar_event_id":{"type":"string","nullable":true},"meeting_url":{"type":"string","nullable":true},"location":{"type":"string","nullable":true},"metadata":{"type":"object","additionalProperties":{"nullable":true}},"cancelled_at":{"type":"string","nullable":true},"cancel_reason":{"type":"string","nullable":true},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","organization_id","event_type_id","calendar_connection_id","assigned_host","status","is_test","start_time","end_time","invitee_name","invitee_email","invitee_timezone","attendee_count","attendees","notes","cancel_token","calendar_event_id","meeting_url","location","metadata","cancelled_at","cancel_reason","created_at","updated_at"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/api-keys":{"post":{"tags":["API Keys"],"summary":"Create an API key","description":"Creates a new API key for the authenticated organization. The full key is returned in the response — this is the only time it will be shown. Requires dashboard authentication: requests authenticated with an API key are rejected, so a leaked key cannot be used to mint further keys.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"mode":{"type":"string","enum":["live","test"]},"expires_at":{"type":"string","format":"date-time"}},"required":["name","mode"]}}}},"responses":{"201":{"description":"API key created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"org_id":{"type":"string"},"name":{"type":"string"},"key":{"type":"string"},"key_prefix":{"type":"string"},"mode":{"type":"string","enum":["live","test"]},"expires_at":{"type":"string","nullable":true},"created_at":{"type":"string"},"warning":{"type":"string"}},"required":["id","org_id","name","key","key_prefix","mode","expires_at","created_at","warning"]}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"403":{"description":"Forbidden — API keys cannot create other API keys","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}},"get":{"tags":["API Keys"],"summary":"List API keys","description":"Lists all API keys for the authenticated organization. Full keys are never shown — only the prefix for identification.","responses":{"200":{"description":"API keys list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"key_prefix":{"type":"string"},"mode":{"type":"string","enum":["live","test"]},"last_used_at":{"type":"string","nullable":true},"expires_at":{"type":"string","nullable":true},"revoked_at":{"type":"string","nullable":true},"created_at":{"type":"string"}},"required":["id","name","key_prefix","mode","last_used_at","expires_at","revoked_at","created_at"]}}},"required":["data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/api-keys/{id}":{"delete":{"tags":["API Keys"],"summary":"Revoke an API key","description":"Revokes an API key by setting its revoked_at timestamp. Revoked keys immediately fail authentication. Idempotent — revoking an already-revoked key returns 204.","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"204":{"description":"API key revoked"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"API key not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/webhooks":{"post":{"tags":["Webhooks"],"summary":"Create a webhook endpoint","description":"Creates a new webhook endpoint for the authenticated organization. The signing secret is returned in the response — this is the only time it will be shown. Use the secret to verify webhook signatures via the `X-Astrocal-Signature` header.\n\nAstrocal immediately POSTs a verification challenge to the URL, which your endpoint must echo back. The endpoint receives no events until `verification_status` is `verified`. Creation always succeeds — check `verification_status` in the response and retry with `POST /v1/webhooks/{id}/verify` if it is not yet verified.\n\nAn organization may hold at most 50 webhook endpoints.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string","enum":["booking.created","booking.cancelled","booking.rescheduled","calendar.disconnected","waitlist_entry.created","waitlist_entry.cancelled","waitlist_entry.promoted","waitlist_entry.expired"]},"minItems":1},"active":{"type":"boolean","default":true}},"required":["url","events"]}}}},"responses":{"201":{"description":"Webhook endpoint created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string"}},"secret":{"type":"string"},"active":{"type":"boolean"},"verification_status":{"type":"string","enum":["pending","verified","failed"]},"verified_at":{"type":"string","nullable":true},"verification_last_error":{"type":"string","nullable":true},"consecutive_failures":{"type":"integer"},"suspended_at":{"type":"string","nullable":true},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","url","events","secret","active","verification_status","verified_at","verification_last_error","consecutive_failures","suspended_at","created_at","updated_at"]}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"409":{"description":"Webhook endpoint limit reached","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}},"get":{"tags":["Webhooks"],"summary":"List webhook endpoints","description":"Lists all webhook endpoints for the authenticated organization. Signing secrets are never included in list responses.","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","format":"uuid"},"required":false,"name":"starting_after","in":"query"}],"responses":{"200":{"description":"Webhook endpoints list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string"}},"active":{"type":"boolean"},"verification_status":{"type":"string","enum":["pending","verified","failed"]},"verified_at":{"type":"string","nullable":true},"verification_last_error":{"type":"string","nullable":true},"consecutive_failures":{"type":"integer"},"suspended_at":{"type":"string","nullable":true},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","url","events","active","verification_status","verified_at","verification_last_error","consecutive_failures","suspended_at","created_at","updated_at"]}},"has_more":{"type":"boolean"}},"required":["data","has_more"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/webhooks/{id}":{"patch":{"tags":["Webhooks"],"summary":"Update a webhook endpoint","description":"Partially updates a webhook endpoint. Only the provided fields are updated. The signing secret cannot be changed.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string","enum":["booking.created","booking.cancelled","booking.rescheduled","calendar.disconnected","waitlist_entry.created","waitlist_entry.cancelled","waitlist_entry.promoted","waitlist_entry.expired"]},"minItems":1},"active":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Webhook endpoint updated","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string"}},"active":{"type":"boolean"},"verification_status":{"type":"string","enum":["pending","verified","failed"]},"verified_at":{"type":"string","nullable":true},"verification_last_error":{"type":"string","nullable":true},"consecutive_failures":{"type":"integer"},"suspended_at":{"type":"string","nullable":true},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","url","events","active","verification_status","verified_at","verification_last_error","consecutive_failures","suspended_at","created_at","updated_at"]}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Webhook endpoint not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}},"delete":{"tags":["Webhooks"],"summary":"Delete a webhook endpoint","description":"Permanently deletes a webhook endpoint and all its delivery records. This action cannot be undone.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"204":{"description":"Webhook endpoint deleted"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Webhook endpoint not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/webhooks/{id}/verify":{"post":{"tags":["Webhooks"],"summary":"Verify a webhook endpoint URL","description":"Proves that the destination wants Astrocal traffic. With an empty body, Astrocal POSTs a challenge to the endpoint URL and requires it echoed back in the response body — either as `{\"challenge\": \"...\"}` or as plain text — with a 2xx status within 5 seconds.\n\nIf your receiver cannot control its response body, read the challenge from its request log and send it back here as `{\"challenge\": \"whchal_...\"}`.\n\nAttempts are limited to 5 per endpoint per hour and 20 per organization per hour.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"challenge":{"type":"string","minLength":1,"maxLength":200}}}}}},"responses":{"200":{"description":"Verification attempted — check `verification_status` for the outcome","content":{"application/json":{"schema":{"type":"object","properties":{"verification_status":{"type":"string","enum":["pending","verified","failed"]},"verified_at":{"type":"string","nullable":true},"verification_last_error":{"type":"string","nullable":true}},"required":["verification_status","verified_at","verification_last_error"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Webhook endpoint not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"409":{"description":"Webhook endpoint is already verified","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"429":{"description":"Too many verification attempts","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/webhooks/{id}/resume":{"post":{"tags":["Webhooks"],"summary":"Resume a suspended webhook endpoint","description":"Lifts a circuit-breaker suspension. Astrocal suspends an endpoint after 5 consecutive failed delivery attempts and drops its queued deliveries. Fix the destination first — resuming a still-broken endpoint simply suspends it again.\n\nSuspension is separate from URL verification: resuming does not re-verify, and verifying does not resume.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Webhook endpoint resumed","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string"}},"active":{"type":"boolean"},"verification_status":{"type":"string","enum":["pending","verified","failed"]},"verified_at":{"type":"string","nullable":true},"verification_last_error":{"type":"string","nullable":true},"consecutive_failures":{"type":"integer"},"suspended_at":{"type":"string","nullable":true},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","url","events","active","verification_status","verified_at","verification_last_error","consecutive_failures","suspended_at","created_at","updated_at"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Webhook endpoint not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"409":{"description":"Webhook endpoint is not suspended","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/webhooks/{id}/deliveries":{"get":{"tags":["Webhooks"],"summary":"List webhook deliveries","description":"Lists delivery attempts for a specific webhook endpoint with pagination and optional status filtering.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","format":"uuid"},"required":false,"name":"starting_after","in":"query"},{"schema":{"type":"string","enum":["pending","success","failed"]},"required":false,"name":"status","in":"query"}],"responses":{"200":{"description":"Webhook deliveries list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"webhook_endpoint_id":{"type":"string","format":"uuid"},"event_type":{"type":"string"},"payload":{"type":"object","additionalProperties":{"nullable":true}},"status":{"type":"string","enum":["pending","success","failed"]},"attempts":{"type":"integer"},"last_attempt_at":{"type":"string","nullable":true},"response_status_code":{"type":"integer","nullable":true},"response_body":{"type":"string","nullable":true},"next_retry_at":{"type":"string","nullable":true},"created_at":{"type":"string"}},"required":["id","webhook_endpoint_id","event_type","payload","status","attempts","last_attempt_at","response_status_code","response_body","next_retry_at","created_at"]}},"has_more":{"type":"boolean"}},"required":["data","has_more"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Webhook endpoint not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/webhooks/{id}/deliveries/{delivery_id}/retry":{"post":{"tags":["Webhooks"],"summary":"Retry a failed webhook delivery","description":"Resets a failed delivery for re-dispatch by the background retry worker. Sets status back to `pending`, clears attempts and response data, and sets `next_retry_at` to now for immediate pickup.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"delivery_id","in":"path"}],"responses":{"200":{"description":"Delivery queued for retry","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"webhook_endpoint_id":{"type":"string","format":"uuid"},"event_type":{"type":"string"},"payload":{"type":"object","additionalProperties":{"nullable":true}},"status":{"type":"string","enum":["pending","success","failed"]},"attempts":{"type":"integer"},"last_attempt_at":{"type":"string","nullable":true},"response_status_code":{"type":"integer","nullable":true},"response_body":{"type":"string","nullable":true},"next_retry_at":{"type":"string","nullable":true},"created_at":{"type":"string"}},"required":["id","webhook_endpoint_id","event_type","payload","status","attempts","last_attempt_at","response_status_code","response_body","next_retry_at","created_at"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Webhook endpoint or delivery not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"409":{"description":"Delivery is not in failed status","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/activity":{"get":{"tags":["Activity"],"summary":"List activity logs","description":"Returns a paginated list of activity log entries for the authenticated organization. Supports filtering by action, resource_type, and resource_id.","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","format":"uuid"},"required":false,"name":"starting_after","in":"query"},{"schema":{"type":"string"},"required":false,"name":"action","in":"query"},{"schema":{"type":"string"},"required":false,"name":"resource_type","in":"query"},{"schema":{"type":"string","format":"uuid"},"required":false,"name":"resource_id","in":"query"}],"responses":{"200":{"description":"Paginated list of activity log entries","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organization_id":{"type":"string","format":"uuid"},"action":{"type":"string"},"resource_type":{"type":"string"},"resource_id":{"type":"string","nullable":true,"format":"uuid"},"actor_type":{"type":"string"},"actor_id":{"type":"string","nullable":true},"metadata":{"type":"object","nullable":true,"additionalProperties":{"nullable":true}},"created_at":{"type":"string","format":"date-time"}},"required":["id","organization_id","action","resource_type","resource_id","actor_type","actor_id","metadata","created_at"]}},"has_more":{"type":"boolean"}},"required":["data","has_more"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/usage":{"get":{"tags":["Usage"],"summary":"Get API usage and limits","description":"Returns the current plan tier, rate limits, usage counters, and reset times for the authenticated organization.","responses":{"200":{"description":"Current usage and limits","content":{"application/json":{"schema":{"type":"object","properties":{"tier":{"type":"string","enum":["free","starter","pro","business","partner"]},"limits":{"type":"object","properties":{"api_calls_per_minute":{"type":"number"},"api_calls_per_day":{"type":"number"},"bookings_per_month":{"type":"number"}},"required":["api_calls_per_minute","api_calls_per_day","bookings_per_month"]},"usage":{"type":"object","properties":{"api_calls_today":{"type":"number"},"api_calls_remaining_daily":{"type":"number"},"bookings_this_month":{"type":"number"},"bookings_remaining_monthly":{"type":"number"}},"required":["api_calls_today","api_calls_remaining_daily","bookings_this_month","bookings_remaining_monthly"]},"reset_at":{"type":"object","properties":{"daily":{"type":"string"},"monthly":{"type":"string"}},"required":["daily","monthly"]}},"required":["tier","limits","usage","reset_at"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/usage/summary":{"get":{"tags":["Usage"],"summary":"Get usage summary metrics","description":"Returns aggregate usage statistics for the authenticated organization over a specified period. Data is derived from existing tables. API call counts may undercount for periods beyond 48 hours due to the rate limit window cleanup job.","parameters":[{"schema":{"type":"string","enum":["7d","30d"],"default":"30d"},"required":false,"name":"period","in":"query"}],"responses":{"200":{"description":"Usage summary for the requested period","content":{"application/json":{"schema":{"type":"object","properties":{"period":{"type":"string","enum":["7d","30d"]},"period_start":{"type":"string"},"period_end":{"type":"string"},"api_calls":{"type":"object","properties":{"total":{"type":"number"}},"required":["total"]},"bookings":{"type":"object","properties":{"total":{"type":"number"},"confirmed":{"type":"number"},"cancelled":{"type":"number"},"pending_payment":{"type":"number"}},"required":["total","confirmed","cancelled","pending_payment"]},"event_types":{"type":"object","properties":{"total":{"type":"number"},"active":{"type":"number"}},"required":["total","active"]},"calendar_connections":{"type":"object","properties":{"total":{"type":"number"},"active":{"type":"number"}},"required":["total","active"]},"webhooks":{"type":"object","properties":{"total_deliveries":{"type":"number"},"successful":{"type":"number"},"failed":{"type":"number"}},"required":["total_deliveries","successful","failed"]},"onboarding":{"type":"object","properties":{"activated_at":{"type":"string","nullable":true},"booking_link_shared_at":{"type":"string","nullable":true},"availability_confirmed_at":{"type":"string","nullable":true},"checklist_dismissed_at":{"type":"string","nullable":true},"first_booking_celebrated_at":{"type":"string","nullable":true},"activation_booking_id":{"type":"string","nullable":true},"organization_created_at":{"type":"string","nullable":true}},"required":["activated_at","booking_link_shared_at","availability_confirmed_at","checklist_dismissed_at","first_booking_celebrated_at","activation_booking_id","organization_created_at"]}},"required":["period","period_start","period_end","api_calls","bookings","event_types","calendar_connections","webhooks","onboarding"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/organization/branding":{"get":{"tags":["Branding"],"summary":"Get branding settings","description":"Returns the organization's branding settings including logo URL and branding toggle.","responses":{"200":{"description":"Branding settings","content":{"application/json":{"schema":{"type":"object","properties":{"logo_url":{"type":"string","nullable":true},"logo_alt_text":{"type":"string","nullable":true},"remove_branding":{"type":"boolean"},"listing_page_enabled":{"type":"boolean"}},"required":["logo_url","logo_alt_text","remove_branding","listing_page_enabled"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}},"put":{"tags":["Branding"],"summary":"Update branding settings","description":"Updates the organization's branding settings. Send a base64 data URI for logo_base64 to upload a new logo. Supported formats: PNG, JPEG, SVG, WebP. Maximum size: 512KB.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"logo_base64":{"type":"string"},"logo_alt_text":{"type":"string","maxLength":100},"remove_branding":{"type":"boolean"},"listing_page_enabled":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Updated branding settings","content":{"application/json":{"schema":{"type":"object","properties":{"logo_url":{"type":"string","nullable":true},"logo_alt_text":{"type":"string","nullable":true},"remove_branding":{"type":"boolean"},"listing_page_enabled":{"type":"boolean"}},"required":["logo_url","logo_alt_text","remove_branding","listing_page_enabled"]}}}},"400":{"description":"Invalid input (bad format, unsupported type, or too large)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/organization/branding/logo":{"delete":{"tags":["Branding"],"summary":"Delete organization logo","description":"Removes the organization's logo from storage and clears the logo URL.","responses":{"200":{"description":"Branding settings after logo removal","content":{"application/json":{"schema":{"type":"object","properties":{"logo_url":{"type":"string","nullable":true},"logo_alt_text":{"type":"string","nullable":true},"remove_branding":{"type":"boolean"},"listing_page_enabled":{"type":"boolean"}},"required":["logo_url","logo_alt_text","remove_branding","listing_page_enabled"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/organizations/{orgId}/members":{"get":{"tags":["Team"],"summary":"List organization members","description":"Lists all members of an organization. Service key authentication required.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"orgId","in":"path"}],"responses":{"200":{"description":"List of members","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organization_id":{"type":"string","format":"uuid"},"user_id":{"type":"string"},"user_email":{"type":"string","nullable":true},"role":{"type":"string","enum":["owner","admin","member","viewer"]},"invited_by":{"type":"string","nullable":true},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","organization_id","user_id","user_email","role","invited_by","created_at","updated_at"]}}},"required":["data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/organizations/{orgId}/members/{memberId}":{"patch":{"tags":["Team"],"summary":"Update member role","description":"Updates a member's role. Cannot change the owner's role (use transfer-ownership). Requires owner or admin role.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"orgId","in":"path"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"memberId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"role":{"type":"string","enum":["admin","member","viewer"]}},"required":["role"]}}}},"responses":{"200":{"description":"Member updated","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organization_id":{"type":"string","format":"uuid"},"user_id":{"type":"string"},"user_email":{"type":"string","nullable":true},"role":{"type":"string","enum":["owner","admin","member","viewer"]},"invited_by":{"type":"string","nullable":true},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","organization_id","user_id","user_email","role","invited_by","created_at","updated_at"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"403":{"description":"Forbidden — insufficient permissions","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Member not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}},"delete":{"tags":["Team"],"summary":"Remove member","description":"Removes a member from the organization. The owner cannot be removed.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"orgId","in":"path"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"memberId","in":"path"}],"responses":{"204":{"description":"Member removed"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Member not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/organizations/{orgId}/invitations":{"post":{"tags":["Team"],"summary":"Create invitation","description":"Sends an email invitation to join the organization.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"orgId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"},"role":{"type":"string","enum":["admin","member","viewer"]}},"required":["email","role"]}}}},"responses":{"201":{"description":"Invitation created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organization_id":{"type":"string","format":"uuid"},"email":{"type":"string"},"role":{"type":"string","enum":["owner","admin","member","viewer"]},"status":{"type":"string","enum":["pending","accepted","expired","revoked"]},"invited_by":{"type":"string"},"expires_at":{"type":"string"},"accepted_at":{"type":"string","nullable":true},"created_at":{"type":"string"}},"required":["id","organization_id","email","role","status","invited_by","expires_at","accepted_at","created_at"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"409":{"description":"Conflict — already a member or pending invitation exists","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}},"get":{"tags":["Team"],"summary":"List pending invitations","description":"Lists all pending invitations for the organization.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"orgId","in":"path"}],"responses":{"200":{"description":"List of pending invitations","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organization_id":{"type":"string","format":"uuid"},"email":{"type":"string"},"role":{"type":"string","enum":["owner","admin","member","viewer"]},"status":{"type":"string","enum":["pending","accepted","expired","revoked"]},"invited_by":{"type":"string"},"expires_at":{"type":"string"},"accepted_at":{"type":"string","nullable":true},"created_at":{"type":"string"}},"required":["id","organization_id","email","role","status","invited_by","expires_at","accepted_at","created_at"]}}},"required":["data"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/organizations/{orgId}/invitations/{invitationId}":{"delete":{"tags":["Team"],"summary":"Revoke invitation","description":"Revokes a pending invitation.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"orgId","in":"path"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"invitationId","in":"path"}],"responses":{"204":{"description":"Invitation revoked"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Invitation not found or not pending","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/invitations/accept":{"post":{"tags":["Team"],"summary":"Accept invitation","description":"Accepts an invitation by token. Creates a member record and marks the invitation as accepted.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","minLength":1},"user_id":{"type":"string","minLength":1},"user_email":{"type":"string","format":"email"}},"required":["token","user_id","user_email"]}}}},"responses":{"200":{"description":"Invitation accepted, member created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"organization_id":{"type":"string","format":"uuid"},"user_id":{"type":"string"},"user_email":{"type":"string","nullable":true},"role":{"type":"string","enum":["owner","admin","member","viewer"]},"invited_by":{"type":"string","nullable":true},"created_at":{"type":"string"},"updated_at":{"type":"string"}},"required":["id","organization_id","user_id","user_email","role","invited_by","created_at","updated_at"]}}}},"400":{"description":"Invitation expired or revoked","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Invitation not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"409":{"description":"Already accepted or already a member","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/organizations/{orgId}/transfer-ownership":{"post":{"tags":["Team"],"summary":"Transfer ownership","description":"Transfers organization ownership to another member. The current owner becomes an admin.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"orgId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":"string","format":"uuid"}},"required":["member_id"]}}}},"responses":{"200":{"description":"Ownership transferred","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"403":{"description":"Forbidden — not the owner","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"404":{"description":"Target member not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}},"/v1/test-data":{"delete":{"tags":["Test Data"],"summary":"Delete all test data","description":"Deletes all test event types, availability rules, and bookings for the authenticated organization. Only works with test API keys (ac_test_*). Returns 400 if called with a live key.","responses":{"200":{"description":"Test data deleted","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"object","properties":{"bookings":{"type":"integer"},"availability_rules":{"type":"integer"},"event_types":{"type":"integer"}},"required":["bookings","availability_rules","event_types"]}},"required":["deleted"]}}}},"400":{"description":"Bad request — must use a test API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"nullable":true}}},"required":["code","message"]}},"required":["error"]}}}}}}}}}