A bookable offer — the universal unit of configuration.
One install can mix services with entirely different booking shapes; the shape is per-service config, never a module-level setting:
time_unit: "minutes"— timed bookings.flexible_duration: falsegives fixed slots on aslot_intervalgrid (massage on the hour);flexible_duration: truegives a free-form range picker bounded bymin_duration/max_duration(max_duration: nil= unbounded — gym).time_unit: "day"/"night"— date-range bookings (hotel).nightdiffers fromdayonly in presentation/pricing semantics (checkout is the exclusive end date in both).min_stay/max_staybound the range;checkin_time/checkout_timeare display attributes, never part of the availability math.
seats is pooled capacity per slot/date (1 = exclusive use — a single
massage table or 1 room; 7 = seven rooms of this type; 20 = gym floor).
signup_policy gates the public flow ("anyone" allows guest bookings,
"login_required" requires an authenticated user); require_approval
makes new bookings start as pending (RSVP-style) instead of instantly
confirmed.
Soft-delete: status: "trashed" sentinel (workspace convention);
"inactive" hides the service from the public page while keeping it
bookable by admins.
Summary
Functions
Status transitions are controlled — never cast from attrs.
Types
@type t() :: %PhoenixKitBookings.Schemas.Service{ __meta__: term(), availability_rules: term(), buffer_after: term(), buffer_before: term(), cancel_notice: term(), checkin_time: term(), checkout_time: term(), currency: term(), description: term(), duration: term(), flexible_duration: term(), inserted_at: term(), max_advance: term(), max_duration: term(), max_stay: term(), min_duration: term(), min_notice: term(), min_stay: term(), name: term(), owner_uuid: term(), price: term(), price_per: term(), provider_uuid: term(), reminder_minutes: term(), require_approval: term(), seats: term(), settings: term(), signup_policy: term(), slot_interval: term(), slug: term(), status: term(), time_unit: term(), updated_at: term(), uuid: term() }