Booking SDK: a typed TypeScript SDK for custom booking flows

Stanislav TyshchenkoEngineering11 min readJul 19, 2026
Booking SDK — a typed TypeScript SDK for custom booking flows

Adding booking to an app looks like a weekend project until you start. Real-time availability across staff and locations, cart state that survives a page refresh, a payment flow that fails gracefully, booking statuses that move through their lifecycle — every piece is more nested and more stateful than it looks from the outside.

A booking SDK is what turns that from raw HTTP plumbing into typed method calls. This is a look at what a booking SDK does, when it's worth using over the raw API, and how the Opencals storefront SDK is structured — with code you can actually run.

Why a booking flow earns an SDK

Plenty of APIs don't need an SDK. A booking API does, and the reason is the shape of the data. A single booking flow touches:

Availability

Nested slots computed across staff, hours, duration, capacity

Cart state

Line items, quantities, selected times — held across steps

Payment intents

Stripe client secrets, confirmation, idempotency

Booking status

Pending, confirmed, rescheduled, cancelled lifecycles

Wire that up with hand-rolled fetch calls and you're maintaining a private, untyped copy of the API's mental model — one that silently drifts every time the backend adds a field. A typed SDK gives you autocomplete for every request, compile-time errors when a shape changes, and runtime validation at the boundary. In a domain this nested, that's not a nicety; it's the difference between a booking flow you trust and one you babysit.

Use a booking SDK when the flow is stateful and the types are nested — availability, carts, and payments. Reach for the raw REST API for one-off scripts, server-to-server jobs, or non-JavaScript stacks.

Install and set up

The Opencals storefront SDK is a TypeScript package:

bash
npm install @opencals/storefront-sdk

Initialize it once with your store API key. Keys are scoped to a single store, carry the sfk_ prefix, and come from the dashboard under Settings → API. Keep the key server-side — it's a secret, not a publishable client key.

ts
import { setupOpencals } from "@opencals/storefront-sdk"; const opencals = setupOpencals({ apiKey: process.env.OPENCALS_API_KEY, // sfk_... });

That's the whole setup. The base URL (https://api.opencals.com) and the OpenAPI 3.0 contract are baked in, so you're calling typed methods, not memorizing routes. There's an interactive spec at opencals.com/docs if you want to see the raw endpoints underneath.

The five things every booking flow does

Almost every custom booking flow is the same five steps. Here's each one through the SDK.

1. Load the service catalog

Your services page is populated from the product catalog. It rarely changes, so cache it.

ts
import { ProductService } from "@opencals/storefront-sdk"; const products = await ProductService.list(); // products: fully typed — name, duration, price, currency, staff, locations

2. Fetch real-time availability

This is the endpoint that earns the platform. You ask for a service, optionally a staff member and location, and a date range; you get back the slots that are genuinely bookable right now — after existing bookings, working hours, buffers, and capacity are all accounted for.

ts
import { AppointmentService } from "@opencals/storefront-sdk"; const slots = await AppointmentService.getAvailability({ productId, staffId, // optional — omit for "any available" locationId, // optional from: "2026-07-20", to: "2026-07-27", });

You render slots straight into a calendar. You are not reconstructing availability logic on the client — the hard part stays on the server where it belongs.

3. Create a cart and add the booking

Carts hold the selected slot as a line item and survive across steps. Store the returned cartId client-side.

ts
import { CartService } from "@opencals/storefront-sdk"; const cart = await CartService.create(); await CartService.addItem(cart.id, { productId, staffId, locationId, startsAt: selectedSlot.startsAt, });

4. Initiate and confirm checkout

Checkout returns a Stripe payment intent client secret. You hand that to Stripe Elements on the frontend to collect the card, then confirm.

ts
import { CheckoutService } from "@opencals/storefront-sdk"; const { clientSecret } = await CheckoutService.initiate(cart.id); // → render Stripe Elements with clientSecret, collect payment await CheckoutService.confirm(cart.id); // idempotent

The confirm step is idempotent: a retried request after a flaky network won't double-book or double-charge. On success the booking is created, the slot is blocked everywhere, and confirmation emails fire.

5. Customer self-service

Viewing, rescheduling, and cancelling a customer's own bookings uses a second credential — a customer JWT issued by your auth flow, not the store key. This dual-auth model keeps the store key server-side while letting a signed-in customer act on their own bookings.

ts
// customer token from your own auth, attached per-request const bookings = await AppointmentService.listForCustomer({ token }); await AppointmentService.reschedule(bookingId, { startsAt, token });
1

Catalog

ProductService.list() — populate services. Cache it.

2

Availability

AppointmentService.getAvailability(...) — render the calendar from real bookable slots.

3

Cart

CartService.create() then addItem() — hold the selection, store cartId client-side.

4

Checkout

CheckoutService.initiate() for the Stripe client secret, then confirm() after payment (idempotent).

5

Self-service

Customer JWT endpoints for view, reschedule, cancel.

Type safety and runtime validation

Static types catch shape mismatches at compile time. But data crossing a network boundary can still surprise you at runtime, so the SDK ships Zod schemas that validate responses as they come in. A malformed or unexpected payload fails loudly at the edge instead of leaking a undefined three components deep into your UI.

It also includes the small formatting helpers you'd otherwise rewrite on every project:

ts
import { formatPrice, formatDuration } from "@opencals/storefront-sdk"; formatPrice(4500, "USD"); // "$45.00" formatDuration(90); // "1h 30m"

Prices live in minor units (cents) and durations in minutes across the API, which is the correct way to avoid floating-point money bugs — the helpers just save you from formatting them by hand every time.

Using it with Next.js and React

The SDK is framework-agnostic; it runs wherever JavaScript runs. The only real rule is credential placement.

Keep the store API key on the server — route handlers, server actions, or server components. Fetch there, then pass typed data (or a customer token) down to the client. Never ship sfk_ keys to the browser.

ts
// app/api/availability/route.ts (Next.js route handler) import { AppointmentService, setupOpencals } from "@opencals/storefront-sdk"; setupOpencals({ apiKey: process.env.OPENCALS_API_KEY }); export async function GET(req: Request) { const { searchParams } = new URL(req.url); const slots = await AppointmentService.getAvailability({ productId: searchParams.get("productId")!, from: searchParams.get("from")!, to: searchParams.get("to")!, }); return Response.json(slots); }

The client calls your route, not Opencals directly. That's the whole headless booking pattern: your frontend, your framework, your domain — the booking engine and payments handled underneath.

SDK or raw REST — which to reach for

Both are supported; they're not competitors.

Pros

  • SDK: typed methods, autocomplete, and compile-time safety across the whole flow
  • SDK: Zod runtime validation and money/duration helpers included
  • SDK: less boilerplate for the stateful cart-and-checkout sequence

Cons

  • Raw REST: better for non-JavaScript stacks (Python, Go, PHP) — no SDK to depend on
  • Raw REST: leaner for server-to-server jobs or one-off scripts
  • Raw REST: nothing to update when you just need a single endpoint

If you're building a production booking experience in TypeScript, the SDK is the shorter path. If you're integrating from another language or writing a quick automation, the REST API is right there with the same guarantees.

Note

The SDK and the website templates are open source and free; the Opencals platform underneath is a managed cloud service. You get open, inspectable client code running against an API you don't have to operate.

Where to go from here

FAQ

Frequently Asked Questions

Early Access — 3 Months Free

Ready to transform your service business?

Join 150+ businesses already using Opencals. Get 3 months completely free with all features unlocked.

No credit card required
Setup in 10 minutes
Cancel anytime