@slotchain/sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1149 @@
1
+ import { AxiosInstance } from 'axios';
2
+ import { NextApiRequest, NextApiResponse } from 'next';
3
+
4
+ /**
5
+ * Standard API response format for all Slotly API endpoints
6
+ */
7
+ interface ApiResponse<T> {
8
+ success: boolean;
9
+ data?: T;
10
+ error?: {
11
+ code: string;
12
+ message: string;
13
+ details?: any;
14
+ };
15
+ pagination?: {
16
+ page: number;
17
+ limit: number;
18
+ total: number;
19
+ totalPages: number;
20
+ };
21
+ }
22
+ /**
23
+ * Placeholder types for domain models
24
+ * TODO: Replace with actual types from the Slotly API
25
+ */
26
+ interface Tenant {
27
+ id: string;
28
+ slug: string;
29
+ name: string;
30
+ }
31
+ /**
32
+ * Tenant branding configuration
33
+ */
34
+ interface TenantBranding {
35
+ logoUrl?: string;
36
+ primaryColor?: string;
37
+ secondaryColor?: string;
38
+ faviconUrl?: string;
39
+ customCss?: string;
40
+ }
41
+ /**
42
+ * Service item within a service
43
+ */
44
+ interface ServiceItem {
45
+ id: string;
46
+ serviceId: string;
47
+ name: string;
48
+ description?: string;
49
+ price?: number;
50
+ duration?: number;
51
+ available?: boolean;
52
+ }
53
+ /**
54
+ * Enhanced Service type with nested service items
55
+ */
56
+ interface ServiceWithItems extends Service {
57
+ /** Array of service items nested within this service (optional, may be empty array) */
58
+ serviceItems?: ServiceItem[];
59
+ }
60
+ /**
61
+ * Service item with parent service reference
62
+ */
63
+ interface ServiceItemWithService extends ServiceItem {
64
+ /** Parent service reference (optional, for convenience when fetching items separately) */
65
+ parentService?: Service;
66
+ }
67
+ /**
68
+ * Category extracted from services
69
+ */
70
+ interface Category {
71
+ id: string;
72
+ name: string;
73
+ slug?: string;
74
+ description?: string;
75
+ /** Array of service IDs in this category */
76
+ serviceIds?: string[];
77
+ /** Optional: services in this category (if expanded) */
78
+ services?: Service[];
79
+ }
80
+ interface Booking {
81
+ id: string;
82
+ tenantId: string;
83
+ slotId?: string;
84
+ customerId?: string;
85
+ status?: string;
86
+ }
87
+ interface Service {
88
+ id: string;
89
+ tenantId: string;
90
+ name: string;
91
+ description?: string;
92
+ duration?: number;
93
+ price?: number;
94
+ }
95
+ interface Slot {
96
+ id: string;
97
+ tenantId: string;
98
+ serviceId?: string;
99
+ startTime: string;
100
+ endTime: string;
101
+ available: boolean;
102
+ }
103
+ interface Customer {
104
+ id: string;
105
+ tenantId: string;
106
+ email?: string;
107
+ name?: string;
108
+ phone?: string;
109
+ }
110
+ interface Flow {
111
+ id: string;
112
+ tenantId: string;
113
+ name: string;
114
+ steps?: FlowStep[];
115
+ }
116
+ interface FlowStep {
117
+ id: string;
118
+ type: string;
119
+ config?: Record<string, any>;
120
+ }
121
+ interface Studio {
122
+ id: string;
123
+ name: string;
124
+ slug?: string;
125
+ }
126
+ interface Notification {
127
+ id: string;
128
+ tenantId?: string;
129
+ type: string;
130
+ recipient: string;
131
+ content: string;
132
+ sentAt?: string;
133
+ }
134
+ interface DataQualityIssue {
135
+ id: string;
136
+ tenantId?: string;
137
+ type: string;
138
+ severity: 'low' | 'medium' | 'high';
139
+ message: string;
140
+ resolved: boolean;
141
+ }
142
+ /**
143
+ * Full tenant configuration including branding, services, and service items
144
+ * Service items are nested within each service object (not as a separate top-level array)
145
+ */
146
+ interface TenantFullConfig {
147
+ /** Tenant ID (required, non-nullable) */
148
+ id: string;
149
+ /** Tenant slug (required, non-nullable) */
150
+ slug: string;
151
+ /** Tenant name (required, non-nullable) */
152
+ name: string;
153
+ /** Branding configuration (optional) */
154
+ branding?: TenantBranding;
155
+ /** Array of services with nested service items (required, non-nullable, may be empty array) */
156
+ services: ServiceWithItems[];
157
+ /** Enabled features (optional) */
158
+ featuresEnabled?: string[];
159
+ /** Subscription level (optional) */
160
+ subscriptionLevel?: string;
161
+ /** Additional configuration (optional) */
162
+ config?: Record<string, any>;
163
+ }
164
+
165
+ declare class TenantClient {
166
+ private client;
167
+ constructor(client: AxiosInstance);
168
+ /**
169
+ * Get tenant configuration by slug
170
+ * GET /api/v1/tenant-config/:slug
171
+ */
172
+ getBySlug(slug: string): Promise<ApiResponse<Tenant>>;
173
+ /**
174
+ * Get tenant by ID
175
+ * GET /api/v1/tenants/:id
176
+ */
177
+ getById(id: string): Promise<ApiResponse<Tenant>>;
178
+ /**
179
+ * List all tenants
180
+ * GET /api/v1/tenants
181
+ */
182
+ list(params?: {
183
+ page?: number;
184
+ limit?: number;
185
+ }): Promise<ApiResponse<Tenant[]>>;
186
+ /**
187
+ * Create a new tenant
188
+ * POST /api/v1/tenants
189
+ */
190
+ create(data: Partial<Tenant>): Promise<ApiResponse<Tenant>>;
191
+ /**
192
+ * Update tenant by ID
193
+ * PATCH /api/v1/tenants/:id
194
+ */
195
+ update(id: string, data: Partial<Tenant>): Promise<ApiResponse<Tenant>>;
196
+ /**
197
+ * Delete tenant by ID
198
+ * DELETE /api/v1/tenants/:id
199
+ */
200
+ delete(id: string): Promise<ApiResponse<void>>;
201
+ /**
202
+ * Get full tenant configuration including branding, services, and service items
203
+ * GET /api/v1/tenants/:slug
204
+ *
205
+ * This method returns the complete tenant configuration including:
206
+ * - Tenant basic info (id, slug, name)
207
+ * - Branding configuration (logo, colors, etc.)
208
+ * - All services associated with the tenant (with nested service items)
209
+ * - Feature flags and subscription level
210
+ *
211
+ * Note: Service items are nested within each service object (not as a separate top-level array).
212
+ * The endpoint returns full config by default (includes services with nested items).
213
+ *
214
+ * @param slug - Tenant slug identifier (required, non-nullable)
215
+ * @param includeServices - Include services with nested items (default: true)
216
+ * @returns Promise resolving to ApiResponse<TenantFullConfig>
217
+ *
218
+ * @example
219
+ * ```typescript
220
+ * const result = await slotly.tenant.getFullConfig('bright-accountants');
221
+ * if (result.success && result.data) {
222
+ * const config = result.data;
223
+ * console.log('Tenant:', config.name);
224
+ * console.log('Logo:', config.branding?.logoUrl);
225
+ * console.log('Services:', config.services.length);
226
+ *
227
+ * // Service items are nested within each service
228
+ * config.services.forEach(service => {
229
+ * console.log(`Service: ${service.name}`);
230
+ * if (service.serviceItems) {
231
+ * console.log(` Items: ${service.serviceItems.length}`);
232
+ * }
233
+ * });
234
+ * }
235
+ * ```
236
+ *
237
+ * **Guaranteed fields (non-nullable):**
238
+ * - `id`: string
239
+ * - `slug`: string
240
+ * - `name`: string
241
+ * - `services`: ServiceWithItems[] (may be empty array, each service may have nested `serviceItems`)
242
+ *
243
+ * **Optional fields:**
244
+ * - `branding`: TenantBranding | undefined
245
+ * - `services[].serviceItems`: ServiceItem[] | undefined (nested within each service)
246
+ * - `featuresEnabled`: string[] | undefined
247
+ * - `subscriptionLevel`: string | undefined
248
+ * - `config`: Record<string, any> | undefined
249
+ *
250
+ * **Error Codes:**
251
+ * - `TENANT_NOT_FOUND` - Tenant with slug not found
252
+ * - `AUTH_ERROR` - Authentication failed
253
+ * - `PERMISSION_DENIED` - Insufficient permissions
254
+ * - `NETWORK_ERROR` - Network request failed
255
+ */
256
+ getFullConfig(slug: string, includeServices?: boolean): Promise<ApiResponse<TenantFullConfig>>;
257
+ }
258
+
259
+ declare class BookingClient {
260
+ private client;
261
+ constructor(client: AxiosInstance);
262
+ /**
263
+ * Get booking by ID
264
+ * GET /api/v1/bookings/:id
265
+ */
266
+ getById(id: string): Promise<ApiResponse<Booking>>;
267
+ /**
268
+ * List bookings
269
+ * GET /api/v1/bookings
270
+ */
271
+ list(params?: {
272
+ slot_id?: string;
273
+ customer_id?: string;
274
+ status?: string;
275
+ page?: number;
276
+ limit?: number;
277
+ }): Promise<ApiResponse<Booking[]>>;
278
+ /**
279
+ * Create a new booking (publishes booking.created event)
280
+ * POST /api/v1/bookings
281
+ */
282
+ create(data: {
283
+ slot_id: string;
284
+ customer_info: {
285
+ name: string;
286
+ email: string;
287
+ phone?: string;
288
+ address?: string;
289
+ city?: string;
290
+ state?: string;
291
+ zipCode?: string;
292
+ isGuestCheckout?: boolean;
293
+ };
294
+ booking_data?: Record<string, unknown>;
295
+ }): Promise<ApiResponse<Booking>>;
296
+ /**
297
+ * Update booking by ID
298
+ * PUT /api/v1/bookings/:id
299
+ */
300
+ update(id: string, data: {
301
+ status?: string;
302
+ customer_info?: Record<string, unknown>;
303
+ booking_data?: Record<string, unknown>;
304
+ }): Promise<ApiResponse<Booking>>;
305
+ /**
306
+ * Cancel booking by ID
307
+ * Note: Cancel is typically done via update with status change
308
+ * If dedicated cancel endpoint exists, update this method
309
+ */
310
+ cancel(id: string): Promise<ApiResponse<Booking>>;
311
+ /**
312
+ * Delete booking by ID (soft delete)
313
+ * DELETE /api/v1/bookings/:id
314
+ */
315
+ delete(id: string): Promise<ApiResponse<void>>;
316
+ }
317
+
318
+ declare class ServiceClient {
319
+ private client;
320
+ constructor(client: AxiosInstance);
321
+ /**
322
+ * Get service by ID
323
+ * GET /api/v1/services/:id
324
+ *
325
+ * @param id - Service ID
326
+ * @param includeItems - Include nested service items (default: false)
327
+ * @returns Service details (with items if includeItems=true)
328
+ */
329
+ getById(id: string, includeItems?: boolean): Promise<ApiResponse<Service | ServiceWithItems>>;
330
+ /**
331
+ * List services by tenant slug
332
+ * GET /api/v1/services?tenantSlug=:slug
333
+ *
334
+ * @param slug - Tenant slug (or use tenant_id in list() method)
335
+ * @param params - Optional parameters (includeItems, page, limit, is_active)
336
+ * @returns List of services for the tenant
337
+ */
338
+ listServices(slug: string, params?: {
339
+ includeItems?: boolean;
340
+ page?: number;
341
+ limit?: number;
342
+ is_active?: boolean;
343
+ }): Promise<ApiResponse<Service[] | ServiceWithItems[]>>;
344
+ /**
345
+ * List services with optional filters
346
+ * GET /api/v1/services
347
+ *
348
+ * @param params - Query parameters (tenant_id, tenantSlug, slot_id, page, limit, is_active, includeItems)
349
+ * @returns Paginated list of services
350
+ */
351
+ list(params?: {
352
+ tenant_id?: string;
353
+ tenantSlug?: string;
354
+ slot_id?: string;
355
+ page?: number;
356
+ limit?: number;
357
+ is_active?: boolean;
358
+ includeItems?: boolean;
359
+ }): Promise<ApiResponse<Service[] | ServiceWithItems[]>>;
360
+ /**
361
+ * Create a new service
362
+ * POST /api/v1/services
363
+ *
364
+ * @param data - Service creation data (tenant_id, name required)
365
+ * @returns Created service
366
+ */
367
+ create(data: Partial<Service>): Promise<ApiResponse<Service>>;
368
+ /**
369
+ * Update service by ID
370
+ * PUT /api/v1/services/:id
371
+ *
372
+ * @param id - Service ID
373
+ * @param data - Service update data
374
+ * @returns Updated service
375
+ */
376
+ update(id: string, data: Partial<Service>): Promise<ApiResponse<Service>>;
377
+ /**
378
+ * Delete service by ID
379
+ * DELETE /api/v1/services/:id
380
+ *
381
+ * @param id - Service ID
382
+ * @returns Empty response on success
383
+ */
384
+ delete(id: string): Promise<ApiResponse<void>>;
385
+ /**
386
+ * Bulk create services
387
+ * POST /api/v1/services/bulk (if available)
388
+ *
389
+ * @param services - Array of services to create
390
+ * @returns Created services
391
+ */
392
+ bulkCreate(services: Partial<Service>[]): Promise<ApiResponse<Service[]>>;
393
+ /**
394
+ * Get service with its service items nested
395
+ * GET /api/v1/services/:id?includeItems=true
396
+ *
397
+ * @param id - Service ID
398
+ * @returns Service with nested service items
399
+ */
400
+ getWithItems(id: string): Promise<ApiResponse<ServiceWithItems>>;
401
+ /**
402
+ * List services with their service items nested
403
+ * GET /api/v1/services?includeItems=true
404
+ *
405
+ * @param params - Query parameters (tenant_id, tenantSlug, slot_id, page, limit, is_active)
406
+ * @returns Paginated list of services with nested service items
407
+ */
408
+ listWithItems(params?: {
409
+ tenant_id?: string;
410
+ tenantSlug?: string;
411
+ slot_id?: string;
412
+ page?: number;
413
+ limit?: number;
414
+ is_active?: boolean;
415
+ }): Promise<ApiResponse<ServiceWithItems[]>>;
416
+ /**
417
+ * List all categories for a tenant (extracted from services)
418
+ * GET /api/v1/categories?tenantSlug=:slug
419
+ *
420
+ * @param tenantSlug - Tenant slug (or use tenant_id in params)
421
+ * @param params - Optional query parameters (tenant_id, includeServices)
422
+ * @returns List of categories for the tenant
423
+ */
424
+ listCategories(tenantSlug?: string, params?: {
425
+ tenant_id?: string;
426
+ includeServices?: boolean;
427
+ }): Promise<ApiResponse<Category[]>>;
428
+ /**
429
+ * Get category by ID (category name, lowercase)
430
+ * GET /api/v1/categories/:id?tenantSlug=:slug
431
+ *
432
+ * @param categoryId - Category ID (lowercase category name, e.g., "consulting")
433
+ * @param tenantSlug - Tenant slug (or use tenant_id in params)
434
+ * @param params - Optional query parameters (tenant_id, includeServices, includeItems)
435
+ * @returns Category details
436
+ */
437
+ getCategory(categoryId: string, tenantSlug?: string, params?: {
438
+ tenant_id?: string;
439
+ includeServices?: boolean;
440
+ includeItems?: boolean;
441
+ }): Promise<ApiResponse<Category>>;
442
+ }
443
+
444
+ /**
445
+ * ServiceItemClient provides methods for managing service items independently
446
+ * or in relation to their parent services.
447
+ */
448
+ declare class ServiceItemClient {
449
+ private client;
450
+ constructor(client: AxiosInstance);
451
+ /**
452
+ * Get service item by ID
453
+ * GET /api/v1/service-items/:id
454
+ *
455
+ * @param id - Service item ID
456
+ * @returns Service item details
457
+ */
458
+ getById(id: string): Promise<ApiResponse<ServiceItem>>;
459
+ /**
460
+ * List all service items for a service
461
+ * GET /api/v1/services/:serviceId/items
462
+ *
463
+ * @param serviceId - Service ID
464
+ * @returns List of service items for the specified service
465
+ */
466
+ listByService(serviceId: string): Promise<ApiResponse<ServiceItem[]>>;
467
+ /**
468
+ * Create a new service item for a service
469
+ * POST /api/v1/services/:serviceId/items
470
+ *
471
+ * @param serviceId - Service ID
472
+ * @param data - Service item creation data
473
+ * @returns Created service item
474
+ */
475
+ createForService(serviceId: string, data: Partial<ServiceItem>): Promise<ApiResponse<ServiceItem>>;
476
+ /**
477
+ * Create a new service item
478
+ * POST /api/v1/service-items (alternative to createForService)
479
+ *
480
+ * @param data - Service item creation data (must include service_id)
481
+ * @returns Created service item
482
+ */
483
+ create(data: Partial<ServiceItem>): Promise<ApiResponse<ServiceItem>>;
484
+ /**
485
+ * Update service item by ID
486
+ * PUT /api/v1/service-items/:id
487
+ *
488
+ * @param id - Service item ID
489
+ * @param data - Service item update data
490
+ * @returns Updated service item
491
+ */
492
+ update(id: string, data: Partial<ServiceItem>): Promise<ApiResponse<ServiceItem>>;
493
+ /**
494
+ * Delete service item by ID
495
+ * DELETE /api/v1/service-items/:id
496
+ *
497
+ * @param id - Service item ID
498
+ * @returns Empty response on success
499
+ */
500
+ delete(id: string): Promise<ApiResponse<void>>;
501
+ }
502
+
503
+ declare class SlotClient {
504
+ private client;
505
+ constructor(client: AxiosInstance);
506
+ /**
507
+ * Get slot by ID
508
+ * GET /api/v1/slots/:id
509
+ *
510
+ * @param id - Slot ID
511
+ * @returns Slot details
512
+ */
513
+ getById(id: string): Promise<ApiResponse<Slot>>;
514
+ /**
515
+ * List available slots
516
+ * GET /api/v1/slots
517
+ *
518
+ * @param params - Query parameters (tenant_id required, status, is_active, page, limit)
519
+ * @returns Paginated list of slots
520
+ */
521
+ list(params: {
522
+ tenant_id: string;
523
+ status?: string;
524
+ is_active?: boolean;
525
+ page?: number;
526
+ limit?: number;
527
+ }): Promise<ApiResponse<Slot[]>>;
528
+ /**
529
+ * Create a new slot
530
+ * POST /api/v1/slots
531
+ *
532
+ * @param data - Slot creation data (tenant_id, name required)
533
+ * @returns Created slot
534
+ */
535
+ create(data: Partial<Slot>): Promise<ApiResponse<Slot>>;
536
+ /**
537
+ * Update slot by ID
538
+ * PUT /api/v1/slots/:id
539
+ *
540
+ * @param id - Slot ID
541
+ * @param data - Slot update data
542
+ * @returns Updated slot
543
+ */
544
+ update(id: string, data: Partial<Slot>): Promise<ApiResponse<Slot>>;
545
+ /**
546
+ * Delete slot by ID
547
+ * DELETE /api/v1/slots/:id
548
+ *
549
+ * @param id - Slot ID
550
+ * @returns Empty response on success
551
+ */
552
+ delete(id: string): Promise<ApiResponse<void>>;
553
+ /**
554
+ * Bulk create slots
555
+ * POST /api/v1/slots/bulk (verify if available)
556
+ *
557
+ * @param slots - Array of slots to create
558
+ * @returns Created slots
559
+ */
560
+ bulkCreate(slots: Partial<Slot>[]): Promise<ApiResponse<Slot[]>>;
561
+ /**
562
+ * Mark slot as available/unavailable
563
+ * Note: Use update() method with status or other fields
564
+ */
565
+ setAvailability(id: string, available: boolean): Promise<ApiResponse<Slot>>;
566
+ /**
567
+ * Get all active services for a slot with their items
568
+ * GET /api/v1/slots/:id/services
569
+ *
570
+ * @param id - Slot ID
571
+ * @returns Services with nested service items
572
+ */
573
+ getServices(id: string): Promise<ApiResponse<ServiceWithItems[]>>;
574
+ /**
575
+ * Get slot with tenant information
576
+ * GET /api/v1/slots/:id/tenant-info
577
+ *
578
+ * @param id - Slot ID
579
+ * @returns Slot with tenant data
580
+ */
581
+ getWithTenant(id: string): Promise<ApiResponse<Slot & {
582
+ tenant: Tenant;
583
+ }>>;
584
+ }
585
+
586
+ declare class CustomerClient {
587
+ private client;
588
+ constructor(client: AxiosInstance);
589
+ /**
590
+ * Get customer by ID
591
+ * GET /api/v1/customers/:id
592
+ *
593
+ * @param id - Customer ID
594
+ * @returns Customer details
595
+ */
596
+ getById(id: string): Promise<ApiResponse<Customer>>;
597
+ /**
598
+ * Get customer by email
599
+ * GET /api/v1/customers?email=:email&tenant_id=:id
600
+ *
601
+ * @param email - Customer email
602
+ * @param tenantId - Required tenant ID for scoping
603
+ * @returns Customer details
604
+ */
605
+ getByEmail(email: string, tenantId: string): Promise<ApiResponse<Customer>>;
606
+ /**
607
+ * List customers with optional filters
608
+ * GET /api/v1/customers
609
+ *
610
+ * @param params - Query parameters (tenant_id required, page, limit, search, is_guest)
611
+ * @returns Paginated list of customers
612
+ */
613
+ list(params: {
614
+ tenant_id: string;
615
+ page?: number;
616
+ limit?: number;
617
+ search?: string;
618
+ is_guest?: boolean;
619
+ }): Promise<ApiResponse<Customer[]>>;
620
+ /**
621
+ * Create a new customer
622
+ * POST /api/v1/customers
623
+ *
624
+ * @param data - Customer creation data (tenant_id, name, email required)
625
+ * @returns Created customer
626
+ */
627
+ create(data: Partial<Customer>): Promise<ApiResponse<Customer>>;
628
+ /**
629
+ * Update customer by ID
630
+ * PUT /api/v1/customers/:id
631
+ *
632
+ * @param id - Customer ID
633
+ * @param data - Customer update data
634
+ * @returns Updated customer
635
+ */
636
+ update(id: string, data: Partial<Customer>): Promise<ApiResponse<Customer>>;
637
+ /**
638
+ * Delete customer by ID
639
+ * DELETE /api/v1/customers/:id
640
+ *
641
+ * @param id - Customer ID
642
+ * @returns Empty response on success
643
+ */
644
+ delete(id: string): Promise<ApiResponse<void>>;
645
+ /**
646
+ * Lookup customer by userId or tenantId
647
+ * GET /api/v1/customers/lookup
648
+ *
649
+ * @param params - Query parameters (userId optional, tenantId required)
650
+ * @returns Customer or null
651
+ */
652
+ lookup(params: {
653
+ userId?: string;
654
+ tenantId: string;
655
+ }): Promise<ApiResponse<Customer | null>>;
656
+ /**
657
+ * Get autocomplete data from existing customers and bookings
658
+ * GET /api/v1/customers/autocomplete
659
+ *
660
+ * @param tenantId - Tenant ID (required)
661
+ * @returns Autocomplete data
662
+ */
663
+ autocomplete(tenantId: string): Promise<ApiResponse<any>>;
664
+ /**
665
+ * Get all bookings for a customer
666
+ * GET /api/v1/customers/bookings
667
+ *
668
+ * @param params - Query parameters (email optional, userId optional, tenantId required)
669
+ * @returns Customer bookings data
670
+ */
671
+ getBookings(params: {
672
+ email?: string;
673
+ userId?: string;
674
+ tenantId: string;
675
+ }): Promise<ApiResponse<{
676
+ customer: Customer | null;
677
+ bookings: Booking[];
678
+ groupedBySlot: Record<string, Booking[]>;
679
+ total: number;
680
+ }>>;
681
+ }
682
+
683
+ declare class FlowClient {
684
+ private client;
685
+ constructor(client: AxiosInstance);
686
+ /**
687
+ * Get flow by ID
688
+ * GET /api/v1/flows/:id
689
+ *
690
+ * @param id - Flow ID
691
+ * @returns Flow details
692
+ */
693
+ getById(id: string): Promise<ApiResponse<Flow>>;
694
+ /**
695
+ * List flows with optional filters
696
+ * GET /api/v1/flows
697
+ *
698
+ * @param params - Query parameters (tenantId, page, limit)
699
+ * @returns Paginated list of flows
700
+ */
701
+ list(params?: {
702
+ tenantId?: string;
703
+ page?: number;
704
+ limit?: number;
705
+ }): Promise<ApiResponse<Flow[]>>;
706
+ /**
707
+ * Create a new flow
708
+ * POST /api/v1/flows
709
+ *
710
+ * @param data - Flow creation data
711
+ * @returns Created flow
712
+ */
713
+ create(data: Partial<Flow>): Promise<ApiResponse<Flow>>;
714
+ /**
715
+ * Update flow by ID
716
+ * PATCH /api/v1/flows/:id
717
+ *
718
+ * @param id - Flow ID
719
+ * @param data - Flow update data
720
+ * @returns Updated flow
721
+ */
722
+ update(id: string, data: Partial<Flow>): Promise<ApiResponse<Flow>>;
723
+ /**
724
+ * Delete flow by ID
725
+ * DELETE /api/v1/flows/:id
726
+ *
727
+ * @param id - Flow ID
728
+ * @returns Empty response on success
729
+ */
730
+ delete(id: string): Promise<ApiResponse<void>>;
731
+ /**
732
+ * Execute a flow
733
+ * POST /api/v1/flows/:id/execute
734
+ *
735
+ * @param id - Flow ID
736
+ * @param input - Optional input data for flow execution
737
+ * @returns Flow execution result
738
+ */
739
+ execute(id: string, input?: Record<string, any>): Promise<ApiResponse<any>>;
740
+ }
741
+
742
+ declare class StudioClient {
743
+ private client;
744
+ constructor(client: AxiosInstance);
745
+ /**
746
+ * Get studio by ID
747
+ * GET /api/v1/studios/:id
748
+ *
749
+ * @param id - Studio ID
750
+ * @returns Studio details
751
+ */
752
+ getById(id: string): Promise<ApiResponse<Studio>>;
753
+ /**
754
+ * Get studio by slug
755
+ * GET /api/v1/studios/slug/:slug
756
+ *
757
+ * @param slug - Studio slug
758
+ * @returns Studio details
759
+ */
760
+ getBySlug(slug: string): Promise<ApiResponse<Studio>>;
761
+ /**
762
+ * List studios with optional filters
763
+ * GET /api/v1/studios
764
+ *
765
+ * @param params - Query parameters (page, limit, search)
766
+ * @returns Paginated list of studios
767
+ */
768
+ list(params?: {
769
+ page?: number;
770
+ limit?: number;
771
+ search?: string;
772
+ }): Promise<ApiResponse<Studio[]>>;
773
+ /**
774
+ * Create a new studio
775
+ * POST /api/v1/studios
776
+ *
777
+ * @param data - Studio creation data
778
+ * @returns Created studio
779
+ */
780
+ create(data: Partial<Studio>): Promise<ApiResponse<Studio>>;
781
+ /**
782
+ * Update studio by ID
783
+ * PATCH /api/v1/studios/:id
784
+ *
785
+ * @param id - Studio ID
786
+ * @param data - Studio update data
787
+ * @returns Updated studio
788
+ */
789
+ update(id: string, data: Partial<Studio>): Promise<ApiResponse<Studio>>;
790
+ /**
791
+ * Delete studio by ID
792
+ * DELETE /api/v1/studios/:id
793
+ *
794
+ * @param id - Studio ID
795
+ * @returns Empty response on success
796
+ */
797
+ delete(id: string): Promise<ApiResponse<void>>;
798
+ /**
799
+ * Get data quality issues for a studio
800
+ * GET /api/v1/studios/:id/data-quality
801
+ *
802
+ * @param id - Studio ID
803
+ * @param params - Query parameters (resolved, severity)
804
+ * @returns List of data quality issues
805
+ */
806
+ getDataQualityIssues(id: string, params?: {
807
+ resolved?: boolean;
808
+ severity?: 'low' | 'medium' | 'high';
809
+ }): Promise<ApiResponse<any[]>>;
810
+ }
811
+
812
+ declare class NotificationClient {
813
+ private client;
814
+ constructor(client: AxiosInstance);
815
+ /**
816
+ * Get notification by ID
817
+ * GET /api/v1/notifications/:id
818
+ *
819
+ * @param id - Notification ID
820
+ * @returns Notification details
821
+ */
822
+ getById(id: string): Promise<ApiResponse<Notification>>;
823
+ /**
824
+ * List notifications with optional filters
825
+ * GET /api/v1/notifications
826
+ *
827
+ * @param params - Query parameters (tenantId, type, recipient, page, limit)
828
+ * @returns Paginated list of notifications
829
+ */
830
+ list(params?: {
831
+ tenantId?: string;
832
+ type?: string;
833
+ recipient?: string;
834
+ page?: number;
835
+ limit?: number;
836
+ }): Promise<ApiResponse<Notification[]>>;
837
+ /**
838
+ * Create a new notification
839
+ * POST /api/v1/notifications
840
+ *
841
+ * @param data - Notification creation data
842
+ * @returns Created notification
843
+ */
844
+ create(data: Partial<Notification>): Promise<ApiResponse<Notification>>;
845
+ /**
846
+ * Send notification immediately
847
+ * POST /api/v1/notifications/send
848
+ *
849
+ * @param data - Notification data to send
850
+ * @returns Sent notification
851
+ */
852
+ send(data: Partial<Notification>): Promise<ApiResponse<Notification>>;
853
+ /**
854
+ * Update notification by ID
855
+ * PATCH /api/v1/notifications/:id
856
+ *
857
+ * @param id - Notification ID
858
+ * @param data - Notification update data
859
+ * @returns Updated notification
860
+ */
861
+ update(id: string, data: Partial<Notification>): Promise<ApiResponse<Notification>>;
862
+ /**
863
+ * Delete notification by ID
864
+ * DELETE /api/v1/notifications/:id
865
+ *
866
+ * @param id - Notification ID
867
+ * @returns Empty response on success
868
+ */
869
+ delete(id: string): Promise<ApiResponse<void>>;
870
+ }
871
+
872
+ declare class DataQualityClient {
873
+ private client;
874
+ constructor(client: AxiosInstance);
875
+ /**
876
+ * Get data quality issue by ID
877
+ * GET /api/v1/data-quality/:id
878
+ *
879
+ * @param id - Issue ID
880
+ * @returns Data quality issue details
881
+ */
882
+ getById(id: string): Promise<ApiResponse<DataQualityIssue>>;
883
+ /**
884
+ * List data quality issues with optional filters
885
+ * GET /api/v1/data-quality
886
+ *
887
+ * @param params - Query parameters (tenantId, type, severity, resolved, page, limit)
888
+ * @returns Paginated list of data quality issues
889
+ */
890
+ list(params?: {
891
+ tenantId?: string;
892
+ type?: string;
893
+ severity?: 'low' | 'medium' | 'high';
894
+ resolved?: boolean;
895
+ page?: number;
896
+ limit?: number;
897
+ }): Promise<ApiResponse<DataQualityIssue[]>>;
898
+ /**
899
+ * Run data quality check for a tenant
900
+ * POST /api/v1/data-quality/check
901
+ *
902
+ * @param tenantId - Tenant ID to check
903
+ * @returns List of detected issues
904
+ */
905
+ runCheck(tenantId: string): Promise<ApiResponse<DataQualityIssue[]>>;
906
+ /**
907
+ * Resolve a data quality issue
908
+ * PATCH /api/v1/data-quality/:id/resolve
909
+ *
910
+ * @param id - Issue ID
911
+ * @returns Resolved issue
912
+ */
913
+ resolve(id: string): Promise<ApiResponse<DataQualityIssue>>;
914
+ /**
915
+ * Get data quality summary for a tenant
916
+ * GET /api/v1/data-quality/summary?tenantId=:id
917
+ *
918
+ * @param tenantId - Tenant ID
919
+ * @returns Summary of data quality issues
920
+ */
921
+ getSummary(tenantId: string): Promise<ApiResponse<any>>;
922
+ }
923
+
924
+ /**
925
+ * Custom error classes for Slotly SDK
926
+ */
927
+ declare class SlotlyApiError extends Error {
928
+ code: string;
929
+ statusCode?: number | undefined;
930
+ details?: any | undefined;
931
+ constructor(code: string, message: string, statusCode?: number | undefined, details?: any | undefined);
932
+ }
933
+ declare class SlotlyAuthError extends SlotlyApiError {
934
+ constructor(message: string, details?: any);
935
+ }
936
+ declare class SlotlyNetworkError extends Error {
937
+ originalError?: any | undefined;
938
+ constructor(message: string, originalError?: any | undefined);
939
+ }
940
+ declare class SlotlyConfigurationError extends Error {
941
+ details?: any | undefined;
942
+ constructor(message: string, details?: any | undefined);
943
+ }
944
+
945
+ /**
946
+ * Authentication context extracted from validated Slotly request
947
+ */
948
+ interface SlotlyAuthContext {
949
+ clientKey: string;
950
+ userId?: string;
951
+ tenantId?: string;
952
+ permissions?: string[];
953
+ tokenClaims?: Record<string, any>;
954
+ }
955
+ /**
956
+ * Extended Next.js request with Slotly context
957
+ */
958
+ interface SlotlyRequest extends NextApiRequest {
959
+ slotlyContext: SlotlyAuthContext;
960
+ }
961
+ /**
962
+ * Safely extracts Slotly context from Next.js request
963
+ *
964
+ * Use this helper when you need to access the context outside of the middleware wrapper,
965
+ * or to check if the context exists.
966
+ *
967
+ * **Important:** Other route handlers should guard user-required flows:
968
+ * ```ts
969
+ * const context = getSlotlyContext(req);
970
+ * if (!context?.userId) {
971
+ * return res.status(403).json({
972
+ * success: false,
973
+ * error: { code: 'USER_REQUIRED', message: 'User authentication required' }
974
+ * });
975
+ * }
976
+ * ```
977
+ *
978
+ * @param req Next.js API request
979
+ * @returns SlotlyAuthContext if present, null otherwise
980
+ */
981
+ declare function getSlotlyContext(req: NextApiRequest): SlotlyAuthContext | null;
982
+ /**
983
+ * Middleware wrapper for Next.js API routes and Edge Functions
984
+ * Validates Slotly authentication headers and injects context into request
985
+ *
986
+ * **Supported Environments:**
987
+ * - Next.js API Routes (serverless functions)
988
+ * - Next.js Edge Functions (Edge Runtime)
989
+ * - Vercel Edge Functions
990
+ *
991
+ * **Note on Edge Functions:**
992
+ * Header names are normalized, but edge runtime may have different header casing.
993
+ * This middleware handles both `x-slotly-api-key` and `X-Slotly-Api-Key` variants.
994
+ *
995
+ * **Error Response Format:**
996
+ * All error responses follow the `ApiResponse<T>` format for consistency:
997
+ * ```ts
998
+ * {
999
+ * success: false,
1000
+ * error: {
1001
+ * code: string, // Error code (e.g., 'AUTH_ERROR', 'INTERNAL_ERROR')
1002
+ * message: string, // Human-readable error message
1003
+ * details?: any // Additional error details
1004
+ * }
1005
+ * }
1006
+ * ```
1007
+ *
1008
+ * **Usage:**
1009
+ * ```ts
1010
+ * import { validateSlotlyRequest } from "@slotly/sdk/middleware/validateSlotlyRequest";
1011
+ *
1012
+ * export default validateSlotlyRequest(async (req, res) => {
1013
+ * const { userId, clientKey, tenantId, permissions } = req.slotlyContext;
1014
+ *
1015
+ * // Check if user authentication is required for this endpoint
1016
+ * if (!userId) {
1017
+ * return res.status(403).json({
1018
+ * success: false,
1019
+ * error: { code: 'USER_REQUIRED', message: 'User authentication required' }
1020
+ * });
1021
+ * }
1022
+ *
1023
+ * // Authenticated + scoped — safe to proceed
1024
+ * res.json({ success: true, data: { userId, tenantId } });
1025
+ * });
1026
+ * ```
1027
+ *
1028
+ * **Performance Considerations:**
1029
+ * - For high-throughput scenarios (e.g., many public booking flows), consider:
1030
+ * - Caching API key lookups with TTL (Redis, in-memory cache)
1031
+ * - Rate limiting per client key
1032
+ * - Database connection pooling for key validation
1033
+ *
1034
+ * @param handler Next.js API route handler or Edge Function handler
1035
+ * @returns Wrapped handler with Slotly validation
1036
+ */
1037
+ declare function validateSlotlyRequest(handler: (req: SlotlyRequest, res: NextApiResponse) => Promise<void> | void): (req: NextApiRequest, res: NextApiResponse) => Promise<void>;
1038
+
1039
+ interface SlotlyClientOptions {
1040
+ /**
1041
+ * Required: Function that returns the API key or service token.
1042
+ * This must return a non-empty string.
1043
+ */
1044
+ getClientKey: () => Promise<string>;
1045
+ /**
1046
+ * Optional: Function that returns a Clerk JWT for user traceability.
1047
+ * If provided and returns a token, it will be included in the Authorization header.
1048
+ */
1049
+ getUserToken?: () => Promise<string | null>;
1050
+ /**
1051
+ * Optional: Custom base URL for the API.
1052
+ * Defaults to process.env.SLOTLY_API_URL or 'https://api.slotly.dev'
1053
+ */
1054
+ baseURL?: string;
1055
+ /**
1056
+ * Optional: Additional headers to include with every request.
1057
+ */
1058
+ headers?: Record<string, string>;
1059
+ /**
1060
+ * Optional: Retry configuration.
1061
+ */
1062
+ retry?: {
1063
+ maxRetries?: number;
1064
+ retryDelay?: number;
1065
+ };
1066
+ }
1067
+ /**
1068
+ * Unified Slotly API client
1069
+ * Provides typed access to all Slotly API domains
1070
+ *
1071
+ * Note: RegisterClient has been moved to @slotly/studio-sdk
1072
+ * for studio-specific booking register operations.
1073
+ */
1074
+ interface SlotlyApi {
1075
+ tenant: TenantClient;
1076
+ booking: BookingClient;
1077
+ service: ServiceClient;
1078
+ serviceItem: ServiceItemClient;
1079
+ slot: SlotClient;
1080
+ customer: CustomerClient;
1081
+ flow: FlowClient;
1082
+ studio: StudioClient;
1083
+ notification: NotificationClient;
1084
+ dataQuality: DataQualityClient;
1085
+ }
1086
+ /**
1087
+ * Initialize and configure a new Slotly API client with dual authentication.
1088
+ *
1089
+ * This function creates an isolated Axios instance per call, ensuring:
1090
+ * - SSR safety: Each request/context gets its own client instance
1091
+ * - No cross-contamination: Different auth contexts don't interfere
1092
+ * - Scalable: Supports future expansion for scoped keys, roles, or per-request tokens
1093
+ *
1094
+ * Authentication model:
1095
+ * - **getClientKey()**: Required. Always sets `x-slotly-api-key` header.
1096
+ * Identifies the SDK/application making the request.
1097
+ * - **getUserToken()**: Optional. Sets `Authorization: Bearer <token>` header if provided.
1098
+ * Provides user traceability and authorization scoping.
1099
+ *
1100
+ * @example Basic usage with API key (public SDK client):
1101
+ * ```ts
1102
+ * import { useSlotly } from '@slotly/sdk';
1103
+ *
1104
+ * const slotly = useSlotly({
1105
+ * getClientKey: async () => process.env.SLOTLY_SDK_KEY!,
1106
+ * });
1107
+ * ```
1108
+ *
1109
+ * @example Server-side with Clerk (authenticated, user-scoped):
1110
+ * ```ts
1111
+ * import { getToken } from '@clerk/clerk-sdk-node';
1112
+ * import { useSlotly } from '@slotly/sdk';
1113
+ *
1114
+ * // In an API route or server action
1115
+ * const slotly = useSlotly({
1116
+ * getClientKey: async () => process.env.SLOTLY_SDK_KEY!,
1117
+ * getUserToken: async () => await getToken(req),
1118
+ * });
1119
+ * ```
1120
+ *
1121
+ * @example Client-side with Clerk (Next.js):
1122
+ * ```ts
1123
+ * import { useAuth } from '@clerk/nextjs';
1124
+ * import { useSlotly } from '@slotly/sdk';
1125
+ *
1126
+ * function MyComponent() {
1127
+ * const { getToken } = useAuth();
1128
+ *
1129
+ * const slotly = useSlotly({
1130
+ * getClientKey: async () => process.env.NEXT_PUBLIC_SLOTLY_SDK_KEY!,
1131
+ * getUserToken: getToken,
1132
+ * });
1133
+ * }
1134
+ * ```
1135
+ *
1136
+ * @example Future: Scoped keys or role-based access
1137
+ * ```ts
1138
+ * // Future enhancement: Per-request scoping
1139
+ * const slotly = useSlotly({
1140
+ * getClientKey: async () => getScopedKey(role, tenantId),
1141
+ * getUserToken: async () => getToken(),
1142
+ * });
1143
+ * ```
1144
+ *
1145
+ * @throws {SlotlyConfigurationError} If getClientKey is missing, returns null, undefined, or empty string.
1146
+ */
1147
+ declare const useSlotly: (options: SlotlyClientOptions) => SlotlyApi;
1148
+
1149
+ export { type ApiResponse, type Booking, type Category, type Customer, type DataQualityIssue, type Flow, type FlowStep, type Notification, type Service, type ServiceItem, type ServiceItemWithService, type ServiceWithItems, type Slot, type SlotlyApi, SlotlyApiError, type SlotlyAuthContext, SlotlyAuthError, type SlotlyClientOptions, SlotlyConfigurationError, SlotlyNetworkError, type SlotlyRequest, type Studio, type Tenant, type TenantBranding, type TenantFullConfig, useSlotly as default, getSlotlyContext, useSlotly, validateSlotlyRequest };