- List services —
GET /storefront/products - Pick a staff member —
GET /storefront/staff-members - Fetch available time slots —
GET /storefront/products/{productId}/current-availabilities - Create an appointment —
POST /storefront/appointments - Add it to a cart —
POST /storefront/cartandPOST /storefront/cart/items - (Optional) Show and attach add-ons —
GET /storefront/products/{productId}/add-onsandPOST /storefront/cart/add-ons - (Optional) Apply a promo code —
POST /storefront/cart/discount-codeandDELETE /storefront/cart/discount-code - Check out —
POST /storefront/cart/checkout/startandPOST /storefront/cart/checkout/submit
Setup
Install the SDK in a Next.js project and initialize it once, for example in a small module imported by your server code:lib/opencals.ts
1
List services
ProductService.list() wraps GET /storefront/products and returns a paginated list of active services:2
Let the customer pick a staff member
If customers choose who they book with, The selected staff member’s
StaffMemberService.list() wraps GET /storefront/staff-members:id filters availability in the next step. The same pattern works for locations via LocationService.list() (GET /storefront/locations) on multi-location stores.3
Fetch available time slots
ProductService.getCurrentAvailabilities() wraps GET /storefront/products/{productId}/current-availabilities. It takes a required date (YYYY-MM-DD) plus optional staffMemberId, locationId, and timezone filters:For custom-duration products (
isCustomDuration: true, e.g. rentals or venue hire), use GET /storefront/products/{productId}/current-availability-ranges and show a date-range picker instead. There’s also GET /storefront/products/{productId}/nearest-availability for “next available slot” badges on service cards.4
Create the appointment
When the customer picks a slot, create an appointment with
POST /storefront/appointments. The required slot object carries the product and the UTC date/time range; staffMemberId and locationId are optional:5
Cart
Carts hold appointments between slot selection and payment, and they expire after a few minutes so abandoned selections release their slots. Cart endpoints identify the cart with the Persist
X-Cart-Id header:cart.id (for example in a cookie) so the customer keeps their cart across pages. If checkout takes a while, POST /storefront/cart/extend adds five minutes to the cart’s expiration.6
Show and attach add-ons (optional)
If the service has add-ons (extras like equipment rentals, upgrades, or accessories), fetch them and let the customer choose before proceeding to payment.When the customer selects an add-on, attach it to the cart item:Add-on pricing fields:
Skip this step if
addOns.data is empty — not every service has add-ons configured.7
Apply a promo code (optional)
If the store offers discount codes, let customers enter a promo code before checkout. Remove a code with
CartService.applyDiscountCode() wraps POST /storefront/cart/discount-code:DELETE /storefront/cart/discount-code:Reading applied discounts
Every cart-returning endpoint now includesappliedDiscounts[] and per-line-item discounts[] fields. Show a “You saved $X” summary by subtracting the cart’s total from the sum of original line prices:8
Checkout
Checkout is a three-call sequence against the same Save the customer so the order and appointments have contact details (guests don’t need an account):If the store has custom checkout questions, fetch them with The response includes the created
X-Cart-Id.Start validates the cart and initializes payment with your chosen provider (list configured providers with GET /storefront/payment/providers):GET /storefront/cart/checkout/questions/{language} and save answers via POST /storefront/cart/checkout/save-answers.Submit captures the payment and creates the order:order and, for new customers, an auth token pair you can use to sign them in automatically — see Authentication.If the email belongs to an existing customer and the request isn’t signed in as them, the booking is still added to that customer’s account, but auth is omitted and their saved profile (name, phone, marketing consent) isn’t changed. The billing and delivery addresses entered are added to the customer’s saved addresses once the order is placed; save-customer and checkout/start don’t write them. The customer sees the booking after signing in (for example with a login code). Always treat auth as optional.For pay-at-venue stores, start checkout with
provider: "cash" and submit directly with no payment confirmation step.After the booking
With a customer token you can power a full self-service portal:GET /storefront/appointments— list the customer’s appointmentsPUT /storefront/appointments/{appointmentId}/reschedule— move a bookingPUT /storefront/appointments/{appointmentId}/cancel— cancel itGET /storefront/orders— order history with payments and refunds