mirror of
https://github.com/Dvorinka/Bookra.git
synced 2026-06-03 20:13:00 +00:00
701 lines
17 KiB
YAML
701 lines
17 KiB
YAML
openapi: 3.0.3
|
|
info:
|
|
title: Bookra API
|
|
version: 0.1.0
|
|
description: >
|
|
Remote-first booking API for Bookra. The Go backend owns business rules,
|
|
scheduling logic, tenant isolation, and Paddle-backed plan enforcement.
|
|
servers:
|
|
- url: http://localhost:8080
|
|
tags:
|
|
- name: Health
|
|
- name: Public Booking
|
|
- name: Dashboard
|
|
- name: Tenant
|
|
- name: Billing
|
|
- name: Jobs
|
|
paths:
|
|
/healthz:
|
|
get:
|
|
tags: [Health]
|
|
operationId: getHealth
|
|
responses:
|
|
"200":
|
|
description: Service health
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/HealthResponse"
|
|
/v1/meta/config:
|
|
get:
|
|
tags: [Health]
|
|
operationId: getPublicConfig
|
|
responses:
|
|
"200":
|
|
description: Public runtime configuration
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/PublicConfig"
|
|
/v1/public/tenants/{tenantSlug}/availability:
|
|
get:
|
|
tags: [Public Booking]
|
|
operationId: getPublicAvailability
|
|
parameters:
|
|
- in: path
|
|
name: tenantSlug
|
|
required: true
|
|
schema:
|
|
type: string
|
|
responses:
|
|
"200":
|
|
description: Availability for a tenant booking page
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/PublicAvailabilityResponse"
|
|
/v1/public/bookings:
|
|
post:
|
|
tags: [Public Booking]
|
|
operationId: createBooking
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/CreateBookingRequest"
|
|
responses:
|
|
"201":
|
|
description: Booking created
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/CreateBookingResponse"
|
|
/v1/dashboard/summary:
|
|
get:
|
|
tags: [Dashboard]
|
|
operationId: getDashboardSummary
|
|
security:
|
|
- bearerAuth: []
|
|
responses:
|
|
"200":
|
|
description: Tenant dashboard summary
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/DashboardSummary"
|
|
"401":
|
|
description: Unauthorized
|
|
/v1/tenants/bootstrap:
|
|
get:
|
|
tags: [Tenant]
|
|
operationId: getTenantBootstrap
|
|
security:
|
|
- bearerAuth: []
|
|
responses:
|
|
"200":
|
|
description: Tenant bootstrap payload for the authenticated user
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/TenantBootstrap"
|
|
"401":
|
|
description: Unauthorized
|
|
/v1/tenants/onboard:
|
|
post:
|
|
tags: [Tenant]
|
|
operationId: onboardTenant
|
|
security:
|
|
- bearerAuth: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/OnboardTenantRequest"
|
|
responses:
|
|
"201":
|
|
description: Tenant created for authenticated user
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/TenantBootstrap"
|
|
"400":
|
|
description: Invalid request
|
|
"401":
|
|
description: Unauthorized
|
|
"409":
|
|
description: Conflict
|
|
/v1/billing/subscription:
|
|
get:
|
|
tags: [Billing]
|
|
operationId: getSubscriptionSnapshot
|
|
security:
|
|
- bearerAuth: []
|
|
responses:
|
|
"200":
|
|
description: Current subscription snapshot and entitlements
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/SubscriptionSnapshot"
|
|
"401":
|
|
description: Unauthorized
|
|
/v1/billing/checkout:
|
|
post:
|
|
tags: [Billing]
|
|
operationId: createBillingCheckout
|
|
security:
|
|
- bearerAuth: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/CheckoutSessionRequest"
|
|
responses:
|
|
"200":
|
|
description: Paddle checkout launch payload
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/CheckoutLaunchResponse"
|
|
"400":
|
|
description: Invalid request
|
|
"401":
|
|
description: Unauthorized
|
|
"503":
|
|
description: Billing provider unavailable
|
|
/v1/billing/refresh:
|
|
post:
|
|
tags: [Billing]
|
|
operationId: refreshSubscriptionSnapshot
|
|
security:
|
|
- bearerAuth: []
|
|
responses:
|
|
"200":
|
|
description: Refreshed snapshot
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/SubscriptionSnapshot"
|
|
"401":
|
|
description: Unauthorized
|
|
"503":
|
|
description: Billing provider unavailable
|
|
/v1/billing/portal:
|
|
post:
|
|
tags: [Billing]
|
|
operationId: createBillingPortalSession
|
|
security:
|
|
- bearerAuth: []
|
|
responses:
|
|
"200":
|
|
description: Paddle customer portal session
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/PortalSessionResponse"
|
|
"400":
|
|
description: Billing customer missing
|
|
"401":
|
|
description: Unauthorized
|
|
"503":
|
|
description: Billing provider unavailable
|
|
/v1/webhooks/paddle:
|
|
post:
|
|
tags: [Billing]
|
|
operationId: handlePaddleWebhook
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
responses:
|
|
"200":
|
|
description: Webhook accepted
|
|
/v1/internal/jobs/reminders/dispatch:
|
|
post:
|
|
tags: [Jobs]
|
|
operationId: dispatchReminderJobs
|
|
security:
|
|
- jobRunnerKey: []
|
|
requestBody:
|
|
required: false
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/DispatchReminderJobsRequest"
|
|
responses:
|
|
"200":
|
|
description: Reminder jobs dispatched
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/DispatchReminderJobsResponse"
|
|
"401":
|
|
description: Unauthorized
|
|
components:
|
|
securitySchemes:
|
|
bearerAuth:
|
|
type: http
|
|
scheme: bearer
|
|
bearerFormat: JWT
|
|
jobRunnerKey:
|
|
type: apiKey
|
|
in: header
|
|
name: X-Bookra-Job-Key
|
|
schemas:
|
|
HealthResponse:
|
|
type: object
|
|
required: [status, environment]
|
|
properties:
|
|
status:
|
|
type: string
|
|
example: ok
|
|
environment:
|
|
type: string
|
|
example: staging
|
|
databaseConfigured:
|
|
type: boolean
|
|
PublicConfig:
|
|
type: object
|
|
required: [environment, neonAuthEnabled, apiUrl, demoMode]
|
|
properties:
|
|
environment:
|
|
type: string
|
|
neonAuthEnabled:
|
|
type: boolean
|
|
apiUrl:
|
|
type: string
|
|
format: uri
|
|
demoMode:
|
|
type: boolean
|
|
TimeSlot:
|
|
type: object
|
|
required: [startsAt, endsAt, mode]
|
|
properties:
|
|
serviceId:
|
|
type: string
|
|
format: uuid
|
|
nullable: true
|
|
classSessionId:
|
|
type: string
|
|
format: uuid
|
|
nullable: true
|
|
staffId:
|
|
type: string
|
|
format: uuid
|
|
nullable: true
|
|
locationId:
|
|
type: string
|
|
format: uuid
|
|
nullable: true
|
|
startsAt:
|
|
type: string
|
|
format: date-time
|
|
endsAt:
|
|
type: string
|
|
format: date-time
|
|
mode:
|
|
type: string
|
|
enum: [appointment, class]
|
|
label:
|
|
type: string
|
|
remainingCapacity:
|
|
type: integer
|
|
nullable: true
|
|
PublicAvailabilityResponse:
|
|
type: object
|
|
required: [tenantSlug, timezone, locale, slots]
|
|
properties:
|
|
tenantSlug:
|
|
type: string
|
|
timezone:
|
|
type: string
|
|
locale:
|
|
type: string
|
|
enum: [cs, en]
|
|
slots:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/TimeSlot"
|
|
CreateBookingRequest:
|
|
type: object
|
|
required: [tenantSlug, bookingMode, customerName, customerEmail, startsAt, endsAt]
|
|
properties:
|
|
tenantSlug:
|
|
type: string
|
|
bookingMode:
|
|
type: string
|
|
enum: [appointment, class]
|
|
serviceId:
|
|
type: string
|
|
format: uuid
|
|
classSessionId:
|
|
type: string
|
|
format: uuid
|
|
staffId:
|
|
type: string
|
|
format: uuid
|
|
locationId:
|
|
type: string
|
|
format: uuid
|
|
customerName:
|
|
type: string
|
|
customerEmail:
|
|
type: string
|
|
format: email
|
|
notes:
|
|
type: string
|
|
startsAt:
|
|
type: string
|
|
format: date-time
|
|
endsAt:
|
|
type: string
|
|
format: date-time
|
|
CreateBookingResponse:
|
|
type: object
|
|
required: [bookingId, reference, status]
|
|
properties:
|
|
bookingId:
|
|
type: string
|
|
format: uuid
|
|
reference:
|
|
type: string
|
|
status:
|
|
type: string
|
|
enum: [confirmed, waitlisted]
|
|
DashboardKPI:
|
|
type: object
|
|
required: [code, label, value]
|
|
properties:
|
|
code:
|
|
type: string
|
|
label:
|
|
type: string
|
|
value:
|
|
type: string
|
|
DashboardSummary:
|
|
type: object
|
|
required: [tenantName, tenantSlug, locale, timezone, planCode, publicBookingUrl, setupCompletion, kpis]
|
|
properties:
|
|
tenantName:
|
|
type: string
|
|
tenantSlug:
|
|
type: string
|
|
locale:
|
|
type: string
|
|
timezone:
|
|
type: string
|
|
planCode:
|
|
type: string
|
|
publicBookingUrl:
|
|
type: string
|
|
setupCompletion:
|
|
type: integer
|
|
kpis:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/DashboardKPI"
|
|
upcomingBookings:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/UpcomingBooking"
|
|
widgetSnippets:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/WidgetSnippet"
|
|
tracking:
|
|
$ref: "#/components/schemas/TrackingStatus"
|
|
UpcomingBooking:
|
|
type: object
|
|
properties:
|
|
reference:
|
|
type: string
|
|
customerName:
|
|
type: string
|
|
customerEmail:
|
|
type: string
|
|
startsAt:
|
|
type: string
|
|
format: date-time
|
|
endsAt:
|
|
type: string
|
|
format: date-time
|
|
status:
|
|
type: string
|
|
label:
|
|
type: string
|
|
WidgetSnippet:
|
|
type: object
|
|
properties:
|
|
kind:
|
|
type: string
|
|
code:
|
|
type: string
|
|
TrackingStatus:
|
|
type: object
|
|
properties:
|
|
provider:
|
|
type: string
|
|
connected:
|
|
type: boolean
|
|
siteId:
|
|
type: string
|
|
message:
|
|
type: string
|
|
BrandProfile:
|
|
type: object
|
|
properties:
|
|
name:
|
|
type: string
|
|
siteUrl:
|
|
type: string
|
|
logoUrl:
|
|
type: string
|
|
primaryColor:
|
|
type: string
|
|
TenantBootstrap:
|
|
type: object
|
|
required: [tenantId, tenantName, preset, locale, timezone, currentUser]
|
|
properties:
|
|
tenantId:
|
|
type: string
|
|
format: uuid
|
|
tenantName:
|
|
type: string
|
|
tenantSlug:
|
|
type: string
|
|
preset:
|
|
type: string
|
|
locale:
|
|
type: string
|
|
timezone:
|
|
type: string
|
|
planCode:
|
|
type: string
|
|
onboardingCompleted:
|
|
type: boolean
|
|
brand:
|
|
$ref: "#/components/schemas/BrandProfile"
|
|
currentUser:
|
|
type: object
|
|
required: [subject, role]
|
|
properties:
|
|
subject:
|
|
type: string
|
|
email:
|
|
type: string
|
|
format: email
|
|
name:
|
|
type: string
|
|
role:
|
|
type: string
|
|
OnboardTenantRequest:
|
|
type: object
|
|
required: [name, slug, preset, locale, timezone]
|
|
properties:
|
|
name:
|
|
type: string
|
|
slug:
|
|
type: string
|
|
preset:
|
|
type: string
|
|
enum: [salon, clinic, massage, repair, studio]
|
|
locale:
|
|
type: string
|
|
enum: [cs, en]
|
|
timezone:
|
|
type: string
|
|
brand:
|
|
$ref: "#/components/schemas/BrandProfile"
|
|
locationName:
|
|
type: string
|
|
bookingDefaults:
|
|
$ref: "#/components/schemas/BookingDefaultsRequest"
|
|
availabilityBlocks:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/AvailabilityBlockRequest"
|
|
teamInvites:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/TeamInviteRequest"
|
|
BookingDefaultsRequest:
|
|
type: object
|
|
properties:
|
|
serviceName:
|
|
type: string
|
|
durationMinutes:
|
|
type: integer
|
|
bufferBeforeMinutes:
|
|
type: integer
|
|
bufferAfterMinutes:
|
|
type: integer
|
|
cancelWindowHours:
|
|
type: integer
|
|
AvailabilityBlockRequest:
|
|
type: object
|
|
required: [dayOfWeek, startsLocal, endsLocal]
|
|
properties:
|
|
dayOfWeek:
|
|
type: integer
|
|
minimum: 1
|
|
maximum: 7
|
|
startsLocal:
|
|
type: string
|
|
example: "09:00"
|
|
endsLocal:
|
|
type: string
|
|
example: "17:00"
|
|
busy:
|
|
type: boolean
|
|
TeamInviteRequest:
|
|
type: object
|
|
required: [email]
|
|
properties:
|
|
email:
|
|
type: string
|
|
format: email
|
|
role:
|
|
type: string
|
|
PlanEntitlements:
|
|
type: object
|
|
required: [maxLocations, maxStaff, emailReminders, advancedReporting, widgetEmbedding, umamiTracking]
|
|
properties:
|
|
maxLocations:
|
|
type: integer
|
|
maxStaff:
|
|
type: integer
|
|
emailReminders:
|
|
type: boolean
|
|
widgetEmbedding:
|
|
type: boolean
|
|
umamiTracking:
|
|
type: boolean
|
|
advancedReporting:
|
|
type: boolean
|
|
SubscriptionSnapshot:
|
|
type: object
|
|
required: [tenantId, provider, customerId, subscriptionId, status, planCode, currency, priceId, cancelAtPeriodEnd, entitlements, displayPrices, trialDays, checkoutUrlAvailable, syncAvailable, portalAvailable]
|
|
properties:
|
|
tenantId:
|
|
type: string
|
|
format: uuid
|
|
provider:
|
|
type: string
|
|
example: paddle
|
|
customerId:
|
|
type: string
|
|
subscriptionId:
|
|
type: string
|
|
status:
|
|
type: string
|
|
planCode:
|
|
type: string
|
|
currency:
|
|
type: string
|
|
enum: [czk, usd, eur]
|
|
priceId:
|
|
type: string
|
|
cancelAtPeriodEnd:
|
|
type: boolean
|
|
currentPeriodStart:
|
|
type: string
|
|
format: date-time
|
|
nullable: true
|
|
currentPeriodEnd:
|
|
type: string
|
|
format: date-time
|
|
nullable: true
|
|
paymentMethodBrand:
|
|
type: string
|
|
paymentMethodLast4:
|
|
type: string
|
|
entitlements:
|
|
$ref: "#/components/schemas/PlanEntitlements"
|
|
displayPrices:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/PlanDisplayPrice"
|
|
trialDays:
|
|
type: integer
|
|
lastSyncedAt:
|
|
type: string
|
|
format: date-time
|
|
nullable: true
|
|
checkoutUrlAvailable:
|
|
type: boolean
|
|
syncAvailable:
|
|
type: boolean
|
|
portalAvailable:
|
|
type: boolean
|
|
CheckoutSessionRequest:
|
|
type: object
|
|
required: [planCode]
|
|
properties:
|
|
planCode:
|
|
type: string
|
|
enum: [starter, pro, business]
|
|
currency:
|
|
type: string
|
|
enum: [czk, usd]
|
|
PlanDisplayPrice:
|
|
type: object
|
|
required: [currency, amountCents, formatted]
|
|
properties:
|
|
currency:
|
|
type: string
|
|
enum: [czk, usd]
|
|
amountCents:
|
|
type: integer
|
|
formatted:
|
|
type: string
|
|
CheckoutLaunchResponse:
|
|
type: object
|
|
required: [priceId, successRedirectUrl, cancelRedirectUrl, customData]
|
|
properties:
|
|
priceId:
|
|
type: string
|
|
customerId:
|
|
type: string
|
|
customerEmail:
|
|
type: string
|
|
format: email
|
|
successRedirectUrl:
|
|
type: string
|
|
format: uri
|
|
cancelRedirectUrl:
|
|
type: string
|
|
format: uri
|
|
customData:
|
|
type: object
|
|
additionalProperties:
|
|
type: string
|
|
PortalSessionResponse:
|
|
type: object
|
|
required: [url]
|
|
properties:
|
|
url:
|
|
type: string
|
|
format: uri
|
|
DispatchReminderJobsRequest:
|
|
type: object
|
|
properties:
|
|
limit:
|
|
type: integer
|
|
minimum: 1
|
|
maximum: 200
|
|
DispatchReminderJobsResponse:
|
|
type: object
|
|
required: [processedCount, sentCount, failedCount]
|
|
properties:
|
|
processedCount:
|
|
type: integer
|
|
sentCount:
|
|
type: integer
|
|
failedCount:
|
|
type: integer
|