@slotchain/sdk 1.1.3 → 1.2.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.
package/dist/index.d.mts CHANGED
@@ -57,20 +57,32 @@ interface TenantBranding {
57
57
  */
58
58
  interface ServiceItem {
59
59
  id: string;
60
- serviceId: string;
60
+ service_id: string;
61
61
  name: string;
62
62
  description?: string;
63
63
  price?: number;
64
64
  duration?: number;
65
- available?: boolean;
65
+ is_available?: boolean;
66
+ sort_order?: number;
67
+ created_at: string;
68
+ updated_at: string;
66
69
  }
67
70
  /**
68
- * Enhanced Service type with nested service items
71
+ * Enhanced Service type with nested service items (for tenant endpoint)
72
+ * Uses 'serviceItems' property name
69
73
  */
70
74
  interface ServiceWithItems extends Service {
71
75
  /** Array of service items nested within this service (optional, may be empty array) */
72
76
  serviceItems?: ServiceItem[];
73
77
  }
78
+ /**
79
+ * Service with items for slot endpoint
80
+ * Uses 'items' property name (different from tenant endpoint)
81
+ */
82
+ interface SlotServiceWithItems extends Service {
83
+ /** Array of service items nested within this service (always included for slot endpoint) */
84
+ items: ServiceItem[];
85
+ }
74
86
  /**
75
87
  * Service item with parent service reference
76
88
  */
@@ -137,11 +149,16 @@ interface ListBookingsOptions {
137
149
  }
138
150
  interface Service {
139
151
  id: string;
140
- tenantId: string;
152
+ tenant_id: string;
153
+ slot_id?: string;
141
154
  name: string;
142
155
  description?: string;
156
+ category?: string;
157
+ is_active?: boolean;
143
158
  duration?: number;
144
159
  price?: number;
160
+ created_at: string;
161
+ updated_at: string;
145
162
  }
146
163
  interface Slot {
147
164
  id: string;
@@ -153,12 +170,93 @@ interface Slot {
153
170
  created_at: string;
154
171
  updated_at: string;
155
172
  }
173
+ /**
174
+ * Customer record — matches the `customers` table.
175
+ */
156
176
  interface Customer {
157
177
  id: string;
178
+ tenant_id: string;
179
+ customer_type: 'individual' | 'organization';
180
+ name: string;
181
+ email: string;
182
+ phone?: string;
183
+ address?: string;
184
+ city?: string;
185
+ state?: string;
186
+ zip_code?: string;
187
+ is_guest?: boolean;
188
+ metadata?: Record<string, unknown>;
189
+ created_at: string;
190
+ updated_at: string;
191
+ }
192
+ /**
193
+ * A row in `customer_users` — links an IDP user (Clerk userId, Auth0 sub, etc.) to a Customer.
194
+ */
195
+ interface CustomerIdentity {
196
+ id: string;
197
+ customer_id: string;
198
+ idp_id: string;
199
+ role: 'owner' | 'admin' | 'member';
200
+ is_primary: boolean;
201
+ created_at: string;
202
+ updated_at: string;
203
+ }
204
+ /**
205
+ * Customer with their associated IDP identities (from `customer_users` join).
206
+ */
207
+ interface CustomerWithIdentities extends Customer {
208
+ identities?: CustomerIdentity[];
209
+ }
210
+ /**
211
+ * Notification preferences stored in `customer.metadata.notification_preferences`.
212
+ */
213
+ interface NotificationPreferences {
214
+ email?: boolean;
215
+ sms?: boolean;
216
+ push?: boolean;
217
+ booking_confirmation?: boolean;
218
+ booking_reminder?: boolean;
219
+ booking_cancellation?: boolean;
220
+ marketing?: boolean;
221
+ [key: string]: boolean | undefined;
222
+ }
223
+ /**
224
+ * Customer with their bookings, as returned by GET /api/v1/customers/bookings.
225
+ */
226
+ interface CustomerWithBookings {
227
+ customer: Customer;
228
+ bookings: Booking[];
229
+ /** Bookings organised by slot ID → array of bookings */
230
+ groupedBySlot: Record<string, Booking[]>;
231
+ total: number;
232
+ total_slots: number;
233
+ }
234
+ /**
235
+ * Options for resolving a customer from an IDP identity or email.
236
+ * At least one of `idpId` or `email` must be provided.
237
+ */
238
+ interface ResolveCustomerOptions {
158
239
  tenantId: string;
240
+ /** IDP user ID (Clerk userId, Auth0 sub, etc.) — checked first */
241
+ idpId?: string;
242
+ /** Email address — used as fallback if idpId lookup returns nothing */
159
243
  email?: string;
244
+ }
245
+ /**
246
+ * Options for atomic customer upsert (find or create by email within a tenant).
247
+ */
248
+ interface FindOrCreateCustomerOptions {
249
+ tenant_id: string;
250
+ email: string;
160
251
  name?: string;
161
252
  phone?: string;
253
+ address?: string;
254
+ city?: string;
255
+ state?: string;
256
+ zip_code?: string;
257
+ customer_type?: 'individual' | 'organization';
258
+ is_guest?: boolean;
259
+ metadata?: Record<string, unknown>;
162
260
  }
163
261
  interface Flow {
164
262
  id: string;
@@ -220,6 +318,63 @@ interface GetOrganizationOptions {
220
318
  limit?: number;
221
319
  is_public?: boolean;
222
320
  }
321
+ /**
322
+ * Artifact / document attached to a customer or booking
323
+ */
324
+ type ArtifactEntityType = 'customer' | 'booking' | 'slot' | 'service' | 'tenant';
325
+ type ArtifactMimeCategory = 'pdf' | 'document' | 'image' | 'spreadsheet' | 'video' | 'audio' | 'other';
326
+ interface Artifact {
327
+ id: string;
328
+ tenant_id: string;
329
+ /** The Slotly entity this file is attached to */
330
+ related_entity_type: ArtifactEntityType;
331
+ related_entity_id: string;
332
+ /** Original filename supplied by the uploader */
333
+ original_filename: string;
334
+ /** Storage path within the tenant bucket (opaque — use url/signed_url to access) */
335
+ storage_path: string;
336
+ /** MIME category, derived server-side */
337
+ mime_category: ArtifactMimeCategory;
338
+ mime_type?: string;
339
+ size_bytes?: number;
340
+ /** Optional label, e.g. "CV", "Invoice", "Contract" */
341
+ label?: string;
342
+ metadata?: Record<string, unknown>;
343
+ uploaded_by_user_id?: string;
344
+ created_at: string;
345
+ updated_at: string;
346
+ }
347
+ /**
348
+ * Artifact with a short-lived signed download URL (returned from getUrl / list with signed=true)
349
+ */
350
+ interface ArtifactWithUrl extends Artifact {
351
+ signed_url: string;
352
+ /** Unix timestamp (seconds) when the signed_url expires */
353
+ signed_url_expires_at: number;
354
+ }
355
+ interface ListArtifactsOptions {
356
+ related_entity_type?: ArtifactEntityType;
357
+ related_entity_id?: string;
358
+ /** Include signed download URLs in each result (adds latency) */
359
+ signed?: boolean;
360
+ mime_category?: ArtifactMimeCategory;
361
+ label?: string;
362
+ page?: number;
363
+ limit?: number;
364
+ }
365
+ interface UploadArtifactOptions {
366
+ /** The file binary */
367
+ file: Blob | Buffer | ArrayBuffer;
368
+ /** Original filename */
369
+ filename: string;
370
+ /** The entity this artifact belongs to */
371
+ related_entity_type: ArtifactEntityType;
372
+ related_entity_id: string;
373
+ /** Optional label for the UI, e.g. "CV", "Invoice" */
374
+ label?: string;
375
+ mime_type?: string;
376
+ metadata?: Record<string, unknown>;
377
+ }
223
378
  /**
224
379
  * Full tenant configuration including branding, services, and service items
225
380
  * Service items are nested within each service object (not as a separate top-level array)
@@ -488,10 +643,45 @@ declare class ServiceClient {
488
643
  * @returns Service details (with items if includeItems=true)
489
644
  */
490
645
  getById(id: string, includeItems?: boolean): Promise<ApiResponse<Service | ServiceWithItems>>;
646
+ /**
647
+ * Get services for a tenant (full list with optional filters)
648
+ * GET /api/v1/services
649
+ *
650
+ * @param options - Query options
651
+ * @param options.tenantId - Tenant ID (UUID) - required if tenantSlug not provided
652
+ * @param options.tenantSlug - Tenant slug - required if tenantId not provided
653
+ * @param options.slotId - Slot ID (DEPRECATED - accepted but ignored for API compatibility)
654
+ * @param options.isActive - Filter by active status
655
+ * @param options.includeItems - Include nested service items (default: false)
656
+ * @param options.page - Page number (default: 1)
657
+ * @param options.limit - Items per page (default: 20, max: 100)
658
+ * @returns Services with optional pagination metadata
659
+ */
660
+ getServicesByTenant(options: {
661
+ tenantId?: string;
662
+ tenantSlug?: string;
663
+ slotId?: string;
664
+ isActive?: boolean;
665
+ includeItems?: boolean;
666
+ page?: number;
667
+ limit?: number;
668
+ }): Promise<ApiResponse<Service[] | ServiceWithItems[]>>;
669
+ /**
670
+ * Get services for a specific slot
671
+ * GET /api/v1/slots/:id/services
672
+ *
673
+ * Note: Services are tenant-level entities. This endpoint returns all active services
674
+ * for the slot's tenant with items included. The response uses 'items' property (not 'serviceItems').
675
+ *
676
+ * @param slotId - Slot ID (UUID)
677
+ * @returns Services with nested items (always includes items, only active services)
678
+ */
679
+ getServicesBySlot(slotId: string): Promise<ApiResponse<SlotServiceWithItems[]>>;
491
680
  /**
492
681
  * List services by tenant slug
493
682
  * GET /api/v1/services?tenantSlug=:slug
494
683
  *
684
+ * @deprecated Consider using getServicesByTenant() for better type safety and validation
495
685
  * @param slug - Tenant slug (or use tenant_id in list() method)
496
686
  * @param params - Optional parameters (includeItems, page, limit, is_active)
497
687
  * @returns List of services for the tenant
@@ -506,6 +696,7 @@ declare class ServiceClient {
506
696
  * List services with optional filters
507
697
  * GET /api/v1/services
508
698
  *
699
+ * @deprecated Consider using getServicesByTenant() for better type safety and validation
509
700
  * @param params - Query parameters (tenant_id, tenantSlug, slot_id, page, limit, is_active, includeItems)
510
701
  * @returns Paginated list of services
511
702
  */
@@ -728,10 +919,13 @@ declare class SlotClient {
728
919
  * Get all active services for a slot with their items
729
920
  * GET /api/v1/slots/:id/services
730
921
  *
731
- * @param id - Slot ID
732
- * @returns Services with nested service items
922
+ * Note: Services are tenant-level entities. This endpoint returns all active services
923
+ * for the slot's tenant with items included. The response uses 'items' property (not 'serviceItems').
924
+ *
925
+ * @param id - Slot ID (UUID)
926
+ * @returns Services with nested items (always includes items, only active services)
733
927
  */
734
- getServices(id: string): Promise<ApiResponse<ServiceWithItems[]>>;
928
+ getServices(id: string): Promise<ApiResponse<SlotServiceWithItems[]>>;
735
929
  /**
736
930
  * Get slot with tenant information
737
931
  * GET /api/v1/slots/:id/tenant-info
@@ -781,11 +975,12 @@ declare class CustomerClient {
781
975
  /**
782
976
  * Create a new customer
783
977
  * POST /api/v1/customers
784
- *
785
- * @param data - Customer creation data (tenant_id, name, email required)
786
- * @returns Created customer
787
978
  */
788
- create(data: Partial<Customer>): Promise<ApiResponse<Customer>>;
979
+ create(data: Partial<Customer> & {
980
+ tenant_id: string;
981
+ name: string;
982
+ email: string;
983
+ }): Promise<ApiResponse<Customer>>;
789
984
  /**
790
985
  * Update customer by ID
791
986
  * PUT /api/v1/customers/:id
@@ -815,30 +1010,67 @@ declare class CustomerClient {
815
1010
  tenantId: string;
816
1011
  }): Promise<ApiResponse<Customer | null>>;
817
1012
  /**
818
- * Get autocomplete data from existing customers and bookings
819
- * GET /api/v1/customers/autocomplete
820
- *
821
- * @param tenantId - Tenant ID (required)
822
- * @returns Autocomplete data
1013
+ * Get autocomplete data from existing customers and bookings.
1014
+ * GET /api/v1/customers/autocomplete?tenantId=:tenantId
823
1015
  */
824
- autocomplete(tenantId: string): Promise<ApiResponse<any>>;
1016
+ autocomplete(tenantId: string): Promise<ApiResponse<{
1017
+ companies: string[];
1018
+ jobTitles: string[];
1019
+ experienceLevels: string[];
1020
+ cities: string[];
1021
+ states: string[];
1022
+ zipCodes: string[];
1023
+ }>>;
825
1024
  /**
826
- * Get all bookings for a customer
827
- * GET /api/v1/customers/bookings
828
- *
829
- * @param params - Query parameters (email optional, userId optional, tenantId required)
830
- * @returns Customer bookings data
1025
+ * Get all bookings for a customer, grouped by slot.
1026
+ * GET /api/v1/customers/bookings?tenantId=:id
831
1027
  */
832
1028
  getBookings(params: {
833
1029
  email?: string;
834
1030
  userId?: string;
835
1031
  tenantId: string;
836
- }): Promise<ApiResponse<{
837
- customer: Customer | null;
838
- bookings: Booking[];
839
- groupedBySlot: Record<string, Booking[]>;
840
- total: number;
1032
+ }): Promise<ApiResponse<CustomerWithBookings>>;
1033
+ /**
1034
+ * Atomic upsert: find an existing customer by email within a tenant, or create one.
1035
+ * POST /api/v1/customers/find-or-create
1036
+ */
1037
+ findOrCreate(options: FindOrCreateCustomerOptions): Promise<ApiResponse<Customer & {
1038
+ created: boolean;
841
1039
  }>>;
1040
+ /**
1041
+ * Resolve a customer from an IDP identity or email.
1042
+ * Tries idpId lookup (customer_users) first, then falls back to email.
1043
+ * POST /api/v1/customers/resolve
1044
+ */
1045
+ resolve(options: ResolveCustomerOptions): Promise<ApiResponse<Customer | null>>;
1046
+ /**
1047
+ * Get a customer with their linked IDP identities (customer_users rows).
1048
+ * GET /api/v1/customers/:id?include_identities=true
1049
+ */
1050
+ getWithIdentities(id: string): Promise<ApiResponse<CustomerWithIdentities>>;
1051
+ /**
1052
+ * Link an IDP user ID to a customer (creates a customer_users row).
1053
+ * POST /api/v1/customers/:id/users
1054
+ */
1055
+ linkUser(customerId: string, idpId: string, options?: {
1056
+ role?: CustomerIdentity['role'];
1057
+ is_primary?: boolean;
1058
+ }): Promise<ApiResponse<CustomerIdentity>>;
1059
+ /**
1060
+ * Unlink an IDP user from a customer (deletes the customer_users row).
1061
+ * DELETE /api/v1/customers/:id/users/:idpId
1062
+ */
1063
+ unlinkUser(customerId: string, idpId: string): Promise<ApiResponse<void>>;
1064
+ /**
1065
+ * Get notification preferences for a customer.
1066
+ * GET /api/v1/customers/:id/preferences/notifications
1067
+ */
1068
+ getNotificationPreferences(customerId: string): Promise<ApiResponse<NotificationPreferences>>;
1069
+ /**
1070
+ * Update notification preferences for a customer (partial merge).
1071
+ * PATCH /api/v1/customers/:id/preferences/notifications
1072
+ */
1073
+ updateNotificationPreferences(customerId: string, preferences: Partial<NotificationPreferences>): Promise<ApiResponse<NotificationPreferences>>;
842
1074
  }
843
1075
 
844
1076
  declare class FlowClient {
@@ -1129,6 +1361,68 @@ declare class OrganizationClient {
1129
1361
  getBySlug(slug: string, options?: GetOrganizationOptions): Promise<ApiResponse<Organization | OrganizationFullConfig>>;
1130
1362
  }
1131
1363
 
1364
+ /**
1365
+ * DocumentClient — manage file artifacts attached to Slotly entities.
1366
+ *
1367
+ * Artifacts are tenant-scoped files (CVs, invoices, images, etc.) linked
1368
+ * to a customer, booking, slot, service, or tenant record.
1369
+ *
1370
+ * @example Upload a CV for a customer
1371
+ * ```ts
1372
+ * const artifact = await slotly.document.upload({
1373
+ * file: buffer,
1374
+ * filename: 'cv-2024.pdf',
1375
+ * related_entity_type: 'customer',
1376
+ * related_entity_id: customerId,
1377
+ * label: 'CV',
1378
+ * });
1379
+ * ```
1380
+ *
1381
+ * @example List all CVs for a customer
1382
+ * ```ts
1383
+ * const { data } = await slotly.document.listByEntity('customer', customerId, { signed: true });
1384
+ * ```
1385
+ */
1386
+ declare class DocumentClient {
1387
+ private readonly client;
1388
+ private readonly base;
1389
+ constructor(client: AxiosInstance);
1390
+ /**
1391
+ * Upload a file artifact.
1392
+ * Sends as multipart/form-data.
1393
+ */
1394
+ upload(options: UploadArtifactOptions): Promise<Artifact>;
1395
+ /**
1396
+ * List artifacts for a specific entity.
1397
+ * Pass `signed: true` to get download URLs in each result.
1398
+ */
1399
+ listByEntity(entityType: ArtifactEntityType, entityId: string, options?: Omit<ListArtifactsOptions, 'related_entity_type' | 'related_entity_id'>): Promise<ApiResponse<(Artifact | ArtifactWithUrl)[]>>;
1400
+ /**
1401
+ * List artifacts with full filter options.
1402
+ */
1403
+ list(options: ListArtifactsOptions): Promise<ApiResponse<(Artifact | ArtifactWithUrl)[]>>;
1404
+ /**
1405
+ * Get a single artifact by ID.
1406
+ */
1407
+ get(id: string, signed?: boolean): Promise<Artifact | ArtifactWithUrl>;
1408
+ /**
1409
+ * Get a signed download URL for an artifact.
1410
+ * Returns the full ArtifactWithUrl including `signed_url` and `signed_url_expires_at`.
1411
+ */
1412
+ getUrl(id: string): Promise<ArtifactWithUrl>;
1413
+ /**
1414
+ * Delete an artifact and its stored file.
1415
+ */
1416
+ delete(id: string): Promise<void>;
1417
+ /**
1418
+ * Update artifact label or metadata.
1419
+ */
1420
+ update(id: string, patch: {
1421
+ label?: string;
1422
+ metadata?: Record<string, unknown>;
1423
+ }): Promise<Artifact>;
1424
+ }
1425
+
1132
1426
  /**
1133
1427
  * Custom error classes for Slotly SDK
1134
1428
  */
@@ -1291,6 +1585,8 @@ interface SlotlyApi {
1291
1585
  notification: NotificationClient;
1292
1586
  dataQuality: DataQualityClient;
1293
1587
  organization: OrganizationClient;
1588
+ /** Upload and retrieve file artifacts (CVs, invoices, images) linked to any Slotly entity */
1589
+ document: DocumentClient;
1294
1590
  }
1295
1591
  /**
1296
1592
  * Initialize and configure a new Slotly API client with dual authentication.
@@ -1355,4 +1651,4 @@ interface SlotlyApi {
1355
1651
  */
1356
1652
  declare const useSlotly: (options: SlotlyClientOptions) => SlotlyApi;
1357
1653
 
1358
- export { type ApiResponse, type Booking, type BookingWithSlot, type Category, type Customer, type DataQualityIssue, type Flow, type FlowStep, type GetOrganizationOptions, type ListBookingsOptions, type Notification, type Organization, type OrganizationFullConfig, 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 };
1654
+ export { type ApiResponse, type Artifact, type ArtifactEntityType, type ArtifactMimeCategory, type ArtifactWithUrl, type Booking, type BookingWithSlot, type Category, type Customer, type CustomerIdentity, type CustomerWithBookings, type CustomerWithIdentities, type DataQualityIssue, type FindOrCreateCustomerOptions, type Flow, type FlowStep, type GetOrganizationOptions, type ListArtifactsOptions, type ListBookingsOptions, type Notification, type NotificationPreferences, type Organization, type OrganizationFullConfig, type ResolveCustomerOptions, type Service, type ServiceItem, type ServiceItemWithService, type ServiceWithItems, type Slot, type SlotServiceWithItems, type SlotlyApi, SlotlyApiError, type SlotlyAuthContext, SlotlyAuthError, type SlotlyClientOptions, SlotlyConfigurationError, SlotlyNetworkError, type SlotlyRequest, type Studio, type Tenant, type TenantBranding, type TenantFullConfig, type UploadArtifactOptions, useSlotly as default, getSlotlyContext, useSlotly, validateSlotlyRequest };