Bronjoy Api-spec API

Bronjoy Api-spec API: 313 operations.

Operations 313

Showing the first 250 of 313. The full set is in the contract, and via get_provider_operations.

GET /org/announcements List announcements for the organization #
POST /org/announcements Create a new announcement #
GET /org/announcements/{announcement} Get a single announcement #
PUT /org/announcements/{announcement} Update an announcement #
DELETE /org/announcements/{announcement} Delete an announcement #
GET /org/appointments #
POST /org/appointments #
GET /org/appointments/pending #
GET /org/appointments/calendar #
POST /org/appointments/batch-update #
POST /org/appointments/batch-destroy #
GET /org/appointments/{appointment} #
PUT /org/appointments/{appointment} #
DELETE /org/appointments/{appointment} #
POST /org/appointments/{appointment}/submit #
POST /org/appointments/{appointment}/cancel #
POST /org/appointments/{appointment}/reschedule #
GET /org/schedule Get unified schedule - both recurring availabilities and one-time exceptions Supports my_schedule=true to filter to current user's schedule only #
POST /org/schedule Store a new schedule entry #
PUT /org/schedule/{id} Update a schedule entry #
DELETE /org/schedule/{id} Delete a schedule entry #
GET /org/availability #
PUT /org/availability #
GET /org/availability/exceptions #
POST /org/availability/exceptions #
POST /organization-profiles/{slug}/booking-requests Leave a booking request on a venue that takes no online bookings yet #
GET /org/branches #
POST /org/branches #
GET /org/branches/{branch} #
PUT /org/branches/{branch} #
DELETE /org/branches/{branch} #
GET /calendar/callback/google Handle Google OAuth callback. Exchanges authorization code for tokens and redirects back to frontend #
GET /org/calendar/connections List the current staff member's calendar connections #
GET /org/calendar/auth-url Generate the OAuth authorization URL for connecting a provider #
GET /org/calendar/feed-url Get the iCal feed URL for the current staff member #
DELETE /org/calendar/connections/{connection} Disconnect a calendar connection and revoke tokens #
PATCH /org/calendar/connections/{connection}/toggle Toggle push or pull sync for a connection #
POST /org/calendar/connections/{connection}/sync Trigger an immediate sync for a calendar connection #
GET /calendar/feed/{token}.ics Serve the iCal feed for a staff member. The token in the URL authenticates the request — no login needed #
GET /categories List all categories with published organization counts and metadata. Used for the /categories index page #
GET /categories/{slug} Get category landing page data: category details, city breakdown, sibling categories #
GET /categories/{slug}/{city} Get category + city landing page data #
GET /org/ai/conversations #
GET /org/ai/conversations/{conversation} #
DELETE /org/ai/conversations/{conversation} #
POST /org/ai/chat SSE streaming chat endpoint #
GET /cities List all cities that have at least one published organization, with counts. Used for the /cities index page and the sitemap. Ordered by org count desc #
GET /cities/{city} City landing page data: the city, its category breakdown, and top providers. The inverse of CategoryLandingController@showCity — "city → its categories + top providers" #
GET /claims List the authenticated user's claim requests #
POST /claims Submit a new claim request #
GET /claims/{claimRequest} Show a single claim request belonging to the authenticated user #
POST /claims/{claimRequest}/documents Attach additional documents to an open claim request #
POST /claims/{claimRequest}/withdraw Withdraw a pending claim request #
POST /claims/{claimRequest}/seen Mark a request as seen (clears red-dot indicator) #
POST /payment/click/callback #
POST /org/clients/batch-update Batch update clients #
POST /org/clients/batch-destroy Batch delete clients #
GET /org/clients List the organization's clients (paginated), with visit counts and last/next visit #
POST /org/clients Create a client #
GET /org/clients/{client} Show a client. Visit history lives on `GET /org/clients/{client}/history` #
PUT /org/clients/{client} Update a client #
DELETE /org/clients/{client} #
GET /org/clients/{client}/notes Get notes for a client #
POST /org/clients/{client}/notes Store a note for a client #
GET /org/clients/{client}/history Get appointment history for a client #
POST /appointments/{appointment}/cancel-by-token Cancel an appointment using a signed token from a reminder email. Public endpoint — no authentication required #
POST /appointments/{appointment}/confirm-by-token Confirm an appointment using a signed token. Public endpoint — used when staff manually creates an appointment and sends the client a confirm link #
GET /client/appointments/upcoming Get upcoming appointments for the authenticated client #
GET /client/appointments/history Get past appointments (history) for the authenticated client #
GET /client/appointments/stats Get appointment statistics for the client #
GET /client/appointments/{appointment} Get a single appointment detail #
POST /client/appointments/{appointment}/cancel Cancel an appointment #
POST /client/appointments/{appointment}/reschedule Reschedule an appointment #
POST /client/auth/phone/request Request phone verification code for client. Clients can register/login via phone #
POST /client/auth/phone/verify Verify phone and login/register client. Auto-creates user if doesn't exist #
POST /client/auth/email/request Request email verification code for client. Clients can register/login via email #
POST /client/auth/email/verify Verify email and login/register client. Auto-creates user if doesn't exist #
POST /client/auth/telegram Authenticate client via Telegram Mini App. Auto-creates user if doesn't exist #
GET /booking/{slug} Get organization public page with services and staff. Public endpoint - no auth required #
GET /booking/{slug}/services Get services with their available staff for an organization #
GET /booking/{slug}/staff Get staff with their services for an organization #
GET /booking/{slug}/slots Get available slots for a service. Can be used by unauthenticated users to view availability #
GET /booking/{slug}/slots/range Get available slots for multiple days #
GET /booking/{slug}/slots/all Get all slots for a date with availability status. Includes both available and booked slots #
GET /booking/{slug}/slots/grid Get available slots for all staff members in a grid layout. Returns slots grouped by staff and date for a Zocdoc-style availability grid #
GET /booking/{slug}/calendar Get calendar availability info for a date range. Returns date availability status for the booking calendar #
GET /booking/{slug}/waitlist/count Get waitlist count for a service on a specific date. Public endpoint #
POST /client/booking/{slug}/request-code Send a confirmation code for a guest booking #
POST /client/booking/{slug} #
POST /client/booking/{slug}/waitlist Join the waitlist for a service on a specific date. Supports authenticated clients, guest phone/email, and Telegram #
GET /client/appointments/{appointment}/ics Download a .ics file for a specific appointment #
GET /client/conversations Get all conversations for the client #
POST /client/conversations/start Start or get existing conversation with an organization #
GET /client/conversations/{conversation} Get a specific conversation #
GET /client/conversations/{conversation}/messages Get messages for a conversation #
POST /client/conversations/{conversation}/messages Send a message in a conversation #
POST /client/conversations/{conversation}/close Close a conversation #
POST /client/device-tokens Register this device for push notifications #
DELETE /client/device-tokens Unregister a device (called on logout) #
GET /client/favorites List the authenticated user's favorite organizations #
POST /client/favorites/toggle Toggle favorite status for an organization #
POST /client/favorites/sync Sync favorites from local storage slugs. Creates favorites for organizations that aren't already favorited #
GET /client/favorites/check/{organization} Check if an organization is favorited by the authenticated user #
GET /client/notification-settings Get the authenticated user's notification preferences #
PUT /client/notification-settings Update notification preferences. The same preference set is applied to every Client record owned by the authenticated user #
GET /client/payments/appointments/{appointment}/requirement Get payment requirement for an appointment the client owns #
GET /client/payments/appointments/{appointment}/transactions List all transactions for an appointment #
POST /client/payments/appointments/{appointment}/pay Initiate payment for an appointment. Amount is auto-calculated from the prepay policy — client only picks the provider #
GET /client/payments/transactions/{transaction} Get status of a specific transaction belonging to the client #
GET /client/profile Get current user's profile #
PUT /client/profile Update profile (name and non-sensitive fields) #
DELETE /client/profile Delete account (soft delete) #
POST /client/profile/email/request Request verification code to update email #
POST /client/profile/email/verify Verify and complete email change #
POST /client/profile/phone/request Request verification code to update phone #
POST /client/profile/phone/verify Verify and complete phone change #
GET /client/profile/organizations Get list of organizations where user is a client #
GET /client/referral GET /api/client/referral #
GET /client/referral/businesses GET /api/client/referral/businesses #
GET /client/referral/commissions GET /api/client/referral/commissions #
GET /client/referral/payout-methods GET /api/client/referral/payout-methods #
POST /client/referral/payout-methods POST /api/client/referral/payout-methods #
DELETE /client/referral/payout-methods/{payoutMethod} DELETE /api/client/referral/payout-methods/{payoutMethod} #
GET /client/referral/payouts GET /api/client/referral/payouts #
POST /client/referral/payouts POST /api/client/referral/payouts #
GET /client/reviews Get reviews left by the client #
POST /client/reviews Create a new review for a completed appointment #
GET /client/reviews/reviewable Get appointments that can be reviewed #
GET /client/reviews/{review} Get a single review #
PUT /client/reviews/{review} Update a review #
DELETE /client/reviews/{review} Delete a review #
GET /client/waitlist List the authenticated client's waitlist entries across all organizations #
POST /client/waitlist/{waitlistEntry}/accept Accept an offered slot — creates the appointment #
POST /client/waitlist/{waitlistEntry}/decline Decline an offered slot #
POST /client/waitlist/{waitlistEntry}/cancel Cancel a waitlist entry #
GET /auth/me Get the authenticated user's information. Works for both clients and staff #
POST /auth/logout Logout the authenticated user. Revokes the current access token #
PUT /auth/me/locale Update the authenticated user's preferred locale. Works for both clients and staff #
PUT /auth/me/platform-notifications Update what Bronjoy itself is allowed to send this person on Telegram #
GET /org/dashboard Get unified dashboard data for the authenticated organization #
GET /geo/cities Every city, for the onboarding / settings location picker #
GET /health #
POST /scraper/import Import businesses from the scraper #
POST /org/join-requests/search Search for an organization by invite code. Public endpoint (but requires auth) for users looking to join #
POST /org/join-requests Create a join request for an organization #
GET /org/join-requests Get pending join requests for the current organization (owner only) #
GET /org/join-requests/my Get the current user's join requests #
DELETE /org/join-requests/{joinRequest} Cancel a join request (by the requester) #
POST /org/join-requests/{joinRequest}/approve Approve a join request #
POST /org/join-requests/{joinRequest}/reject Reject a join request #
POST /org/ai/tokens Generate a new MCP access token for external AI clients #
POST /org/media Upload a file and return its URL #
GET /client/notifications List notifications (paginated) with unread count #
GET /client/notifications/unread-count Get unread notification count (lightweight endpoint for polling) #
POST /client/notifications/{id}/read Mark a single notification as read #
POST /client/notifications/read-all Mark all notifications as read #
GET /org/notifications List notifications (paginated) with unread count #
GET /org/notifications/unread-count Get unread notification count (lightweight endpoint for polling) #
POST /org/notifications/{id}/read Mark a single notification as read #
POST /org/notifications/read-all Mark all notifications as read #
GET /org/onboarding/maps/providers List map import providers and their availability #
POST /org/onboarding/maps/search Search across Google / 2GIS / Yandex for businesses matching a query #
POST /org/onboarding/complete Mark organization owner onboarding as complete. Sets onboarding_completed_at on the organization so the setup wizard is not shown again #
POST /org/onboarding/state Persist the owner's setup wizard progress #
POST /org/onboarding/staff-complete Mark staff onboarding as complete for the currently authenticated staff member. Sets onboarding_completed_at on the staff record so the welcome screen is not shown again #
GET /org/auth/organizations #
POST /org/organizations #
GET /org/organizations #
GET /org/organizations/{organization} #
PUT /org/organizations/{organization} #
DELETE /org/organizations/{organization} #
POST /org/invite-code/generate Generate invite code for an organization #
POST /org/invite-code/regenerate Regenerate invite code for an organization #
GET /org/notification-settings/catalog The catalogue of everything the provider bot can send #
GET /org/notification-settings Get notification settings for the current organization. Creates with defaults if not found #
PUT /org/notification-settings Update notification settings for the current organization #
GET /org/payment-settings List all payment settings for the current organization #
POST /org/payment-settings Create or update a payment setting for a provider. Uses upsert on (organization_id, provider) unique constraint #
GET /org/payment-settings/{paymentSetting} Get a specific payment setting #
DELETE /org/payment-settings/{paymentSetting} Delete a payment setting #
POST /org/payment-settings/{paymentSetting}/toggle Toggle the enabled state of a payment setting #
POST /org/payment-settings/{paymentSetting}/validate Validate stored credentials by running a health check against the provider #
GET /organization-profiles/search Search/list public organization profiles (marketplace) Public endpoint for marketplace/directory #
GET /organization-profiles/search/map Get lightweight map markers for all matching profiles (no pagination). Public endpoint for marketplace map view #
GET /organization-profiles/autocomplete Autocomplete suggestions for marketplace search #
GET /organization-profiles/filter-metadata Get available filter options for marketplace search sidebar #
GET /organization-profiles/{slug} Get a single organization profile by slug (SEO page) Public endpoint #
GET /organization-profiles/{slug}/slots #
GET /organization-profiles/{slug}/nearby Get nearby venues in the same city as the given organization. Public endpoint #
GET /organization-profiles/{slug}/phone Reveal full phone number for an organization profile. Rate-limited public endpoint to prevent scraping #
POST /organization-profiles/{slug}/events Count a visitor's interest in a venue: a page open, a messenger tap or a directions tap #
GET /org/organization-profile Get or create profile for authenticated organization owner Protected endpoint (staff only) #
PUT /org/organization-profile Update organization profile Protected endpoint (staff only) #
POST /org/organization-profile/toggle-publish Publish/unpublish profile Protected endpoint (staff only) #
POST /org/organization-profile/update-stats Update profile stats manually Protected endpoint (staff only) #
POST /partner/validate-code POST /api/partner/validate-code #
POST /payment/payme/callback #
GET /org/payments/appointments/{appointment} List payment transactions for an appointment #
POST /org/payments/appointments/{appointment}/initiate Initiate a payment for an appointment #
GET /org/payments/appointments/{appointment}/requirement Get payment requirement for an appointment #
GET /org/payments/{transaction} Get the status of a specific payment transaction #
POST /org/payments/{transaction}/refund Request a refund for a payment transaction #
GET /platform/sales-contact Who to send a business to for a conversation with a manager #
POST /referrals/validate-code Validate a referral code (public endpoint for the onboarding form) #
GET /org/referrals List referrals made by the current organization (as referrer) #
GET /org/referrals/stats Get referral statistics for the current organization #
GET /org/referrals/incoming Get the incoming referral (where the current org was referred) #
POST /org/referrals/regenerate-code Regenerate the referral code for the current organization #
GET /org/reviews List reviews for the organization with filtering and sorting #
GET /org/reviews/stats Get review statistics for the organization #
GET /org/reviews/trend Get review trend data for the organization #
GET /org/reviews/{review} Get a single review #
PUT /org/reviews/{review} Update review moderation (visibility) #
POST /org/reviews/{review}/reply Reply to a review #
POST /org/reviews/{review}/report Report a review #
GET /seo/sitemap-index Sitemap index: every locale sitemap file and how many URLs it holds #
GET /seo/sitemap-urls One sitemap page of indexable venue URLs for a locale #
GET /org/services/categories Get categories for services #
GET /org/services #
POST /org/services #
GET /org/services/{service} #
PUT /org/services/{service} #
DELETE /org/services/{service} #
GET /org/services/{service}/staff Get staff that can perform this service #
POST /org/services/{service}/staff Assign staff to service #
POST /org/services/batch-update Batch update services #
POST /org/services/batch-destroy Batch delete services #
GET /org/settings #
PUT /org/settings #
GET /org/setup-status Get the organization's completeness status #
GET /short-links/{code} #
GET /org/staff #
POST /org/staff #
GET /org/staff/{staff} #
PUT /org/staff/{staff} #
DELETE /org/staff/{staff} #
GET /org/staff/{staff}/availability Get availability for a staff member #
PUT /org/staff/{staff}/availability Update availability for a staff member #
GET /org/staff/{staff}/services Get services assigned to this staff member #
POST /org/staff/{staff}/services #
POST /org/staff/batch-update Batch update staff members #
POST /org/staff/batch-destroy Batch delete staff members #
POST /org/staff/{staff}/deactivate Deactivate a staff member #
POST /org/staff/{staff}/activate Activate a staff member #
POST /org/auth/login Login: - If auth_method=password -> verify password -> return token. - If auth_method=code -> generate code -> return "code sent" #
POST /org/auth/register Register: - Creates user. - If auth_method=password -> sets password. - ALWAYS sends code for verification (even if password set) #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/bronjoy-com:bronjoy-api-spec-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

