@opencals/storefront-sdk is the official type-safe TypeScript client for the Storefront API. It’s generated from the same OpenAPI specification as the API reference, so every request and response is fully typed, and it ships with Zod schemas for runtime validation plus formatting helpers for prices, durations, and timezones. MIT licensed.
Installation
npm install @opencals/storefront-sdk
Setup
Call setupOpencals() once at app startup — a root layout, an API client singleton, or a module you import from your server code:
import { setupOpencals } from "@opencals/storefront-sdk";
setupOpencals({
apiKey: "sfk_…", // or set the OPENCALS_API_KEY env var
baseUrl: "https://…", // optional, defaults to https://api.opencals.com
logging: true, // optional request/response logging
errorHandler: true, // throw OpencalsApiError on failures (default)
customerToken: "eyJ…", // optional customer JWT for authenticated requests
});
With the OPENCALS_API_KEY environment variable set, setupOpencals() needs no arguments.
The API key is attached to every request automatically — which also means SDK calls belong on the server, where the key is safe.
Services
The SDK mirrors the API’s tags as static service classes:
| Service | Methods |
|---|
ProductService | list, get, getBySlug, getByExternalId, getCurrentAvailabilities, getCurrentAvailabilitiesMerged, getNearestAvailability |
AuthService | signIn, signUp, oauth, refresh, requestPasswordReset, resetPassword, requestEmailVerification, verifyEmail |
CartService | get, createOrGet, addItem, removeItem, extendExpiration, applyDiscountCode, removeDiscountCode |
CheckoutService | start, submit, saveCustomer, saveAnswers, getCartQuestions |
AppointmentService | list, find, create, cancel, reschedule, feedback, getBookingPreferences |
OrderService | list, find |
SelfService | getProfile, updateProfile, changePassword |
LocationService | list, get, getBySlug |
StaffMemberService | list, getBySlug |
PaymentService | getAvailableProviders, getSettings |
StoreService | getStorePublicSettings |
ProductCollectionService | list, getBySlug |
ImageService | get |
CheckoutQuestionService | listTranslations |
FeedbackQuestionService | listTranslations |
Every method takes a single options object with typed path, query, body, and headers fields matching the underlying endpoint:
import { ProductService } from "@opencals/storefront-sdk";
// GET /storefront/products/slug/{slug}
const { data: product } = await ProductService.getBySlug({
path: { slug: "haircut" },
});
// GET /storefront/products/{productId}/current-availabilities
const { data: slots } = await ProductService.getCurrentAvailabilities({
path: { productId: product!.id },
query: { date: "2026-06-15", timezone: "America/New_York" },
});
Cart and checkout calls pass the cart ID through the typed headers field:
import { CartService } from "@opencals/storefront-sdk";
const { data: cart } = await CartService.createOrGet({
headers: { "X-Cart-Id": cartId ?? "" },
});
await CartService.addItem({
body: { appointmentId },
headers: { "X-Cart-Id": cart!.id },
});
Customer-authenticated requests
For endpoints that act on behalf of a signed-in customer, either pass customerToken to setupOpencals() or set the header per call:
import { AppointmentService } from "@opencals/storefront-sdk";
const { data: appointments } = await AppointmentService.list({
headers: { Authorization: `Bearer ${accessToken}` },
});
See Authentication for how to obtain and refresh customer tokens.
Error handling
With the default error handler enabled, failed requests throw a typed OpencalsApiError:
import { OpencalsApiError, ProductService } from "@opencals/storefront-sdk";
try {
await ProductService.get({ path: { productId: "missing" } });
} catch (error) {
if (error instanceof OpencalsApiError) {
console.error(error.message);
}
}
Zod validation
Response types come with auto-generated Zod schemas, useful when data crosses a trust boundary (for example, after passing through your own API routes):
import { zProductListResponse } from "@opencals/storefront-sdk";
const result = zProductListResponse.safeParse(apiResponse);
Helpers
The SDK includes small utilities you’d otherwise rewrite in every booking UI:
import {
formatPrice,
formatDuration,
toLocalFromUtc,
toUtcFromLocal,
mergeTimeIntervals,
} from "@opencals/storefront-sdk";
formatPrice(4500, "USD"); // "$45.00"
formatDuration(3600); // "1h"
toLocalFromUtc("2026-06-15T14:00:00Z", "America/New_York");
toLocalFromUtc / toUtcFromLocal handle the UTC conversion the API expects for appointment slots, and mergeTimeIntervals is handy when rendering availability ranges.
Putting it together
The Build a booking page tutorial uses the SDK end-to-end — services, availability, cart, and checkout — and the open-source booking templates are complete Next.js apps built on it.