npm package: @slotchain/sdk
Description: TypeScript SDK for Slotly API
Latest version: 1.1.4

# @slotly/sdk

🧩 **Slotly SDK** — A typed, developer‑friendly client for interacting with the Slotly platform API.

## Table of Contents
- [Overview](#overview)  
- [Installation](#installation)  
- [Quick Start](#quick‑start)  
- [API Client Architecture](#api‑client‑architecture)  
- [Key Concepts](#key‑concepts)  
- [Usage Examples](#usage‑examples)  
- [Middleware & Auth](#middleware‑auth)  
- [Mocking & Testing](#mocking‑testing)  
- [Contributing](#contributing)  
- [License](#license)

## Overview  
The Slotly SDK abstracts your HTTP API into typed domain‑clients, handling authentication, retries, error handling, and client‑scoped access. Whether you're building internal admin tools, public booking flows, or partner integrations, the SDK provides a consistent interface.

## Installation  
```bash
npm install @slotly/sdk
# or
yarn add @slotly/sdk
```

**Dependencies:**
- `jwt-decode` (for decoding user token) - included in dependencies
- `axios` (HTTP client with custom retry logic)
- Custom exponential backoff retry with jitter

**Peer Dependencies** (install at least one based on your environment):
- `@clerk/clerk-sdk-node` - For server-side usage
- `@clerk/nextjs` - For Next.js client-side usage
- `@clerk/clerk-react` - For React client-side usage
- `next` - For Next.js middleware (required if using middleware)

## Quick Start  
```ts
import { useSlotly } from '@slotly/sdk';

const slotly = useSlotly({
  getClientKey: async () => process.env.SLOTLY_API_KEY!,
  getUserToken: async () => /* optional: return user token or null */
});

const tenant = await slotly.tenant.getBySlug('example‑tenant');
console.log(tenant.data);
```

## API Client Architecture  
- Single entry point → `useSlotly()`  
- Axios instance under the hood with:  
  - Base URL config  
  - Request/response interceptors (logging, error shaping)  
  - Exponential back‑off retry logic with jitter  
- Domain‑specific clients exposed via `slotly.tenant`, `slotly.booking`, etc.  
- Strong types for all requests and responses (`ApiResponse<T>`, domain types)  
- Client isolation per instantiation ensures SSR‑safety (supports both client‑side and server‑side usage)

## Domain‑Specific Clients  
The SDK provides typed clients for each domain:

- **TenantClient** - Tenant configuration and management
- **OrganizationClient** - Organization management and tenant associations
- **BookingClient** - Booking CRUD operations
- **ServiceClient** - Service management (listServices, create, update, delete, bulkCreate)
- **SlotClient** - Time slot management and availability
- **CustomerClient** - Customer management and lookup
- **FlowClient** - Booking flow configuration and execution
- **StudioClient** - Studio CRUD and data quality operations
- **NotificationClient** - Notification management and sending
- **DataQualityClient** - Data quality issue tracking and resolution

**Note:** RegisterClient (studio‑specific booking register operations) will be available in the separate `@slotly/studio-sdk`.

## Key Concepts  
### **Client Key (x‑slotly‑api‑key)**  
Scope‑based key used to identify the _calling client_ or application (e.g., booking site, admin UI). Carries permissions that scope API access to specific tenants and operations.  
### **User Token (Authorization: Bearer …)**  
Optional token that identifies the _end‑user_ (e.g., a clerk user).  
### **Tenant Context**  
Many operations are tenant‑scoped. The SDK enables context propagation so your calls act within the right tenant.

## Usage Examples  
### Domain Client Example – Tenant  
```ts
const result = await slotly.tenant.getBySlug('bright‑accountants');
if (result.success) {
  console.log('Tenant data:', result.data);
} else {
  console.error('Error:', result.error);
}
```

### Booking Flow for Public Site  
```ts
const slotly = useSlotly({
  getClientKey: async () => process.env.SLOTLY_PAGES_KEY!,
  getUserToken: async () => null
});

const booking = await slotly.booking.create({
  slot_id: 'xyz123',
  customer_info: { name: 'Jane Doe', email: 'jane@example.com' },
  booking_data: { services: ['s1'], total: 100 }
});
```

### List Bookings by Tenant  
```ts
// List bookings for a tenant (single query - fast!)
const bookings = await slotly.booking.list({
  tenant_id: 'tenant-123',
  includeSlot: true,
  page: 1,
  limit: 20
});

// Convenience methods
const tenantBookings = await slotly.booking.listByTenant('tenant-123');
const tenantBookingsWithSlots = await slotly.booking.listByTenantWithSlots('tenant-123');

// Access slot information
if (bookings.success && bookings.data) {
  bookings.data.forEach(booking => {
    if ('slots' in booking && booking.slots) {
      console.log('Slot name:', booking.slots.name);
    }
  });
}
```

### Service Management  
```ts
const services = await slotly.service.listServices('bright-accountants');
if (services.success) {
  console.log('Available services:', services.data);
}
```

### Organization Management  
```ts
// Get organization only
const org = await slotly.organization.getById('org-123');

// Get organization with nested tenants
const orgWithTenants = await slotly.organization.getById('org-123', {
  includeTenants: true,
  page: 1,
  limit: 10,
  is_public: true
});

if (orgWithTenants.success && orgWithTenants.data) {
  const org = orgWithTenants.data;
  if ('tenants' in org) {
    console.log('Organization:', org.name);
    console.log('Tenants:', org.tenants.map(t => t.name));
  }
}
```

## Middleware & Auth  
For your API routes, use the `validateSlotlyRequest()` middleware to enforce both client and optional user auth:

```ts
import { validateSlotlyRequest } from '@slotly/sdk/middleware/validateSlotlyRequest';

export default validateSlotlyRequest(async (req, res) => {
  const { clientKey, userId, tenantId } = req.slotlyContext;
  // … your logic here …
});
```

**Features of middleware:**  
- Validates `x‑slotly‑api‑key` (required)  
- Decodes optional `Authorization` bearer token (JWT)  
- Throws `SlotlyAuthError` if validation fails  
- Injects `slotlyContext` into request object (clientKey, userId, tenantId, permissions)  
- Mock allowlist for development – replace with production key store  

## Mocking & Testing  
- A `mockAdapter.ts` is included to enable mocking Axios responses in tests  
- Configure mocks to simulate API behaviour without needing live backend  
- Use domain‑clients in unit tests with predictable responses  

## Bundle & Publishing  
The SDK is bundled using `tsup` for optimal performance and compatibility:
- Generates both ESM (`dist/index.esm.js`) and CJS (`dist/index.cjs.js`) formats
- Includes TypeScript declaration files (`.d.ts`) for type support
- Source maps included for debugging
- Dependencies are marked as external to minimize bundle size
- Target: ES2020 for broad runtime compatibility

**Known Limitations:**
- No asset bundling currently supported (e.g., CSS, images)
- If you require custom assets or legacy browser support, switching to Rollup with plugins is supported

**Build Commands:**
```bash
npm run build        # Production build
npm run build:watch  # Watch mode for development
```

## Contributing  
We welcome contributions!  
- Fork the repo  
- Run `npm install` and tests (`npm test`)  
- Submit PRs with clear changelog entries  
- Please keep README, JSDoc, and type definitions up to date  

## License  
[MIT License](./LICENSE)