@resira/sdk
v0.9.0
Published
TypeScript SDK for the Resira public reservation API
Maintainers
Readme
@resira/sdk v0.3.1
TypeScript SDK for the Resira public reservation API.
Version history lives in CHANGELOG.md.
Installation
npm install @resira/[email protected]Quick start
import { Resira } from "@resira/sdk";
const resira = new Resira({
apiKey: "resira_live_your_api_key_here",
});Client config
const resira = new Resira({
apiKey: "resira_live_your_api_key_here",
maxRetries: 3,
retryBaseDelay: 500,
});| Option | Type | Default | Description |
| --- | --- | --- | --- |
| apiKey | string | required | Public Resira API key |
| baseUrl | string | unset | Pin all SDK requests to one origin |
| baseUrls | Array<string \| { url: string; weight?: number }> | unset | Ordered fallback origin list; first valid origin is used |
| maxRetries | number | 3 | Retry attempts for 429 and 5xx responses |
| retryBaseDelay | number | 500 | Base exponential backoff delay in ms |
| fetch | typeof fetch | globalThis.fetch | Custom fetch implementation |
Routing behavior
- The SDK no longer performs client-side 50/50 routing or writes routing state into browser storage.
- Only explicit SDK config via
baseUrlorbaseUrlsoverrides the default routing behavior. baseUrlsis treated as an ordered fallback list; the first valid origin is used.- In development, the SDK defaults to
http://localhost:3001. - In production, the SDK defaults to
https://api.resira.app.
Core methods
resira.getConfig()
Fetch non-sensitive public property config such as Stripe publishable key, deposit percentage, currency, and branding.
const config = await resira.getConfig();
console.log(config.stripePublishableKey);resira.validatePromoCode(code)
const promo = await resira.validatePromoCode("SUMMER20");
if (promo.valid) {
console.log(promo.discountType, promo.discountValue);
}resira.getAvailability(resourceId, params?)
const availability = await resira.getAvailability("prop-1", {
startDate: "2026-07-01",
endDate: "2026-07-07",
});const slots = await resira.getAvailability("table-5", {
date: "2026-07-01",
partySize: 4,
});resira.listResources()
const { resources } = await resira.listResources();resira.listProducts()
const { products } = await resira.listProducts();resira.listDishes()
Fetch all dishes with 3D model data for the organisation.
const { dishes } = await resira.listDishes();
dishes.forEach((dish) => {
console.log(dish.name, dish.model?.glbUrl);
});resira.getDish(dishId)
Fetch a single dish by ID.
const { dish } = await resira.getDish("dish-uuid");
console.log(dish.name, dish.model?.glbUrl, dish.model?.usdzUrl);resira.createPaymentIntent(payload, options?)
Sends Idempotency-Key on every request (auto-generated UUID unless you pass options.idempotencyKey). Your API should treat the same key + same body as one logical operation and return the same payment intent (dedupes double-submit).
409 Conflict usually means the slot/hold/capacity no longer matches (e.g. expired hold, sold out). Branch on the JSON error body’s code field (machine-readable), not the English error string:
| body.code (examples) | Typical meaning |
| --- | --- |
| SLOT_UNAVAILABLE | Slot no longer bookable |
| HOLD_EXPIRED | Soft hold / lock expired |
| CAPACITY_EXCEEDED | Not enough capacity |
| CONFLICT | Generic booking conflict |
import { ResiraApiError, getResiraApiErrorMachineCode } from "@resira/sdk";
try {
const paymentIntent = await resira.createPaymentIntent({ /* … */ });
} catch (e) {
if (e instanceof ResiraApiError && e.status === 409) {
const code = e.machineCode ?? getResiraApiErrorMachineCode(e);
// branch on code
}
}const paymentIntent = await resira.createPaymentIntent({
productId: "prod-123",
resourceId: "res-456",
partySize: 2,
startDate: "2026-07-01",
startTime: "2026-07-01T10:00:00Z",
endTime: "2026-07-01T11:00:00Z",
guestName: "Jane Doe",
guestEmail: "[email protected]",
});resira.confirmPayment(payload)
const confirmation = await resira.confirmPayment({
paymentIntentId: "pi_xxx",
reservationId: "res_123",
});resira.createReservation(payload, options?)
Creates a reservation via the public booking flow. An idempotency key is automatically generated unless you pass one explicitly.
const { reservation } = await resira.createReservation({
resourceId: "prop-1",
guestName: "Jane Doe",
guestEmail: "[email protected]",
startDate: "2026-07-01",
endDate: "2026-07-07",
partySize: 3,
});resira.listReservations(resourceId, params?)
const page = await resira.listReservations("prop-1", {
status: "confirmed",
page: 1,
limit: 50,
});resira.getReservation(resourceId, reservationId)
const result = await resira.getReservation("prop-1", "res-uuid");Error handling
All SDK errors extend ResiraError.
import {
ResiraApiError,
ResiraNetworkError,
ResiraRateLimitError,
} from "@resira/sdk";
try {
await resira.createReservation(payload);
} catch (error) {
if (error instanceof ResiraRateLimitError) {
console.log(error.retryAfter);
} else if (error instanceof ResiraApiError) {
console.log(error.status, error.message, error.machineCode, error.body);
} else if (error instanceof ResiraNetworkError) {
console.log(error.message);
}
}| Error class | Meaning |
| --- | --- |
| ResiraRateLimitError | 429 response after retry handling |
| ResiraApiError | Non-2xx API response |
| ResiraNetworkError | Network/DNS/TLS failure |
Requirements
- Node.js >= 18
- No runtime dependencies
Release notes
See CHANGELOG.md for release-by-release details.
