npm package: @resira/sdk
Description: TypeScript SDK for the Resira public reservation API
Latest version: 0.4.1

# @resira/sdk v0.3.1

TypeScript SDK for the Resira public reservation API.

Version history lives in [CHANGELOG.md](./CHANGELOG.md).

## Installation

```bash
npm install @resira/sdk@0.3.1
```

## Quick start

```ts
import { Resira } from "@resira/sdk";

const resira = new Resira({
  apiKey: "resira_live_your_api_key_here",
});
```

## Client config

```ts
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 `baseUrl` or `baseUrls` overrides the default routing behavior.
- `baseUrls` is 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.

```ts
const config = await resira.getConfig();
console.log(config.stripePublishableKey);
```

### `resira.validatePromoCode(code)`

```ts
const promo = await resira.validatePromoCode("SUMMER20");
if (promo.valid) {
  console.log(promo.discountType, promo.discountValue);
}
```

### `resira.getAvailability(resourceId, params?)`

```ts
const availability = await resira.getAvailability("prop-1", {
  startDate: "2026-07-01",
  endDate: "2026-07-07",
});
```

```ts
const slots = await resira.getAvailability("table-5", {
  date: "2026-07-01",
  partySize: 4,
});
```

### `resira.listResources()`

```ts
const { resources } = await resira.listResources();
```

### `resira.listProducts()`

```ts
const { products } = await resira.listProducts();
```

### `resira.listDishes()`

Fetch all dishes with 3D model data for the organisation.

```ts
const { dishes } = await resira.listDishes();
dishes.forEach((dish) => {
  console.log(dish.name, dish.model?.glbUrl);
});
```

### `resira.getDish(dishId)`

Fetch a single dish by ID.

```ts
const { dish } = await resira.getDish("dish-uuid");
console.log(dish.name, dish.model?.glbUrl, dish.model?.usdzUrl);
```

### `resira.createPaymentIntent(payload, options?)`

```ts
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: "jane@example.com",
});
```

### `resira.confirmPayment(payload)`

```ts
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.

```ts
const { reservation } = await resira.createReservation({
  resourceId: "prop-1",
  guestName: "Jane Doe",
  guestEmail: "jane@example.com",
  startDate: "2026-07-01",
  endDate: "2026-07-07",
  partySize: 3,
});
```

### `resira.listReservations(resourceId, params?)`

```ts
const page = await resira.listReservations("prop-1", {
  status: "confirmed",
  page: 1,
  limit: 50,
});
```

### `resira.getReservation(resourceId, reservationId)`

```ts
const result = await resira.getReservation("prop-1", "res-uuid");
```

## Error handling

All SDK errors extend `ResiraError`.

```ts
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);
  } 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](./CHANGELOG.md) for release-by-release details.