bronjoy-com-api-spec-openapi.json Raw ↑
{"openapi":"3.1.0","info":{"title":"Bronjoy","version":"0.0.1"},"servers":[{"url":"https://api.bronjoy.com/api"}],"paths":{"/org/announcements":{"get":{"operationId":"announcements.index","summary":"List announcements for the organization","tags":["Announcement"],"parameters":[{"name":"active","in":"query","schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"Paginated set of `AnnouncementResource`","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AnnouncementResource"}},"links":{"type":"object","properties":{"first":{"type":["string","null"]},"last":{"type":["string","null"]},"prev":{"type":["string","null"]},"next":{"type":["string","null"]}},"required":["first","last","prev","next"]},"meta":{"type":"object","properties":{"current_page":{"type":"integer"},"from":{"type":["integer","null"]},"last_page":{"type":"integer"},"links":{"type":"array","description":"Generated paginator links.","items":{"type":"object","properties":{"url":{"type":["string","null"]},"label":{"type":"string"},"active":{"type":"boolean"}},"required":["url","label","active"]}},"path":{"type":["string","null"],"description":"Base path for paginator generated URLs."},"per_page":{"type":"integer","description":"Number of items shown per page."},"to":{"type":["integer","null"],"description":"Number of the last item in the slice."},"total":{"type":"integer","description":"Total number of items being paginated."}},"required":["current_page","from","last_page","links","path","per_page","to","total"]}},"required":["data","links","meta"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"announcements.store","summary":"Create a new announcement","tags":["Announcement"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAnnouncementRequest"}}}},"responses":{"200":{"description":"`AnnouncementResource`","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AnnouncementResource"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/org/announcements/{announcement}":{"get":{"operationId":"announcements.show","summary":"Get a single announcement","tags":["Announcement"],"parameters":[{"name":"announcement","in":"path","required":true,"description":"The announcement ID","schema":{"type":"integer"}}],"responses":{"200":{"description":"`AnnouncementResource`","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AnnouncementResource"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"}}},"put":{"operationId":"announcements.update","summary":"Update an announcement","tags":["Announcement"],"parameters":[{"name":"announcement","in":"path","required":true,"description":"The announcement ID","schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAnnouncementRequest"}}}},"responses":{"200":{"description":"`AnnouncementResource`","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AnnouncementResource"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"422":{"$ref":"#/components/responses/ValidationException"}}},"delete":{"operationId":"announcements.destroy","summary":"Delete an announcement","tags":["Announcement"],"parameters":[{"name":"announcement","in":"path","required":true,"description":"The announcement ID","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","enum":["Announcement deleted"]}},"required":["message"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"}}}},"/org/appointments":{"get":{"operationId":"appointment.index","tags":["Appointment"],"parameters":[{"name":"status[]","in":"query","schema":{"type":"array","enum":["pending","confirmed","completed","cancelled","no_show"],"items":{"type":"string","enum":["pending","confirmed","completed","cancelled","no_show"]}}},{"name":"payment_status[]","in":"query","schema":{"type":"array","enum":["unpaid","paid","refunded"],"items":{"type":"string","enum":["unpaid","paid","refunded"]}}},{"name":"staff_id[]","in":"query","schema":{"type":"array","items":{"type":"integer"}}},{"name":"service_id[]","in":"query","schema":{"type":"array","items":{"type":"integer"}}},{"name":"search","in":"query","schema":{"type":["string","null"],"maxLength":255}},{"name":"from","in":"query","schema":{"type":["string","null"],"format":"date-time"}},{"name":"to","in":"query","schema":{"type":["string","null"],"format":"date-time"}},{"name":"my_appointments","in":"query","schema":{"type":["boolean","null"]}},{"name":"page","in":"query","schema":{"type":["integer","null"],"minimum":1}},{"name":"per_page","in":"query","schema":{"type":["integer","null"],"minimum":1,"maximum":1000}},{"name":"sort_by","in":"query","schema":{"type":["string","null"],"enum":["id","start_time","status","payment_status","created_at"]}},{"name":"sort_order","in":"query","schema":{"type":["string","null"],"enum":["asc","desc"]}}],"responses":{"200":{"description":"Paginated set of `AppointmentResource`","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AppointmentResource"}},"links":{"type":"object","properties":{"first":{"type":["string","null"]},"last":{"type":["string","null"]},"prev":{"type":["string","null"]},"next":{"type":["string","null"]}},"required":["first","last","prev","next"]},"meta":{"type":"object","properties":{"current_page":{"type":"integer"},"from":{"type":["integer","null"]},"last_page":{"type":"integer"},"links":{"type":"array","description":"Generated paginator links.","items":{"type":"object","properties":{"url":{"type":["string","null"]},"label":{"type":"string"},"active":{"type":"boolean"}},"required":["url","label","active"]}},"path":{"type":["string","null"],"description":"Base path for paginator generated URLs."},"per_page":{"type":"integer","description":"Number of items shown per page."},"to":{"type":["integer","null"],"description":"Number of the last item in the slice."},"total":{"type":"integer","description":"Total number of items being paginated."}},"required":["current_page","from","last_page","links","path","per_page","to","total"]}},"required":["data","links","meta"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"422":{"$ref":"#/components/responses/ValidationException"}}},"post":{"operationId":"appointment.store","tags":["Appointment"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoreAppointmentRequest"}}}},"responses":{"200":{"description":"`AppointmentResource`","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AppointmentResource"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/org/appointments/pending":{"get":{"operationId":"appointment.pending","tags":["Appointment"],"parameters":[{"name":"per_page","in":"query","schema":{"type":"string","default":20}}],"responses":{"200":{"description":"Paginated set of `AppointmentResource`","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AppointmentResource"}},"links":{"type":"object","properties":{"first":{"type":["string","null"]},"last":{"type":["string","null"]},"prev":{"type":["string","null"]},"next":{"type":["string","null"]}},"required":["first","last","prev","next"]},"meta":{"type":"object","properties":{"current_page":{"type":"integer"},"from":{"type":["integer","null"]},"last_page":{"type":"integer"},"links":{"type":"array","description":"Generated paginator links.","items":{"type":"object","properties":{"url":{"type":["string","null"]},"label":{"type":"string"},"active":{"type":"boolean"}},"required":["url","label","active"]}},"path":{"type":["string","null"],"description":"Base path for paginator generated URLs."},"per_page":{"type":"integer","description":"Number of items shown per page."},"to":{"type":["integer","null"],"description":"Number of the last item in the slice."},"total":{"type":"integer","description":"Total number of items being paginated."}},"required":["current_page","from","last_page","links","path","per_page","to","total"]}},"required":["data","links","meta"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/org/appointments/calendar":{"get":{"operationId":"appointment.calendar","tags":["Appointment"],"parameters":[{"name":"start","in":"query","required":true,"schema":{"type":"string","format":"date-time"}},{"name":"end","in":"query","required":true,"schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"Array of `AppointmentResource`","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AppointmentResource"}}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/org/appointments/batch-update":{"post":{"operationId":"appointment.batchUpdate","tags":["Appointment"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchUpdateAppointmentsRequest"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"updated_count":{"type":"string"}},"required":["message","updated_count"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/org/appointments/batch-destroy":{"post":{"operationId":"appointment.batchDestroy","tags":["Appointment"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchDestroyAppointmentsRequest"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"deleted_count":{"type":"string"}},"required":["message","deleted_count"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/org/appointments/{appointment}":{"get":{"operationId":"appointment.show","tags":["Appointment"],"parameters":[{"name":"appointment","in":"path","required":true,"description":"The appointment ID","schema":{"type":"integer"}}],"responses":{"200":{"description":"`AppointmentResource`","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AppointmentResource"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"}}},"put":{"operationId":"appointment.update","tags":["Appointment"],"parameters":[{"name":"appointment","in":"path","required":true,"description":"The appointment ID","schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAppointmentRequest"}}}},"responses":{"200":{"description":"`AppointmentResource`","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AppointmentResource"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"422":{"$ref":"#/components/responses/ValidationException"}}},"delete":{"operationId":"appointment.destroy","tags":["Appointment"],"parameters":[{"name":"appointment","in":"path","required":true,"description":"The appointment ID","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","enum":["Appointment deleted"]}},"required":["message"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"}}}},"/org/appointments/{appointment}/submit":{"post":{"operationId":"appointment.submit","tags":["Appointment"],"parameters":[{"name":"appointment","in":"path","required":true,"description":"The appointment ID","schema":{"type":"integer"}}],"responses":{"200":{"description":"`AppointmentResource`","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AppointmentResource"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"}}}},"/org/appointments/{appointment}/cancel":{"post":{"operationId":"appointment.cancel","tags":["Appointment"],"parameters":[{"name":"appointment","in":"path","required":true,"description":"The appointment ID","schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelAppointmentRequest"}}}},"responses":{"200":{"description":"`AppointmentResource`","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AppointmentResource"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/org/appointments/{appointment}/reschedule":{"post":{"operationId":"appointment.reschedule","tags":["Appointment"],"parameters":[{"name":"appointment","in":"path","required":true,"description":"The appointment ID","schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RescheduleAppointmentRequest"}}}},"responses":{"200":{"description":"`AppointmentResource`","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AppointmentResource"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/org/schedule":{"get":{"operationId":"availability.schedule","summary":"Get unified schedule - both recurring availabilities and one-time exceptions\nSupports my_schedule=true to filter to current user's schedule only","tags":["Availability"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"string"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"availability.store","summary":"Store a new schedule entry","tags":["Availability"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StoreScheduleEntryRequest"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"is_recurring":{"type":"boolean"}},"required":["id","is_recurring"]},"message":{"type":"string","enum":["One-time availability created"]}},"required":["data","message"]},{"type":"object","properties":{"data":{"type":"array","prefixItems":[{"type":"object","properties":{"id":{"type":"string"},"is_recurring":{"type":"boolean"}},"required":["id","is_recurring"]}],"minItems":1,"maxItems":1,"additionalItems":false},"message":{"type":"string","enum":["Recurring availability created"]}},"required":["data","message"]}]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"403":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]}}}},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/org/schedule/{id}":{"put":{"operationId":"availability.update","summary":"Update a schedule entry","tags":["Availability"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateScheduleEntryRequest"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"message":{"type":"string","enum":["Exception updated"]},"data":{"type":"object","properties":{"id":{"type":"integer"},"is_recurring":{"type":"boolean"}},"required":["id","is_recurring"]}},"required":["message","data"]},{"type":"object","properties":{"message":{"type":"string","enum":["Availability updated"]},"data":{"type":"object","properties":{"id":{"type":"integer"},"is_recurring":{"type":"boolean"}},"required":["id","is_recurring"]}},"required":["message","data"]},{"type":"object","properties":{"message":{"type":"string","enum":["Converted to recurring availability"]},"data":{"type":"object","properties":{"id":{"type":"string"},"is_recurring":{"type":"boolean"}},"required":["id","is_recurring"]}},"required":["message","data"]},{"type":"object","properties":{"message":{"type":"string","enum":["Converted to one-time availability"]},"data":{"type":"object","properties":{"id":{"type":"string"},"is_recurring":{"type":"boolean"}},"required":["id","is_recurring"]}},"required":["message","data"]}]}}}},"400":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["day_of_week is required for recurring entries"]}},"required":["error"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"403":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]}}}},"404":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","enum":["Entry not found"]}},"required":["message"]}}}},"422":{"$ref":"#/components/responses/ValidationException"}}},"delete":{"operationId":"availability.destroy","summary":"Delete a schedule entry","tags":["Availability"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}},{"name":"is_recurring","in":"query","required":true,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","enum":["Availability deleted","Exception deleted"]}},"required":["message"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"403":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]}}}},"404":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","enum":["Entry not found"]}},"required":["message"]}}}},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/org/availability":{"get":{"operationId":"availability.index","tags":["Availability"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"string"}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"put":{"operationId":"availability.legacyUpdate","tags":["Availability"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"availabilities":{"type":"array","items":{"type":"object","properties":{"staff_id":{"type":["integer","null"]},"service_id":{"type":["integer","null"]},"day_of_week":{"type":"integer","minimum":0,"maximum":6},"start_time":{"type":"string"},"end_time":{"type":"string"},"is_available":{"type":"boolean"}},"required":["day_of_week","start_time","end_time"]}}},"required":["availabilities"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","prefixItems":[{"type":"string"}],"minItems":1,"maxItems":1,"additionalItems":false},"message":{"type":"string","enum":["Availability updated"]}},"required":["data","message"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/org/availability/exceptions":{"get":{"operationId":"availability.exceptions","tags":["Availability"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"string"}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"availability.storeException","tags":["Availability"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"staff_id":{"type":["integer","null"]},"service_id":{"type":["integer","null"]},"date":{"type":"string","format":"date"},"start_time":{"type":["string","null"]},"end_time":{"type":["string","null"]},"is_available":{"type":"boolean"},"reason":{"type":["string","null"],"maxLength":255}},"required":["date"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"string"}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/organization-profiles/{slug}/booking-requests":{"post":{"operationId":"bookingRequest.store","description":"Unclaimed venues only: Bronjoy passes the request on to the venue by\nphone. A claimed venue answers 409 — it has real online booking. The same\nphone asking for the same venue again within 24 hours gets the first\nrequest back (200) instead of a duplicate.\n\nPublic endpoint, published profiles only. Guarded by Turnstile (when\nconfigured) and the `booking-request` limiter.","summary":"Leave a booking request on a venue that takes no online bookings yet","tags":["BookingRequest"],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitBookingRequestRequest"}}}},"responses":{"201":{"description":"201 for a new request; 200 when a duplicate returns the first one.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer"},"status":{"type":"string","enum":["new","forwarded","booked","no_answer","declined","spam"]},"telegram_ref":{"type":"string"}},"required":["id","status","telegram_ref"]}}}},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/org/branches":{"get":{"operationId":"branches.index","tags":["Branch"],"responses":{"200":{"description":"Paginated set of `BranchResource`","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BranchResource"}},"links":{"type":"object","properties":{"first":{"type":["string","null"]},"last":{"type":["string","null"]},"prev":{"type":["string","null"]},"next":{"type":["string","null"]}},"required":["first","last","prev","next"]},"meta":{"type":"object","properties":{"current_page":{"type":"integer"},"from":{"type":["integer","null"]},"last_page":{"type":"integer"},"links":{"type":"array","description":"Generated paginator links.","items":{"type":"object","properties":{"url":{"type":["string","null"]},"label":{"type":"string"},"active":{"type":"boolean"}},"required":["url","label","active"]}},"path":{"type":["string","null"],"description":"Base path for paginator generated URLs."},"per_page":{"type":"integer","description":"Number of items shown per page."},"to":{"type":["integer","null"],"description":"Number of the last item in the slice."},"total":{"type":"integer","description":"Total number of items being paginated."}},"required":["current_page","from","last_page","links","path","per_page","to","total"]}},"required":["data","links","meta"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"branches.store","tags":["Branch"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBranchRequest"}}}},"responses":{"200":{"description":"`BranchResource`","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/BranchResource"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"403":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"upgrade_required":{"type":"boolean"}},"required":["message","upgrade_required"]}}}},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/org/branches/{branch}":{"get":{"operationId":"branches.show","tags":["Branch"],"parameters":[{"name":"branch","in":"path","required":true,"description":"The branch ID","schema":{"type":"integer"}}],"responses":{"200":{"description":"`BranchResource`","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/BranchResource"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"}}},"put":{"operationId":"branches.update","tags":["Branch"],"parameters":[{"name":"branch","in":"path","required":true,"description":"The branch ID","schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateBranchRequest"}}}},"responses":{"200":{"description":"`BranchResource`","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/BranchResource"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"422":{"$ref":"#/components/responses/ValidationException"}}},"delete":{"operationId":"branches.destroy","tags":["Branch"],"parameters":[{"name":"branch","in":"path","required":true,"description":"The branch ID","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","enum":["Deleted"]}},"required":["message"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"}}}},"/calendar/callback/google":{"get":{"operationId":"calendar.callback.google","summary":"Handle Google OAuth callback.\nExchanges authorization code for tokens and redirects back to frontend","tags":["CalendarCallback"],"parameters":[{"name":"code","in":"query","schema":{"type":"string"}},{"name":"state","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/org/calendar/connections":{"get":{"operationId":"calendarConnection.index","summary":"List the current staff member's calendar connections","tags":["CalendarConnection"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CalendarConnectionResource"}}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"404":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","enum":["Staff profile not found."]}},"required":["message"]}}}}}}},"/org/calendar/auth-url":{"get":{"operationId":"calendarConnection.getAuthUrl","summary":"Generate the OAuth authorization URL for connecting a provider","tags":["CalendarConnection"],"parameters":[{"name":"provider","in":"query","schema":{"type":"string","default":"google"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":["string","null"]}},"required":["url"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"404":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","enum":["Staff profile not found."]}},"required":["message"]}}}},"422":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","enum":["Unsupported calendar provider."]}},"required":["message"]}}}}}}},"/org/calendar/feed-url":{"get":{"operationId":"calendarConnection.getFeedUrl","summary":"Get the iCal feed URL for the current staff member","tags":["CalendarConnection"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"feed_url":{"type":"string"}},"required":["feed_url"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"404":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","enum":["Staff profile not found."]}},"required":["message"]}}}}}}},"/org/calendar/connections/{connection}":{"delete":{"operationId":"calendarConnection.disconnect","summary":"Disconnect a calendar connection and revoke tokens","tags":["CalendarConnection"],"parameters":[{"name":"connection","in":"path","required":true,"description":"The connection ID","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","enum":["Calendar disconnected."]}},"required":["message"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"403":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","enum":["Not authorized."]}},"required":["message"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"}}}},"/org/calendar/connections/{connection}/toggle":{"patch":{"operationId":"calendarConnection.toggleSync","summary":"Toggle push or pull sync for a connection","tags":["CalendarConnection"],"parameters":[{"name":"connection","in":"path","required":true,"description":"The connection ID","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CalendarConnectionResource"},"message":{"type":"string","enum":["Sync settings updated."]}},"required":["data","message"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"403":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","enum":["Not authorized."]}},"required":["message"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"}}}},"/org/calendar/connections/{connection}/sync":{"post":{"operationId":"calendarConnection.syncNow","summary":"Trigger an immediate sync for a calendar connection","tags":["CalendarConnection"],"parameters":[{"name":"connection","in":"path","required":true,"description":"The connection ID","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","enum":["Sync queued."]}},"required":["message"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"},"403":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","enum":["Not authorized."]}},"required":["message"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"}}}},"/calendar/feed/{token}.ics":{"get":{"operationId":"calendar.feed","summary":"Serve the iCal feed for a staff member.\nThe token in the URL authenticates the request — no login needed","tags":["CalendarFeed"],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"text/calendar; charset=utf-8":{"schema":{"type":"string"}}},"headers":{"Content-Disposition":{"schema":{"type":"string","enum":["attachment; filename=\"bronjoy-calendar.ics\""]}},"Cache-Control":{"schema":{"type":"string","enum":["no-cache, must-revalidate"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"}}}},"/categories":{"get":{"operationId":

# --- truncated at 32 KB (375 KB total) ---
# Full source: https://artifacts.apis.io/bronjoy-com/openapi/_original/bronjoy-com-api-spec-openapi.json