@praxium/sdk 0.2.4

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,707 @@
1
+ /**
2
+ * Tenant identifier slug
3
+ */
4
+ type TenantSlug = string;
5
+ /**
6
+ * Weekly opening hours schedule. Null if not configured.
7
+ */
8
+ type OpeningHours = {
9
+ /**
10
+ * Weekly schedule (7 entries, Mon-Sun)
11
+ */
12
+ schedule: Array<PublicDaySchedule>;
13
+ /**
14
+ * General note about opening hours
15
+ */
16
+ globalNote: string | null;
17
+ } | null;
18
+ /**
19
+ * Daily opening schedule entry
20
+ */
21
+ type PublicDaySchedule = {
22
+ /**
23
+ * Day of week (0=Monday, 6=Sunday)
24
+ */
25
+ dayIndex: number;
26
+ /**
27
+ * Localized day name
28
+ */
29
+ dayName: string;
30
+ /**
31
+ * Opening hours text
32
+ */
33
+ hours: string | null;
34
+ /**
35
+ * Whether the practice is closed on this day
36
+ */
37
+ isClosed: boolean;
38
+ /**
39
+ * Additional note for this day
40
+ */
41
+ note: string | null;
42
+ };
43
+ /**
44
+ * Standard error response
45
+ */
46
+ type ApiError = {
47
+ /**
48
+ * Error type
49
+ */
50
+ error: string;
51
+ /**
52
+ * Machine-readable error code
53
+ */
54
+ errorCode: string;
55
+ /**
56
+ * Human-readable error message
57
+ */
58
+ message: string;
59
+ /**
60
+ * Validation error details (only for 400 responses)
61
+ */
62
+ details?: Array<{
63
+ /**
64
+ * Field path that failed validation
65
+ */
66
+ field: string;
67
+ /**
68
+ * Validation error message
69
+ */
70
+ message: string;
71
+ }>;
72
+ };
73
+ /**
74
+ * Practice contact details. Null if not configured.
75
+ */
76
+ type ContactDetails = {
77
+ /**
78
+ * Main phone number
79
+ */
80
+ phone: string;
81
+ /**
82
+ * WhatsApp contact number
83
+ */
84
+ whatsappPhone: string;
85
+ /**
86
+ * Contact email address
87
+ */
88
+ email: string;
89
+ } | null;
90
+ /**
91
+ * Contact form submission result
92
+ */
93
+ type ContactFormResult = {
94
+ /**
95
+ * Whether the form was submitted successfully
96
+ */
97
+ success: boolean;
98
+ /**
99
+ * Email delivery status
100
+ */
101
+ emailStatus: string;
102
+ };
103
+ /**
104
+ * Practice location with coordinates and directions. Null if not configured.
105
+ */
106
+ type Location = {
107
+ /**
108
+ * Street address
109
+ */
110
+ address: string;
111
+ /**
112
+ * City name
113
+ */
114
+ city: string;
115
+ /**
116
+ * Postal/ZIP code
117
+ */
118
+ postalCode: string;
119
+ /**
120
+ * Country name (localized)
121
+ */
122
+ country: string;
123
+ /**
124
+ * Complete formatted address
125
+ */
126
+ fullAddress: string;
127
+ /**
128
+ * GPS latitude coordinate
129
+ */
130
+ latitude: number | null;
131
+ /**
132
+ * GPS longitude coordinate
133
+ */
134
+ longitude: number | null;
135
+ /**
136
+ * Google Maps URL for the practice
137
+ */
138
+ googleMapsUrl: string | null;
139
+ /**
140
+ * Parking information (localized)
141
+ */
142
+ parkingInfo: string | null;
143
+ /**
144
+ * Accessibility information (localized)
145
+ */
146
+ accessibility: string | null;
147
+ } | null;
148
+ /**
149
+ * Practice business name
150
+ */
151
+ type BusinessName = {
152
+ /**
153
+ * Practice name (localized)
154
+ */
155
+ name: string;
156
+ };
157
+ /**
158
+ * Social media profile URLs. Null if not configured.
159
+ */
160
+ type SocialLinks = {
161
+ /**
162
+ * Facebook page URL
163
+ */
164
+ facebook?: string;
165
+ /**
166
+ * Instagram profile URL
167
+ */
168
+ instagram?: string;
169
+ /**
170
+ * LinkedIn page URL
171
+ */
172
+ linkedin?: string;
173
+ /**
174
+ * Twitter/X profile URL
175
+ */
176
+ twitter?: string;
177
+ } | null;
178
+ /**
179
+ * List of team members visible on the public team page
180
+ */
181
+ type TeamMembers = Array<PublicTeamMember>;
182
+ /**
183
+ * Public team member profile
184
+ */
185
+ type PublicTeamMember = {
186
+ /**
187
+ * Staff member UUID
188
+ */
189
+ id: string;
190
+ /**
191
+ * Public display name
192
+ */
193
+ publicName: string;
194
+ /**
195
+ * Public profile image URL (Supabase CDN)
196
+ */
197
+ publicImage: string;
198
+ /**
199
+ * List of specializations
200
+ */
201
+ specializations: Array<LocalizedSpecialization>;
202
+ /**
203
+ * Staff biography in NL and EN
204
+ */
205
+ bio: BilingualText | null;
206
+ /**
207
+ * BIG registration number (Dutch healthcare registration)
208
+ */
209
+ bigNumber: string | null;
210
+ };
211
+ /**
212
+ * Specialization with bilingual names
213
+ */
214
+ type LocalizedSpecialization = {
215
+ /**
216
+ * Dutch specialization name
217
+ */
218
+ nl: string;
219
+ /**
220
+ * English specialization name
221
+ */
222
+ en: string;
223
+ };
224
+ /**
225
+ * Bilingual text with Dutch (nl) and English (en) translations
226
+ */
227
+ type BilingualText = {
228
+ /**
229
+ * Dutch text
230
+ */
231
+ nl: string;
232
+ /**
233
+ * English text
234
+ */
235
+ en: string;
236
+ };
237
+ /**
238
+ * FAQs grouped by category, ordered by category.order then faq.order
239
+ */
240
+ type FaqContent = Array<FaqGroup>;
241
+ /**
242
+ * FAQ category with its items
243
+ */
244
+ type FaqGroup = {
245
+ /**
246
+ * The FAQ category
247
+ */
248
+ category: FaqCategory;
249
+ /**
250
+ * FAQ items in this category
251
+ */
252
+ faqs: Array<FaqItem>;
253
+ };
254
+ /**
255
+ * FAQ category with bilingual name
256
+ */
257
+ type FaqCategory = {
258
+ /**
259
+ * Category UUID
260
+ */
261
+ id: string;
262
+ /**
263
+ * Category name per locale
264
+ */
265
+ name: {
266
+ [key: string]: string;
267
+ };
268
+ /**
269
+ * Display order (ascending)
270
+ */
271
+ order: number;
272
+ };
273
+ /**
274
+ * Individual FAQ question and answer
275
+ */
276
+ type FaqItem = {
277
+ /**
278
+ * FAQ item UUID
279
+ */
280
+ id: string;
281
+ /**
282
+ * Question text per locale
283
+ */
284
+ question: {
285
+ [key: string]: string;
286
+ };
287
+ /**
288
+ * Answer text per locale (may contain HTML)
289
+ */
290
+ answer: {
291
+ [key: string]: string;
292
+ };
293
+ /**
294
+ * Parent category UUID
295
+ */
296
+ categoryId: string;
297
+ /**
298
+ * Display order within category (ascending)
299
+ */
300
+ order: number;
301
+ /**
302
+ * Whether this FAQ item is currently visible
303
+ */
304
+ visible: boolean;
305
+ };
306
+ /**
307
+ * All service variants ordered by category, service, then variant order
308
+ */
309
+ type PricingVariants = Array<PricingVariant>;
310
+ /**
311
+ * Service variant with pricing and category info
312
+ */
313
+ type PricingVariant = {
314
+ /**
315
+ * Variant UUID
316
+ */
317
+ id: string;
318
+ /**
319
+ * Parent service name per locale
320
+ */
321
+ serviceName: {
322
+ [key: string]: string;
323
+ };
324
+ /**
325
+ * Variant name per locale (null if single variant)
326
+ */
327
+ name: {
328
+ [key: string]: string;
329
+ } | null;
330
+ /**
331
+ * Variant description per locale
332
+ */
333
+ description: {
334
+ [key: string]: string;
335
+ } | null;
336
+ /**
337
+ * Duration in minutes
338
+ */
339
+ duration: number;
340
+ /**
341
+ * Price in euros (cents)
342
+ */
343
+ price: number;
344
+ /**
345
+ * Display order within service
346
+ */
347
+ order: number;
348
+ /**
349
+ * Parent service category
350
+ */
351
+ category: ServiceCategoryInfo;
352
+ /**
353
+ * Category display order
354
+ */
355
+ categoryOrder: number;
356
+ /**
357
+ * Service display order within category
358
+ */
359
+ serviceOrder: number;
360
+ };
361
+ /**
362
+ * Service category metadata
363
+ */
364
+ type ServiceCategoryInfo = {
365
+ /**
366
+ * Category UUID
367
+ */
368
+ id: string;
369
+ /**
370
+ * Category name per locale
371
+ */
372
+ name: {
373
+ [key: string]: string;
374
+ };
375
+ /**
376
+ * URL-safe category identifier
377
+ */
378
+ slug: string;
379
+ /**
380
+ * Category color hex code
381
+ */
382
+ color: string | null;
383
+ };
384
+ /**
385
+ * Insurance information items ordered by display order
386
+ */
387
+ type InsuranceList = Array<InsuranceInfo>;
388
+ /**
389
+ * Insurance coverage information
390
+ */
391
+ type InsuranceInfo = {
392
+ /**
393
+ * Insurance info UUID
394
+ */
395
+ id: string;
396
+ /**
397
+ * Title per locale
398
+ */
399
+ title: {
400
+ [key: string]: string;
401
+ };
402
+ /**
403
+ * Description per locale (may contain HTML)
404
+ */
405
+ description: {
406
+ [key: string]: string;
407
+ };
408
+ /**
409
+ * Display order (ascending)
410
+ */
411
+ order: number;
412
+ /**
413
+ * ISO 8601 creation timestamp
414
+ */
415
+ createdAt: string;
416
+ /**
417
+ * ISO 8601 last update timestamp
418
+ */
419
+ updatedAt: string;
420
+ };
421
+ /**
422
+ * Practice feature items ordered by display order
423
+ */
424
+ type FeatureList = Array<FeatureItem>;
425
+ /**
426
+ * Practice feature or highlight
427
+ */
428
+ type FeatureItem = {
429
+ /**
430
+ * Feature item UUID
431
+ */
432
+ id: string;
433
+ /**
434
+ * Feature text per locale
435
+ */
436
+ text: {
437
+ [key: string]: string;
438
+ };
439
+ /**
440
+ * Display order (ascending)
441
+ */
442
+ order: number;
443
+ /**
444
+ * ISO 8601 creation timestamp
445
+ */
446
+ createdAt: string;
447
+ /**
448
+ * ISO 8601 last update timestamp
449
+ */
450
+ updatedAt: string;
451
+ };
452
+ /**
453
+ * Accepted payment methods ordered by display order
454
+ */
455
+ type PaymentMethodList = Array<PaymentMethod>;
456
+ /**
457
+ * Accepted payment method
458
+ */
459
+ type PaymentMethod = {
460
+ /**
461
+ * Payment method UUID
462
+ */
463
+ id: string;
464
+ /**
465
+ * Payment method name per locale
466
+ */
467
+ name: {
468
+ [key: string]: string;
469
+ };
470
+ /**
471
+ * Display order (ascending)
472
+ */
473
+ order: number;
474
+ /**
475
+ * ISO 8601 creation timestamp
476
+ */
477
+ createdAt: string;
478
+ /**
479
+ * ISO 8601 last update timestamp
480
+ */
481
+ updatedAt: string;
482
+ };
483
+ /**
484
+ * Practice policy items ordered by display order
485
+ */
486
+ type PolicyList = Array<PolicyInfo>;
487
+ /**
488
+ * Cancellation/practice policy information
489
+ */
490
+ type PolicyInfo = {
491
+ /**
492
+ * Policy info UUID
493
+ */
494
+ id: string;
495
+ /**
496
+ * Policy title per locale
497
+ */
498
+ title: {
499
+ [key: string]: string;
500
+ };
501
+ /**
502
+ * Policy description per locale (may contain HTML)
503
+ */
504
+ description: {
505
+ [key: string]: string;
506
+ };
507
+ /**
508
+ * Display order (ascending)
509
+ */
510
+ order: number;
511
+ /**
512
+ * ISO 8601 creation timestamp
513
+ */
514
+ createdAt: string;
515
+ /**
516
+ * ISO 8601 last update timestamp
517
+ */
518
+ updatedAt: string;
519
+ };
520
+ /**
521
+ * All bookable services with their variants and categories
522
+ */
523
+ type BookableServices = Array<BookableService>;
524
+ /**
525
+ * Service available for online booking with its variants
526
+ */
527
+ type BookableService = {
528
+ /**
529
+ * Service UUID
530
+ */
531
+ id: string;
532
+ /**
533
+ * URL-safe service identifier
534
+ */
535
+ slug: string;
536
+ /**
537
+ * Service name per locale
538
+ */
539
+ name: {
540
+ [key: string]: string;
541
+ };
542
+ /**
543
+ * Service description per locale
544
+ */
545
+ description: {
546
+ [key: string]: string;
547
+ };
548
+ /**
549
+ * Service category
550
+ */
551
+ category: ServiceCategoryInfo;
552
+ /**
553
+ * Service color hex code
554
+ */
555
+ color: string | null;
556
+ /**
557
+ * Available booking variants
558
+ */
559
+ variants: Array<BookableVariantInfo>;
560
+ };
561
+ /**
562
+ * Bookable service variant with pricing
563
+ */
564
+ type BookableVariantInfo = {
565
+ /**
566
+ * Variant UUID
567
+ */
568
+ id: string;
569
+ /**
570
+ * Variant name per locale (null for single-variant services)
571
+ */
572
+ name: {
573
+ [key: string]: string;
574
+ } | null;
575
+ /**
576
+ * Duration in minutes
577
+ */
578
+ duration: number;
579
+ /**
580
+ * Price in euros (cents)
581
+ */
582
+ price: number;
583
+ /**
584
+ * Display order within service
585
+ */
586
+ order: number;
587
+ /**
588
+ * Whether this is the default variant for the service
589
+ */
590
+ isDefault: boolean;
591
+ /**
592
+ * Cal.com event type slug for online booking
593
+ */
594
+ externalEventTypeSlug: string | null;
595
+ /**
596
+ * Variant description per locale
597
+ */
598
+ description: {
599
+ [key: string]: string;
600
+ } | null;
601
+ };
602
+ type SubmitContactFormData = {
603
+ body: {
604
+ name: string;
605
+ email: string;
606
+ phone?: string;
607
+ subject: string;
608
+ message: string;
609
+ };
610
+ path: {
611
+ /**
612
+ * Tenant identifier slug
613
+ */
614
+ tenantSlug: TenantSlug;
615
+ };
616
+ query?: never;
617
+ url: '/api/public/{tenantSlug}/contact';
618
+ };
619
+
620
+ interface TenantClientConfig {
621
+ /** Platform base URL (e.g., 'https://platform.praxium.nl') */
622
+ baseUrl: string;
623
+ /** HMAC-signed API key (format: hmac_v1_{slug}_{timestamp}_{signature}) */
624
+ apiKey: string;
625
+ /** Tenant identifier slug (must match the slug in the API key) */
626
+ tenantSlug: string;
627
+ /** Accept-Language header for locale-aware responses (e.g., 'nl', 'en') */
628
+ locale: string;
629
+ }
630
+ declare function createTenantClient(config: TenantClientConfig): {
631
+ getOpeningHours: () => Promise<OpeningHours>;
632
+ getContactDetails: () => Promise<ContactDetails>;
633
+ getLocation: () => Promise<Location>;
634
+ getBusinessName: () => Promise<BusinessName>;
635
+ getSocialLinks: () => Promise<SocialLinks>;
636
+ getTeamMembers: () => Promise<TeamMembers>;
637
+ getFaq: () => Promise<FaqContent>;
638
+ getServiceVariants: () => Promise<PricingVariants>;
639
+ getInsuranceInfo: () => Promise<InsuranceList>;
640
+ getFeatures: () => Promise<FeatureList>;
641
+ getPaymentMethods: () => Promise<PaymentMethodList>;
642
+ getPolicyInfo: () => Promise<PolicyList>;
643
+ getBookableServices: () => Promise<BookableServices>;
644
+ submitContactForm: (body: SubmitContactFormData["body"]) => Promise<ContactFormResult>;
645
+ };
646
+ /** Return type of createTenantClient for type inference */
647
+ type TenantClient = ReturnType<typeof createTenantClient>;
648
+
649
+ /**
650
+ * Typed error hierarchy for the Praxium SDK.
651
+ *
652
+ * Every method on `TenantClient` throws one of these errors when the
653
+ * API returns a non-2xx response. Consumers can catch a specific
654
+ * subclass (e.g. `PraxiumNotFoundError`) or the base `PraxiumError`.
655
+ *
656
+ * @example
657
+ * ```ts
658
+ * try {
659
+ * const hours = await client.getOpeningHours()
660
+ * } catch (err) {
661
+ * if (err instanceof PraxiumNotFoundError) {
662
+ * // tenant slug is invalid
663
+ * }
664
+ * }
665
+ * ```
666
+ */
667
+
668
+ /** Validation error detail from the API */
669
+ type ValidationDetail = NonNullable<ApiError['details']>[number];
670
+ /**
671
+ * Base error for all Praxium API failures.
672
+ *
673
+ * Properties mirror the `ApiError` response body from the platform:
674
+ * - `status` — HTTP status code
675
+ * - `errorCode` — Machine-readable code (e.g. `'TENANT_NOT_FOUND'`)
676
+ * - `message` — Human-readable explanation
677
+ * - `details` — Optional validation error details (400 responses only)
678
+ */
679
+ declare class PraxiumError extends Error {
680
+ readonly status: number;
681
+ readonly errorCode: string;
682
+ readonly details?: ApiError['details'];
683
+ name: string;
684
+ constructor(status: number, errorCode: string, message: string, details?: ApiError['details']);
685
+ }
686
+ /** Thrown when the server rejects the request body (HTTP 400). */
687
+ declare class PraxiumValidationError extends PraxiumError {
688
+ constructor(errorCode: string, message: string, details?: ApiError['details']);
689
+ }
690
+ /** Thrown when the API key is missing or invalid (HTTP 401). */
691
+ declare class PraxiumAuthError extends PraxiumError {
692
+ constructor(errorCode: string, message: string);
693
+ }
694
+ /** Thrown when the API key does not match the requested tenant (HTTP 403). */
695
+ declare class PraxiumForbiddenError extends PraxiumError {
696
+ constructor(errorCode: string, message: string);
697
+ }
698
+ /** Thrown when the tenant is not found or inactive (HTTP 404). */
699
+ declare class PraxiumNotFoundError extends PraxiumError {
700
+ constructor(errorCode: string, message: string);
701
+ }
702
+ /** Thrown when the rate limit is exceeded (HTTP 429). */
703
+ declare class PraxiumRateLimitError extends PraxiumError {
704
+ constructor(errorCode: string, message: string);
705
+ }
706
+
707
+ export { type ApiError, type BilingualText, type BookableService, type BookableServices, type BookableVariantInfo, type BusinessName, type ContactDetails, type ContactFormResult, type FaqCategory, type FaqContent, type FaqGroup, type FaqItem, type FeatureItem, type FeatureList, type InsuranceInfo, type InsuranceList, type LocalizedSpecialization, type Location, type OpeningHours, type PaymentMethod, type PaymentMethodList, type PolicyInfo, type PolicyList, PraxiumAuthError, PraxiumError, PraxiumForbiddenError, PraxiumNotFoundError, PraxiumRateLimitError, PraxiumValidationError, type PricingVariant, type PricingVariants, type PublicDaySchedule, type PublicTeamMember, type ServiceCategoryInfo, type SocialLinks, type TeamMembers, type TenantClient, type TenantClientConfig, type ValidationDetail, createTenantClient };