@businessdash/sdk 0.9.60 → 0.9.70
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/README.md +1012 -186
- package/assets/biab-sw.js +92 -0
- package/dist/account-core/index.d.ts +126 -0
- package/dist/account-core/index.d.ts.map +1 -0
- package/dist/account-core/index.js +191 -0
- package/dist/account-core/index.js.map +1 -0
- package/dist/account.d.ts +11 -0
- package/dist/account.d.ts.map +1 -0
- package/dist/account.js +11 -0
- package/dist/account.js.map +1 -0
- package/dist/auth-handler.d.ts +1 -1
- package/dist/auth-links.d.ts +33 -0
- package/dist/auth-links.d.ts.map +1 -0
- package/dist/auth-links.js +64 -0
- package/dist/auth-links.js.map +1 -0
- package/dist/blog-core/index.d.ts +112 -0
- package/dist/blog-core/index.d.ts.map +1 -0
- package/dist/blog-core/index.js +330 -0
- package/dist/blog-core/index.js.map +1 -0
- package/dist/blog.d.ts +11 -0
- package/dist/blog.d.ts.map +1 -0
- package/dist/blog.js +11 -0
- package/dist/blog.js.map +1 -0
- package/dist/chat-core/index.d.ts +97 -0
- package/dist/chat-core/index.d.ts.map +1 -0
- package/dist/chat-core/index.js +237 -0
- package/dist/chat-core/index.js.map +1 -0
- package/dist/chat.d.ts +11 -0
- package/dist/chat.d.ts.map +1 -0
- package/dist/chat.js +11 -0
- package/dist/chat.js.map +1 -0
- package/dist/checkout-core/index.d.ts +65 -0
- package/dist/checkout-core/index.d.ts.map +1 -0
- package/dist/checkout-core/index.js +159 -0
- package/dist/checkout-core/index.js.map +1 -0
- package/dist/checkout.d.ts +10 -0
- package/dist/checkout.d.ts.map +1 -0
- package/dist/checkout.js +10 -0
- package/dist/checkout.js.map +1 -0
- package/dist/cli.js +133 -25
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +567 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +807 -1
- package/dist/client.js.map +1 -1
- package/dist/contracts.d.ts +3309 -761
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +541 -4
- package/dist/contracts.js.map +1 -1
- package/dist/data-model-schema.d.ts +1 -1
- package/dist/data-model-schema.d.ts.map +1 -1
- package/dist/element.d.ts +30 -0
- package/dist/element.d.ts.map +1 -1
- package/dist/element.js +56 -0
- package/dist/element.js.map +1 -1
- package/dist/embed-protocol.d.ts +86 -0
- package/dist/embed-protocol.d.ts.map +1 -0
- package/dist/embed-protocol.js +60 -0
- package/dist/embed-protocol.js.map +1 -0
- package/dist/framework-support.d.ts +122 -0
- package/dist/framework-support.d.ts.map +1 -0
- package/dist/framework-support.js +323 -0
- package/dist/framework-support.js.map +1 -0
- package/dist/index.cjs +1244 -5
- package/dist/legal-core/index.cjs +115 -0
- package/dist/legal-core/index.d.ts +160 -0
- package/dist/legal-core/index.d.ts.map +1 -0
- package/dist/legal-core/index.js +220 -0
- package/dist/legal-core/index.js.map +1 -0
- package/dist/marketing-core/index.d.ts +66 -0
- package/dist/marketing-core/index.d.ts.map +1 -0
- package/dist/marketing-core/index.js +154 -0
- package/dist/marketing-core/index.js.map +1 -0
- package/dist/marketing.d.ts +7 -0
- package/dist/marketing.d.ts.map +1 -0
- package/dist/marketing.js +7 -0
- package/dist/marketing.js.map +1 -0
- package/dist/mcp-adapters/index.d.ts +135 -0
- package/dist/mcp-adapters/index.d.ts.map +1 -0
- package/dist/mcp-adapters/index.js +187 -0
- package/dist/mcp-adapters/index.js.map +1 -0
- package/dist/mcp-adapters.d.ts +7 -0
- package/dist/mcp-adapters.d.ts.map +1 -0
- package/dist/mcp-adapters.js +7 -0
- package/dist/mcp-adapters.js.map +1 -0
- package/dist/model3d-core/index.cjs +95 -0
- package/dist/model3d-core/index.d.ts +167 -0
- package/dist/model3d-core/index.d.ts.map +1 -0
- package/dist/model3d-core/index.js +158 -0
- package/dist/model3d-core/index.js.map +1 -0
- package/dist/notifications-core/index.cjs +315 -0
- package/dist/notifications-core/index.d.ts +229 -0
- package/dist/notifications-core/index.d.ts.map +1 -0
- package/dist/notifications-core/index.js +445 -0
- package/dist/notifications-core/index.js.map +1 -0
- package/dist/portal-core/index.d.ts +451 -0
- package/dist/portal-core/index.d.ts.map +1 -0
- package/dist/portal-core/index.js +819 -0
- package/dist/portal-core/index.js.map +1 -0
- package/dist/portal.d.ts +7 -0
- package/dist/portal.d.ts.map +1 -0
- package/dist/portal.js +7 -0
- package/dist/portal.js.map +1 -0
- package/dist/push-core/index.cjs +171 -0
- package/dist/push-core/index.d.ts +149 -0
- package/dist/push-core/index.d.ts.map +1 -0
- package/dist/push-core/index.js +231 -0
- package/dist/push-core/index.js.map +1 -0
- package/dist/react-marketing.d.ts +1 -17
- package/dist/react-marketing.d.ts.map +1 -1
- package/dist/react-marketing.js +4 -0
- package/dist/react-marketing.js.map +1 -1
- package/dist/react.d.ts +294 -101
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +302 -246
- package/dist/react.js.map +1 -1
- package/dist/seed-core/index.d.ts +354 -0
- package/dist/seed-core/index.d.ts.map +1 -0
- package/dist/seed-core/index.js +420 -0
- package/dist/seed-core/index.js.map +1 -0
- package/dist/seed.d.ts +7 -0
- package/dist/seed.d.ts.map +1 -0
- package/dist/seed.js +7 -0
- package/dist/seed.js.map +1 -0
- package/dist/seo-core/blog.d.ts +94 -0
- package/dist/seo-core/blog.d.ts.map +1 -0
- package/dist/seo-core/blog.js +149 -0
- package/dist/seo-core/blog.js.map +1 -0
- package/dist/seo-core/commerce.d.ts +64 -0
- package/dist/seo-core/commerce.d.ts.map +1 -0
- package/dist/seo-core/commerce.js +87 -0
- package/dist/seo-core/commerce.js.map +1 -0
- package/dist/seo-core/index.cjs +751 -0
- package/dist/seo-core/index.d.ts +226 -0
- package/dist/seo-core/index.d.ts.map +1 -0
- package/dist/seo-core/index.js +455 -0
- package/dist/seo-core/index.js.map +1 -0
- package/dist/seo-core/local.d.ts +181 -0
- package/dist/seo-core/local.d.ts.map +1 -0
- package/dist/seo-core/local.js +193 -0
- package/dist/seo-core/local.js.map +1 -0
- package/dist/sitemap-core/index.cjs +167 -0
- package/dist/sitemap-core/index.d.ts +155 -0
- package/dist/sitemap-core/index.d.ts.map +1 -0
- package/dist/sitemap-core/index.js +195 -0
- package/dist/sitemap-core/index.js.map +1 -0
- package/dist/social-core/index.d.ts +74 -0
- package/dist/social-core/index.d.ts.map +1 -0
- package/dist/social-core/index.js +72 -0
- package/dist/social-core/index.js.map +1 -0
- package/dist/social.d.ts +7 -0
- package/dist/social.d.ts.map +1 -0
- package/dist/social.js +7 -0
- package/dist/social.js.map +1 -0
- package/dist/solid-compat.d.ts +204 -0
- package/dist/solid-compat.d.ts.map +1 -0
- package/dist/solid-compat.js +221 -0
- package/dist/solid-compat.js.map +1 -0
- package/dist/store-core/index.d.ts +141 -0
- package/dist/store-core/index.d.ts.map +1 -0
- package/dist/store-core/index.js +277 -0
- package/dist/store-core/index.js.map +1 -0
- package/dist/store.d.ts +12 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +12 -0
- package/dist/store.js.map +1 -0
- package/dist/vanilla.d.ts +111 -0
- package/dist/vanilla.d.ts.map +1 -1
- package/dist/vanilla.js +206 -0
- package/dist/vanilla.js.map +1 -1
- package/dist/verify-core/index.d.ts +98 -0
- package/dist/verify-core/index.d.ts.map +1 -0
- package/dist/verify-core/index.js +186 -0
- package/dist/verify-core/index.js.map +1 -0
- package/dist/verify.d.ts +8 -0
- package/dist/verify.d.ts.map +1 -0
- package/dist/verify.js +8 -0
- package/dist/verify.js.map +1 -0
- package/openapi.json +4316 -1313
- package/package.json +121 -9
- package/src/account-core/index.ts +309 -0
- package/src/account.ts +11 -0
- package/src/angular/headless.ts +519 -0
- package/src/angular/index.ts +3 -0
- package/src/auth-links.ts +76 -0
- package/src/blog-core/index.ts +423 -0
- package/src/blog.ts +11 -0
- package/src/chat-core/index.ts +335 -0
- package/src/chat.ts +11 -0
- package/src/checkout-core/index.ts +225 -0
- package/src/checkout.ts +10 -0
- package/src/cli.ts +184 -28
- package/src/client.ts +998 -0
- package/src/contracts.ts +730 -11
- package/src/data-model-schema.ts +1 -1
- package/src/element.ts +102 -0
- package/src/embed-protocol.ts +100 -0
- package/src/framework-support.ts +440 -0
- package/src/legal-core/index.ts +276 -0
- package/src/marketing-core/index.ts +209 -0
- package/src/marketing.ts +7 -0
- package/src/mcp-adapters/index.ts +264 -0
- package/src/mcp-adapters.ts +7 -0
- package/src/model3d-core/index.ts +248 -0
- package/src/notifications-core/index.ts +650 -0
- package/src/portal-core/index.ts +1564 -0
- package/src/portal.ts +7 -0
- package/src/push-core/index.ts +335 -0
- package/src/qwik.tsx +602 -2
- package/src/react-marketing.tsx +5 -0
- package/src/react.tsx +434 -319
- package/src/seed-core/index.ts +735 -0
- package/src/seed.ts +7 -0
- package/src/seo-core/blog.ts +216 -0
- package/src/seo-core/commerce.ts +138 -0
- package/src/seo-core/index.ts +605 -0
- package/src/seo-core/local.ts +333 -0
- package/src/sitemap-core/index.ts +296 -0
- package/src/social-core/index.ts +103 -0
- package/src/social.ts +7 -0
- package/src/solid-compat.tsx +347 -0
- package/src/solid-surfaces.tsx +1311 -0
- package/src/solid.tsx +82 -60
- package/src/store-core/index.ts +426 -0
- package/src/store.ts +12 -0
- package/src/svelte/SocialLinks.svelte +84 -0
- package/src/svelte/headless.ts +435 -0
- package/src/svelte/index.ts +9 -0
- package/src/vanilla.ts +367 -0
- package/src/verify-core/index.ts +288 -0
- package/src/verify.ts +8 -0
- package/src/vue/headless.ts +527 -0
- package/src/vue/index.ts +3 -0
package/src/client.ts
CHANGED
|
@@ -70,6 +70,9 @@ import {
|
|
|
70
70
|
type CheckoutStatusResponse,
|
|
71
71
|
type ConfirmVerificationInput,
|
|
72
72
|
type ConfirmVerificationResponse,
|
|
73
|
+
type CouponCreateInput,
|
|
74
|
+
type CouponCreateResponse,
|
|
75
|
+
type CouponListResponse,
|
|
73
76
|
type CouponValidateResponse,
|
|
74
77
|
type CreateCollectionInput,
|
|
75
78
|
type CreateCollectionResponse,
|
|
@@ -80,9 +83,12 @@ import {
|
|
|
80
83
|
type CustomerContactCompanyInput,
|
|
81
84
|
type CustomerContactCompanyResponse,
|
|
82
85
|
type CustomerContractDetail,
|
|
86
|
+
type CustomerContractListResponse,
|
|
83
87
|
type CustomerContractSignResponse,
|
|
84
88
|
type CustomerDownloadsResponse,
|
|
89
|
+
type CustomerInviteResendResponse,
|
|
85
90
|
type CustomerInvoiceDetail,
|
|
91
|
+
type CustomerInvoiceListResponse,
|
|
86
92
|
type CustomerInvoicePayInput,
|
|
87
93
|
type CustomerInvoicePayResponse,
|
|
88
94
|
type CustomerInvoicePdf,
|
|
@@ -95,24 +101,38 @@ import {
|
|
|
95
101
|
type CustomerNotificationPreferencesResponse,
|
|
96
102
|
type CustomerNotificationPreferencesUpdateInput,
|
|
97
103
|
type CustomerNotificationPreferencesUpdateResponse,
|
|
104
|
+
type CustomerNotReceivedResult,
|
|
98
105
|
type CustomerOrderDetail,
|
|
99
106
|
type CustomerOrderListResponse,
|
|
100
107
|
type CustomerPortalContextResponse,
|
|
108
|
+
type CustomerPortalMessage,
|
|
109
|
+
type CustomerPortalMessageListResponse,
|
|
110
|
+
type CustomerPortalMessagePostInput,
|
|
111
|
+
type CustomerPortalMessagesMarkReadResponse,
|
|
101
112
|
type CustomerPortalOtherOrgsResponse,
|
|
102
113
|
type CustomerPortalProfile,
|
|
103
114
|
type CustomerPortalUpdateProfileInput,
|
|
104
115
|
type CustomerPortalUpdateProfileResponse,
|
|
116
|
+
type CustomerProductReviewCreated,
|
|
117
|
+
type CustomerProductReviewList,
|
|
118
|
+
type CustomerPushConfig,
|
|
105
119
|
type CustomerQuoteActionResponse,
|
|
106
120
|
type CustomerQuoteDetail,
|
|
121
|
+
type CustomerQuoteListResponse,
|
|
107
122
|
type CustomerReferralHistoryResponse,
|
|
108
123
|
type CustomerReferralPayoutsResponse,
|
|
109
124
|
type CustomerReferralStats,
|
|
110
125
|
type CustomerReturnRequestCreated,
|
|
111
126
|
type CustomerReturnRequestInput,
|
|
127
|
+
type CustomerReturnRequestListResponse,
|
|
112
128
|
type CustomerReviewListResponse,
|
|
113
129
|
type CustomerReviewSubmitInput,
|
|
114
130
|
type CustomerReviewSubmitResponse,
|
|
131
|
+
type CustomerShipmentListResponse,
|
|
115
132
|
type CustomerShipmentTracking,
|
|
133
|
+
type CustomerSubscriberContentResponse,
|
|
134
|
+
type CustomerSubscriptionCancelResponse,
|
|
135
|
+
type CustomerSubscriptionResponse,
|
|
116
136
|
type CustomerWorkBundle,
|
|
117
137
|
cartAddItemInputSchema,
|
|
118
138
|
cartCouponInputSchema,
|
|
@@ -142,6 +162,9 @@ import {
|
|
|
142
162
|
checkoutStatusResponseSchema,
|
|
143
163
|
confirmVerificationInputSchema,
|
|
144
164
|
confirmVerificationResponseSchema,
|
|
165
|
+
couponCreateInputSchema,
|
|
166
|
+
couponCreateResponseSchema,
|
|
167
|
+
couponListResponseSchema,
|
|
145
168
|
couponValidateInputSchema,
|
|
146
169
|
couponValidateResponseSchema,
|
|
147
170
|
createCollectionInputSchema,
|
|
@@ -153,9 +176,12 @@ import {
|
|
|
153
176
|
customerContactCompanyInputSchema,
|
|
154
177
|
customerContactCompanyResponseSchema,
|
|
155
178
|
customerContractDetailSchema,
|
|
179
|
+
customerContractListResponseSchema,
|
|
156
180
|
customerContractSignResponseSchema,
|
|
157
181
|
customerDownloadsResponseSchema,
|
|
182
|
+
customerInviteResendResponseSchema,
|
|
158
183
|
customerInvoiceDetailSchema,
|
|
184
|
+
customerInvoiceListResponseSchema,
|
|
159
185
|
customerInvoicePayInputSchema,
|
|
160
186
|
customerInvoicePayResponseSchema,
|
|
161
187
|
customerInvoicePdfSchema,
|
|
@@ -168,25 +194,38 @@ import {
|
|
|
168
194
|
customerNotificationPreferencesResponseSchema,
|
|
169
195
|
customerNotificationPreferencesUpdateInputSchema,
|
|
170
196
|
customerNotificationPreferencesUpdateResponseSchema,
|
|
197
|
+
customerNotReceivedResultSchema,
|
|
171
198
|
customerOrderDetailSchema,
|
|
172
199
|
customerOrderListResponseSchema,
|
|
173
200
|
customerPaymentHistoryResponseSchema,
|
|
174
201
|
customerPortalContextResponseSchema,
|
|
202
|
+
customerPortalMessageListResponseSchema,
|
|
203
|
+
customerPortalMessageSchema,
|
|
204
|
+
customerPortalMessagesMarkReadResponseSchema,
|
|
175
205
|
customerPortalOtherOrgsResponseSchema,
|
|
176
206
|
customerPortalProfileSchema,
|
|
177
207
|
customerPortalUpdateProfileInputSchema,
|
|
178
208
|
customerPortalUpdateProfileResponseSchema,
|
|
209
|
+
customerProductReviewCreatedSchema,
|
|
210
|
+
customerProductReviewListSchema,
|
|
211
|
+
customerPushConfigSchema,
|
|
179
212
|
customerQuoteActionResponseSchema,
|
|
180
213
|
customerQuoteDetailSchema,
|
|
214
|
+
customerQuoteListResponseSchema,
|
|
181
215
|
customerReferralHistoryResponseSchema,
|
|
182
216
|
customerReferralPayoutsResponseSchema,
|
|
183
217
|
customerReferralStatsSchema,
|
|
184
218
|
customerReturnRequestCreatedSchema,
|
|
185
219
|
customerReturnRequestInputSchema,
|
|
220
|
+
customerReturnRequestListResponseSchema,
|
|
186
221
|
customerReviewListResponseSchema,
|
|
187
222
|
customerReviewSubmitInputSchema,
|
|
188
223
|
customerReviewSubmitResponseSchema,
|
|
224
|
+
customerShipmentListResponseSchema,
|
|
189
225
|
customerShipmentTrackingSchema,
|
|
226
|
+
customerSubscriberContentResponseSchema,
|
|
227
|
+
customerSubscriptionCancelResponseSchema,
|
|
228
|
+
customerSubscriptionResponseSchema,
|
|
190
229
|
customerWorkBundleSchema,
|
|
191
230
|
type DashboardSessionResponse,
|
|
192
231
|
type DataModelRecord,
|
|
@@ -230,6 +269,8 @@ import {
|
|
|
230
269
|
jobCategoryListResponseSchema,
|
|
231
270
|
jobPostingListResponseSchema,
|
|
232
271
|
jobPostingSchema,
|
|
272
|
+
type LegalDocument,
|
|
273
|
+
type LegalListResponse,
|
|
233
274
|
type ListCollectionsResponse,
|
|
234
275
|
type ListDataModelRecordsInput,
|
|
235
276
|
type ListDataModelRecordsResponse,
|
|
@@ -238,6 +279,8 @@ import {
|
|
|
238
279
|
type ListRowsInput,
|
|
239
280
|
type ListRowsResponse,
|
|
240
281
|
type ListSiteMarketingPagesResponse,
|
|
282
|
+
legalDocumentSchema,
|
|
283
|
+
legalListResponseSchema,
|
|
241
284
|
listCollectionsResponseSchema,
|
|
242
285
|
listDataModelRecordsInputSchema,
|
|
243
286
|
listDataModelRecordsResponseSchema,
|
|
@@ -264,6 +307,13 @@ import {
|
|
|
264
307
|
queryRowsResponseSchema,
|
|
265
308
|
type ReviewWallListResponse,
|
|
266
309
|
reviewWallListResponseSchema,
|
|
310
|
+
type SchedulingBookingResult,
|
|
311
|
+
type SchedulingBookingView,
|
|
312
|
+
type SchedulingCancelResult,
|
|
313
|
+
type SchedulingEventTypeDetail,
|
|
314
|
+
type SchedulingEventTypeList,
|
|
315
|
+
type SchedulingRescheduleResult,
|
|
316
|
+
type SchedulingSlotsResponse,
|
|
267
317
|
type ShippingRatesInput,
|
|
268
318
|
type ShippingRatesResponse,
|
|
269
319
|
type ShippingSettingsResponse,
|
|
@@ -282,10 +332,18 @@ import {
|
|
|
282
332
|
type StorefrontSessionInput,
|
|
283
333
|
type StorefrontSessionResponse,
|
|
284
334
|
type StorefrontSort,
|
|
335
|
+
type StorefrontSyncToStripeResponse,
|
|
285
336
|
type SubscriptionCheckoutInput,
|
|
286
337
|
type SubscriptionCheckoutResponse,
|
|
287
338
|
type SubscriptionOffering,
|
|
288
339
|
type SubscriptionOfferingsListResponse,
|
|
340
|
+
schedulingBookingResultSchema,
|
|
341
|
+
schedulingBookingViewSchema,
|
|
342
|
+
schedulingCancelResultSchema,
|
|
343
|
+
schedulingEventTypeDetailSchema,
|
|
344
|
+
schedulingEventTypeListSchema,
|
|
345
|
+
schedulingRescheduleResultSchema,
|
|
346
|
+
schedulingSlotsResponseSchema,
|
|
289
347
|
shippingRatesInputSchema,
|
|
290
348
|
shippingRatesResponseSchema,
|
|
291
349
|
shippingSettingsResponseSchema,
|
|
@@ -303,6 +361,7 @@ import {
|
|
|
303
361
|
storefrontRelatedProductsResponseSchema,
|
|
304
362
|
storefrontSessionInputSchema,
|
|
305
363
|
storefrontSessionResponseSchema,
|
|
364
|
+
storefrontSyncToStripeResponseSchema,
|
|
306
365
|
subscriptionCheckoutInputSchema,
|
|
307
366
|
subscriptionCheckoutResponseSchema,
|
|
308
367
|
subscriptionOfferingSchema,
|
|
@@ -513,6 +572,11 @@ export class BiabDevClient {
|
|
|
513
572
|
return new BiabDevShippingClient(this);
|
|
514
573
|
}
|
|
515
574
|
|
|
575
|
+
/** Customer-portal invitations — currently resend. */
|
|
576
|
+
get customerInvites() {
|
|
577
|
+
return new BiabDevCustomerInvitesClient(this);
|
|
578
|
+
}
|
|
579
|
+
|
|
516
580
|
get notifications() {
|
|
517
581
|
return new BiabDevNotificationsClient(this);
|
|
518
582
|
}
|
|
@@ -534,6 +598,17 @@ export class BiabDevClient {
|
|
|
534
598
|
return new BiabDevHiringClient(this);
|
|
535
599
|
}
|
|
536
600
|
|
|
601
|
+
/**
|
|
602
|
+
* The org's published legal pages — privacy, terms, refund policy, whatever
|
|
603
|
+
* they authored in the dashboard.
|
|
604
|
+
*
|
|
605
|
+
* Pair with `@businessdash/sdk/legal` to render them without writing a page
|
|
606
|
+
* per document.
|
|
607
|
+
*/
|
|
608
|
+
get legal() {
|
|
609
|
+
return new BiabDevLegalClient(this);
|
|
610
|
+
}
|
|
611
|
+
|
|
537
612
|
get chatbot() {
|
|
538
613
|
return new BiabDevChatbotClient(this);
|
|
539
614
|
}
|
|
@@ -648,12 +723,21 @@ export class BiabDevSiteClient {
|
|
|
648
723
|
*/
|
|
649
724
|
readonly marketing: BiabDevMarketingClient;
|
|
650
725
|
readonly parallelPages: BiabDevParallelPagesClient;
|
|
726
|
+
/**
|
|
727
|
+
* Booking and conference calls.
|
|
728
|
+
*
|
|
729
|
+
* Previously reachable only through `sdk.ts`'s separate scheduling
|
|
730
|
+
* resource, which is why the OpenAPI generator — which reads this file —
|
|
731
|
+
* never saw it, and why no non-JS starter had scheduling at all.
|
|
732
|
+
*/
|
|
733
|
+
readonly scheduling: BiabDevSchedulingClient;
|
|
651
734
|
|
|
652
735
|
constructor(
|
|
653
736
|
readonly client: BiabDevClient,
|
|
654
737
|
readonly siteId: string,
|
|
655
738
|
) {
|
|
656
739
|
this.collections = new BiabDevCollectionsClient(client, siteId);
|
|
740
|
+
this.scheduling = new BiabDevSchedulingClient(client, siteId);
|
|
657
741
|
this.rows = new BiabDevRowsClient(client, siteId);
|
|
658
742
|
this.dataModel = new BiabDevDataModelClient(client, siteId);
|
|
659
743
|
this.analytics = new BiabDevAnalyticsClient(client, siteId);
|
|
@@ -677,6 +761,30 @@ export class BiabDevSiteClient {
|
|
|
677
761
|
* <img src={b.logoUrl ?? b.orgIcon ?? ""} alt={b.orgName ?? ""} />
|
|
678
762
|
* ```
|
|
679
763
|
*/
|
|
764
|
+
/**
|
|
765
|
+
* Every URL the PLATFORM owns the shape of, as JSON.
|
|
766
|
+
*
|
|
767
|
+
* Published site-builder pages, published legal documents, and every
|
|
768
|
+
* materialised programmatic-page URL. Blog posts and products are not here
|
|
769
|
+
* — the platform does not know where your site renders them. Merge those in
|
|
770
|
+
* with `@businessdash/sdk/sitemap`, which handles the whole job.
|
|
771
|
+
*
|
|
772
|
+
* Paths are relative, so you join them against your own origin rather than
|
|
773
|
+
* the org's primary verified domain.
|
|
774
|
+
*/
|
|
775
|
+
async sitemapEntries(): Promise<{
|
|
776
|
+
entries: Array<{ loc: string; lastmod?: string | undefined }>;
|
|
777
|
+
}> {
|
|
778
|
+
return this.client.request({
|
|
779
|
+
path: `sites/${encodeURIComponent(this.siteId)}/sitemap-entries`,
|
|
780
|
+
responseSchema: z.object({
|
|
781
|
+
entries: z.array(
|
|
782
|
+
z.object({ loc: z.string(), lastmod: z.string().optional() }),
|
|
783
|
+
),
|
|
784
|
+
}),
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
|
|
680
788
|
async branding(): Promise<SiteBranding> {
|
|
681
789
|
return this.client.request({
|
|
682
790
|
path: `sites/${encodeURIComponent(this.siteId)}/branding`,
|
|
@@ -1591,6 +1699,45 @@ export class BiabDevStorefrontClient {
|
|
|
1591
1699
|
});
|
|
1592
1700
|
}
|
|
1593
1701
|
|
|
1702
|
+
/**
|
|
1703
|
+
* Push your catalog into YOUR OWN Stripe account.
|
|
1704
|
+
*
|
|
1705
|
+
* Products and their variant combinations become Stripe Products and
|
|
1706
|
+
* Prices on the Stripe account connected to your workspace, so you can
|
|
1707
|
+
* manage the catalog here and let Stripe be the system of record for
|
|
1708
|
+
* money. Safe to re-run: an unchanged product is left alone, and a new
|
|
1709
|
+
* Price is minted only when the amount actually changed (Stripe Prices
|
|
1710
|
+
* are immutable on amount).
|
|
1711
|
+
*
|
|
1712
|
+
* Requires a SECRET key holding `storefront:write` — never a publishable
|
|
1713
|
+
* token, because this spends against Stripe on your behalf.
|
|
1714
|
+
*
|
|
1715
|
+
* Pass `productId` to sync one product. Omit it to sync everything not yet
|
|
1716
|
+
* pushed, in bounded batches: check `remaining` and call again until it
|
|
1717
|
+
* reaches 0.
|
|
1718
|
+
*
|
|
1719
|
+
* ```ts
|
|
1720
|
+
* let remaining = 1;
|
|
1721
|
+
* while (remaining > 0) {
|
|
1722
|
+
* const res = await biab.storefront.syncToStripe();
|
|
1723
|
+
* remaining = res.remaining;
|
|
1724
|
+
* }
|
|
1725
|
+
* ```
|
|
1726
|
+
*
|
|
1727
|
+
* Per-product failures are returned in `failed` rather than throwing, so
|
|
1728
|
+
* one bad product doesn't abandon the batch.
|
|
1729
|
+
*/
|
|
1730
|
+
async syncToStripe(input?: {
|
|
1731
|
+
productId?: string;
|
|
1732
|
+
}): Promise<StorefrontSyncToStripeResponse> {
|
|
1733
|
+
return this.client.request({
|
|
1734
|
+
method: "POST",
|
|
1735
|
+
path: "storefront/products/sync-to-stripe",
|
|
1736
|
+
body: input?.productId ? { productId: input.productId } : {},
|
|
1737
|
+
responseSchema: storefrontSyncToStripeResponseSchema,
|
|
1738
|
+
});
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1594
1741
|
/**
|
|
1595
1742
|
* Paginated list of live products for the calling org's storefront.
|
|
1596
1743
|
* Cursor is an opaque integer offset — pass back the response's
|
|
@@ -1662,6 +1809,43 @@ export class BiabDevStorefrontClient {
|
|
|
1662
1809
|
* via `BiabApiError` if the product isn't live or doesn't belong
|
|
1663
1810
|
* to the org bound to the API key.
|
|
1664
1811
|
*/
|
|
1812
|
+
/**
|
|
1813
|
+
* The CAD file for a product — STEP, IGES, or a native part file.
|
|
1814
|
+
*
|
|
1815
|
+
* ## Call this from YOUR server, then proxy it
|
|
1816
|
+
*
|
|
1817
|
+
* The API key is a server secret, so a browser cannot reach this directly.
|
|
1818
|
+
* Expose your own route (`/api/products/:id/cad`), call this behind it, and
|
|
1819
|
+
* redirect the visitor to the URL it returns.
|
|
1820
|
+
*
|
|
1821
|
+
* The platform withholds the raw URL from every product payload on purpose:
|
|
1822
|
+
* CAD files are the largest thing it serves and are the org's intellectual
|
|
1823
|
+
* property, so a catalog of naked URLs is a design library published to
|
|
1824
|
+
* anyone willing to write a loop. This endpoint requires a `biab_cad` cookie
|
|
1825
|
+
* on the incoming request, rate-limits per IP, and re-checks the
|
|
1826
|
+
* `ecommerce.product_3d` entitlement before answering.
|
|
1827
|
+
*
|
|
1828
|
+
* `cacheSeconds` is what to put on your own response — CAD at a content URL
|
|
1829
|
+
* does not change, so caching hard is where the bandwidth saving comes from.
|
|
1830
|
+
*
|
|
1831
|
+
* Rejects 404 when the product is a draft, has no CAD file, or the org lacks
|
|
1832
|
+
* the add-on. Those are deliberately one answer.
|
|
1833
|
+
*/
|
|
1834
|
+
async getCadDownload(productId: string): Promise<{
|
|
1835
|
+
url: string;
|
|
1836
|
+
fileName: string | null;
|
|
1837
|
+
cacheSeconds: number;
|
|
1838
|
+
}> {
|
|
1839
|
+
return this.client.request({
|
|
1840
|
+
path: `storefront/products/${encodeURIComponent(productId)}/cad`,
|
|
1841
|
+
responseSchema: z.object({
|
|
1842
|
+
url: z.string(),
|
|
1843
|
+
fileName: z.string().nullable(),
|
|
1844
|
+
cacheSeconds: z.number(),
|
|
1845
|
+
}),
|
|
1846
|
+
});
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1665
1849
|
async getProduct(productId: string): Promise<StorefrontProductDetail> {
|
|
1666
1850
|
return this.client.request({
|
|
1667
1851
|
path: `storefront/products/${encodeURIComponent(productId)}`,
|
|
@@ -2087,6 +2271,49 @@ export class BiabDevCouponsClient {
|
|
|
2087
2271
|
* else toast.error(result.message);
|
|
2088
2272
|
* ```
|
|
2089
2273
|
*/
|
|
2274
|
+
/**
|
|
2275
|
+
* Create a coupon and make it live in YOUR OWN Stripe account in one call —
|
|
2276
|
+
* a Stripe Coupon plus a Promotion Code, so the same string works on your
|
|
2277
|
+
* storefront and on Stripe-hosted Checkout.
|
|
2278
|
+
*
|
|
2279
|
+
* Creation and the Stripe sync are one operation deliberately: a coupon
|
|
2280
|
+
* that exists locally but not in Stripe is the failure worth designing out,
|
|
2281
|
+
* where a customer types a code your storefront accepts and Stripe rejects.
|
|
2282
|
+
* If the sync fails the coupon is still created and `stripeSynced` comes
|
|
2283
|
+
* back `false` with the reason, rather than pretending it is live.
|
|
2284
|
+
*
|
|
2285
|
+
* Requires a SECRET key holding `storefront:write`.
|
|
2286
|
+
*
|
|
2287
|
+
* ```ts
|
|
2288
|
+
* const { coupon, stripeSynced } = await biab.coupons.create({
|
|
2289
|
+
* code: 'WELCOME10',
|
|
2290
|
+
* discountType: 'percent',
|
|
2291
|
+
* value: 10,
|
|
2292
|
+
* })
|
|
2293
|
+
* ```
|
|
2294
|
+
*
|
|
2295
|
+
* Buy-X-get-Y and bundle-gift coupons aren't available here — Stripe has no
|
|
2296
|
+
* native BXGY, so those run through our own cart math and need the offer
|
|
2297
|
+
* rows the dashboard collects.
|
|
2298
|
+
*/
|
|
2299
|
+
async create(input: CouponCreateInput): Promise<CouponCreateResponse> {
|
|
2300
|
+
const body = couponCreateInputSchema.parse(input);
|
|
2301
|
+
return this.client.request({
|
|
2302
|
+
method: "POST",
|
|
2303
|
+
path: "coupons",
|
|
2304
|
+
body,
|
|
2305
|
+
responseSchema: couponCreateResponseSchema,
|
|
2306
|
+
});
|
|
2307
|
+
}
|
|
2308
|
+
|
|
2309
|
+
/** Every coupon on the workspace, newest first. Requires `storefront:write`. */
|
|
2310
|
+
async list(): Promise<CouponListResponse> {
|
|
2311
|
+
return this.client.request({
|
|
2312
|
+
path: "coupons",
|
|
2313
|
+
responseSchema: couponListResponseSchema,
|
|
2314
|
+
});
|
|
2315
|
+
}
|
|
2316
|
+
|
|
2090
2317
|
async validate(code: string): Promise<CouponValidateResponse> {
|
|
2091
2318
|
const body = couponValidateInputSchema.parse({ code });
|
|
2092
2319
|
return this.client.request({
|
|
@@ -2555,6 +2782,51 @@ export class BiabDevHiringClient {
|
|
|
2555
2782
|
}
|
|
2556
2783
|
}
|
|
2557
2784
|
|
|
2785
|
+
/**
|
|
2786
|
+
* The org's published legal pages.
|
|
2787
|
+
*
|
|
2788
|
+
* Everything here is content the org deliberately put on the public internet;
|
|
2789
|
+
* drafts are filtered server-side and unreachable. Requires `legal:read`, which
|
|
2790
|
+
* is publishable — a browser token is the expected caller.
|
|
2791
|
+
*
|
|
2792
|
+
* @example
|
|
2793
|
+
* ```ts
|
|
2794
|
+
* const { items, presentation } = await client.legal.list();
|
|
2795
|
+
* const privacy = await client.legal.get("privacy");
|
|
2796
|
+
* ```
|
|
2797
|
+
*/
|
|
2798
|
+
export class BiabDevLegalClient {
|
|
2799
|
+
constructor(private readonly client: BiabDevClient) {}
|
|
2800
|
+
|
|
2801
|
+
/**
|
|
2802
|
+
* Every published document, plus the header/footer presentation.
|
|
2803
|
+
*
|
|
2804
|
+
* Both in one response because the common use — a footer link list — needs
|
|
2805
|
+
* the titles and the presentation together, and a second round trip for the
|
|
2806
|
+
* latter is one a site either makes on every page or skips entirely.
|
|
2807
|
+
*/
|
|
2808
|
+
async list(): Promise<LegalListResponse> {
|
|
2809
|
+
return this.client.request({
|
|
2810
|
+
path: "legal",
|
|
2811
|
+
responseSchema: legalListResponseSchema,
|
|
2812
|
+
});
|
|
2813
|
+
}
|
|
2814
|
+
|
|
2815
|
+
/**
|
|
2816
|
+
* One document by slug, body included.
|
|
2817
|
+
*
|
|
2818
|
+
* Rejects with a 404 when the slug is unpublished or absent — the two are
|
|
2819
|
+
* deliberately indistinguishable, since "exists but hidden" would leak that
|
|
2820
|
+
* an org is drafting a policy and is not something a caller can act on.
|
|
2821
|
+
*/
|
|
2822
|
+
async get(slug: string): Promise<LegalDocument> {
|
|
2823
|
+
return this.client.request({
|
|
2824
|
+
path: `legal/${encodeURIComponent(slug)}`,
|
|
2825
|
+
responseSchema: legalDocumentSchema,
|
|
2826
|
+
});
|
|
2827
|
+
}
|
|
2828
|
+
}
|
|
2829
|
+
|
|
2558
2830
|
export class BiabDevChatbotClient {
|
|
2559
2831
|
constructor(private readonly client: BiabDevClient) {}
|
|
2560
2832
|
|
|
@@ -3225,6 +3497,157 @@ export class BiabDevCustomerPortalClient {
|
|
|
3225
3497
|
});
|
|
3226
3498
|
}
|
|
3227
3499
|
|
|
3500
|
+
/**
|
|
3501
|
+
* The return requests on one order, newest first.
|
|
3502
|
+
*
|
|
3503
|
+
* `resolutionNote` carries staff's answer — when a return is denied that IS
|
|
3504
|
+
* the answer, and hiding it just moves the question to email.
|
|
3505
|
+
*/
|
|
3506
|
+
async listReturnRequests(
|
|
3507
|
+
orderId: string,
|
|
3508
|
+
): Promise<CustomerReturnRequestListResponse> {
|
|
3509
|
+
return this.client.request({
|
|
3510
|
+
path: `customer-portal/orders/${encodeURIComponent(orderId)}/return-request`,
|
|
3511
|
+
responseSchema: customerReturnRequestListResponseSchema,
|
|
3512
|
+
headers: this.headers(),
|
|
3513
|
+
});
|
|
3514
|
+
}
|
|
3515
|
+
|
|
3516
|
+
/**
|
|
3517
|
+
* The customer's message thread with the org's staff, newest first.
|
|
3518
|
+
*
|
|
3519
|
+
* Automated rows — the review-ask and similar — are excluded server-side, so
|
|
3520
|
+
* this is only what a person wrote.
|
|
3521
|
+
*/
|
|
3522
|
+
async listMessages(opts?: {
|
|
3523
|
+
limit?: number;
|
|
3524
|
+
jobId?: string;
|
|
3525
|
+
}): Promise<CustomerPortalMessageListResponse> {
|
|
3526
|
+
const params = new URLSearchParams();
|
|
3527
|
+
if (typeof opts?.limit === "number")
|
|
3528
|
+
params.set("limit", String(opts.limit));
|
|
3529
|
+
if (opts?.jobId) params.set("jobId", opts.jobId);
|
|
3530
|
+
const qs = params.toString();
|
|
3531
|
+
return this.client.request({
|
|
3532
|
+
path: `customer-portal/messages${qs ? `?${qs}` : ""}`,
|
|
3533
|
+
responseSchema: customerPortalMessageListResponseSchema,
|
|
3534
|
+
headers: this.headers(),
|
|
3535
|
+
});
|
|
3536
|
+
}
|
|
3537
|
+
|
|
3538
|
+
/** Send a message to the org's staff. Lands in the CRM timeline. */
|
|
3539
|
+
async postMessage(
|
|
3540
|
+
input: CustomerPortalMessagePostInput,
|
|
3541
|
+
): Promise<CustomerPortalMessage> {
|
|
3542
|
+
return this.client.request({
|
|
3543
|
+
path: "customer-portal/messages",
|
|
3544
|
+
method: "POST",
|
|
3545
|
+
body: input,
|
|
3546
|
+
responseSchema: customerPortalMessageSchema,
|
|
3547
|
+
headers: this.headers(),
|
|
3548
|
+
});
|
|
3549
|
+
}
|
|
3550
|
+
|
|
3551
|
+
/**
|
|
3552
|
+
* Mark the staff messages read up to now.
|
|
3553
|
+
*
|
|
3554
|
+
* One-way: it never clears a stamp. A portal that could un-read a message
|
|
3555
|
+
* would let a customer hide from staff that they had seen something.
|
|
3556
|
+
*/
|
|
3557
|
+
async markMessagesRead(): Promise<CustomerPortalMessagesMarkReadResponse> {
|
|
3558
|
+
return this.client.request({
|
|
3559
|
+
path: "customer-portal/messages",
|
|
3560
|
+
method: "PATCH",
|
|
3561
|
+
responseSchema: customerPortalMessagesMarkReadResponseSchema,
|
|
3562
|
+
headers: this.headers(),
|
|
3563
|
+
});
|
|
3564
|
+
}
|
|
3565
|
+
|
|
3566
|
+
/**
|
|
3567
|
+
* The customer's invoices, newest first.
|
|
3568
|
+
*
|
|
3569
|
+
* `unpaid: true` filters on the computed BALANCE rather than on the status
|
|
3570
|
+
* string, because a partially-paid invoice still owes money whatever it is
|
|
3571
|
+
* called. This is the call behind "show me everything I owe" — which had no
|
|
3572
|
+
* endpoint at all before 0.9.61.
|
|
3573
|
+
*/
|
|
3574
|
+
async listInvoices(opts?: {
|
|
3575
|
+
limit?: number;
|
|
3576
|
+
status?: string;
|
|
3577
|
+
unpaid?: boolean;
|
|
3578
|
+
}): Promise<CustomerInvoiceListResponse> {
|
|
3579
|
+
const params = new URLSearchParams();
|
|
3580
|
+
if (typeof opts?.limit === "number")
|
|
3581
|
+
params.set("limit", String(opts.limit));
|
|
3582
|
+
if (opts?.status) params.set("status", opts.status);
|
|
3583
|
+
if (opts?.unpaid) params.set("unpaid", "1");
|
|
3584
|
+
const qs = params.toString();
|
|
3585
|
+
return this.client.request({
|
|
3586
|
+
path: `customer-portal/invoices${qs ? `?${qs}` : ""}`,
|
|
3587
|
+
responseSchema: customerInvoiceListResponseSchema,
|
|
3588
|
+
headers: this.headers(),
|
|
3589
|
+
});
|
|
3590
|
+
}
|
|
3591
|
+
|
|
3592
|
+
/** The customer's quotes. `status: "sent"` is the set still awaiting them. */
|
|
3593
|
+
async listQuotes(opts?: {
|
|
3594
|
+
limit?: number;
|
|
3595
|
+
status?: string;
|
|
3596
|
+
}): Promise<CustomerQuoteListResponse> {
|
|
3597
|
+
const params = new URLSearchParams();
|
|
3598
|
+
if (typeof opts?.limit === "number")
|
|
3599
|
+
params.set("limit", String(opts.limit));
|
|
3600
|
+
if (opts?.status) params.set("status", opts.status);
|
|
3601
|
+
const qs = params.toString();
|
|
3602
|
+
return this.client.request({
|
|
3603
|
+
path: `customer-portal/quotes${qs ? `?${qs}` : ""}`,
|
|
3604
|
+
responseSchema: customerQuoteListResponseSchema,
|
|
3605
|
+
headers: this.headers(),
|
|
3606
|
+
});
|
|
3607
|
+
}
|
|
3608
|
+
|
|
3609
|
+
/** The customer's contracts. `status: "sent"` still awaits a signature. */
|
|
3610
|
+
async listContracts(opts?: {
|
|
3611
|
+
limit?: number;
|
|
3612
|
+
status?: string;
|
|
3613
|
+
}): Promise<CustomerContractListResponse> {
|
|
3614
|
+
const params = new URLSearchParams();
|
|
3615
|
+
if (typeof opts?.limit === "number")
|
|
3616
|
+
params.set("limit", String(opts.limit));
|
|
3617
|
+
if (opts?.status) params.set("status", opts.status);
|
|
3618
|
+
const qs = params.toString();
|
|
3619
|
+
return this.client.request({
|
|
3620
|
+
path: `customer-portal/contracts${qs ? `?${qs}` : ""}`,
|
|
3621
|
+
responseSchema: customerContractListResponseSchema,
|
|
3622
|
+
headers: this.headers(),
|
|
3623
|
+
});
|
|
3624
|
+
}
|
|
3625
|
+
|
|
3626
|
+
/**
|
|
3627
|
+
* Every shipment across the customer's orders. `active: true` narrows to
|
|
3628
|
+
* parcels still moving.
|
|
3629
|
+
*
|
|
3630
|
+
* STORED status only — no live carrier poll. `trackShipment(id)` spends a
|
|
3631
|
+
* Shippo request because it is one parcel the customer opened; doing that
|
|
3632
|
+
* per row here would mean a carrier call for every parcel in their history
|
|
3633
|
+
* on every page load.
|
|
3634
|
+
*/
|
|
3635
|
+
async listShipments(opts?: {
|
|
3636
|
+
limit?: number;
|
|
3637
|
+
active?: boolean;
|
|
3638
|
+
}): Promise<CustomerShipmentListResponse> {
|
|
3639
|
+
const params = new URLSearchParams();
|
|
3640
|
+
if (typeof opts?.limit === "number")
|
|
3641
|
+
params.set("limit", String(opts.limit));
|
|
3642
|
+
if (opts?.active) params.set("active", "1");
|
|
3643
|
+
const qs = params.toString();
|
|
3644
|
+
return this.client.request({
|
|
3645
|
+
path: `customer-portal/shipments${qs ? `?${qs}` : ""}`,
|
|
3646
|
+
responseSchema: customerShipmentListResponseSchema,
|
|
3647
|
+
headers: this.headers(),
|
|
3648
|
+
});
|
|
3649
|
+
}
|
|
3650
|
+
|
|
3228
3651
|
/**
|
|
3229
3652
|
* The signed-in customer's orders (newest first), each with a shipment
|
|
3230
3653
|
* roll-up (`overallStatus`, `anyInTransit`, `latestEstimatedDelivery`) for
|
|
@@ -3320,6 +3743,281 @@ export class BiabDevCustomerPortalClient {
|
|
|
3320
3743
|
});
|
|
3321
3744
|
}
|
|
3322
3745
|
|
|
3746
|
+
/**
|
|
3747
|
+
* Ask for a replacement instead of a refund.
|
|
3748
|
+
*
|
|
3749
|
+
* Sugar over `submitReturnRequest` with `kind: "exchange"`, because an
|
|
3750
|
+
* exchange reads as its own action to the person writing the UI, and burying
|
|
3751
|
+
* it in an options bag is how it stays undiscovered. `variantId` is the one
|
|
3752
|
+
* they want instead — omit it when that still needs a conversation.
|
|
3753
|
+
*
|
|
3754
|
+
* @example
|
|
3755
|
+
* ```ts
|
|
3756
|
+
* await portal.submitExchangeRequest(orderId, {
|
|
3757
|
+
* exchangeForVariantId: largeVariantId,
|
|
3758
|
+
* reason: "wrong size",
|
|
3759
|
+
* });
|
|
3760
|
+
* ```
|
|
3761
|
+
*/
|
|
3762
|
+
async submitExchangeRequest(
|
|
3763
|
+
orderId: string,
|
|
3764
|
+
input?: Omit<CustomerReturnRequestInput, "kind">,
|
|
3765
|
+
): Promise<CustomerReturnRequestCreated> {
|
|
3766
|
+
return this.submitReturnRequest(orderId, {
|
|
3767
|
+
...(input ?? {}),
|
|
3768
|
+
kind: "exchange",
|
|
3769
|
+
});
|
|
3770
|
+
}
|
|
3771
|
+
|
|
3772
|
+
/**
|
|
3773
|
+
* Report that a parcel never arrived.
|
|
3774
|
+
*
|
|
3775
|
+
* This does NOT change the shipment's carrier status, and deliberately so:
|
|
3776
|
+
* when the carrier says delivered and the customer says otherwise, both
|
|
3777
|
+
* facts matter — the carrier's scan is what the org files a claim against.
|
|
3778
|
+
* The result carries `carrierStatus` alongside the report so a UI can show
|
|
3779
|
+
* both: *"Carrier says delivered Aug 14 · You reported not received Aug 16."*
|
|
3780
|
+
*
|
|
3781
|
+
* `contested: true` means exactly that disagreement. Re-reporting an open
|
|
3782
|
+
* claim returns it with `alreadyOpen: true` rather than erroring — a
|
|
3783
|
+
* customer tapping twice is anxious, not hostile.
|
|
3784
|
+
*
|
|
3785
|
+
* @example
|
|
3786
|
+
* ```ts
|
|
3787
|
+
* const r = await portal.reportNotReceived(shipmentId, {
|
|
3788
|
+
* note: "Nothing on the porch, checked with neighbours.",
|
|
3789
|
+
* });
|
|
3790
|
+
* if (r.contested) showCarrierDisputeHelp();
|
|
3791
|
+
* ```
|
|
3792
|
+
*/
|
|
3793
|
+
async reportNotReceived(
|
|
3794
|
+
shipmentId: string,
|
|
3795
|
+
input?: { note?: string },
|
|
3796
|
+
): Promise<CustomerNotReceivedResult> {
|
|
3797
|
+
const body = input?.note ? { note: input.note } : {};
|
|
3798
|
+
return this.client.request({
|
|
3799
|
+
method: "POST",
|
|
3800
|
+
path: `customer-portal/shipments/${encodeURIComponent(shipmentId)}/not-received`,
|
|
3801
|
+
body,
|
|
3802
|
+
responseSchema: customerNotReceivedResultSchema,
|
|
3803
|
+
headers: this.headers(),
|
|
3804
|
+
});
|
|
3805
|
+
}
|
|
3806
|
+
|
|
3807
|
+
/**
|
|
3808
|
+
* The product reviews this customer has written for this org.
|
|
3809
|
+
*
|
|
3810
|
+
* Distinct from `listReviews()`, which is reviews of the ORG. Both exist
|
|
3811
|
+
* because "are these people good to deal with" and "is this thing any good"
|
|
3812
|
+
* are different questions, stored in different tables, and shown in
|
|
3813
|
+
* different places.
|
|
3814
|
+
*
|
|
3815
|
+
* `isApproved: false` means QUEUED, not rejected — render it as "awaiting
|
|
3816
|
+
* review" or the customer will assume it was lost and write it again.
|
|
3817
|
+
*/
|
|
3818
|
+
/**
|
|
3819
|
+
* The customer's subscription with this org, and what the org offers.
|
|
3820
|
+
*
|
|
3821
|
+
* One subscription per org — `user_subscriptions` is unique on
|
|
3822
|
+
* (user, org) — so there is no id to pass. The org's live offerings come
|
|
3823
|
+
* back alongside, because a portal that reports "not subscribed" and
|
|
3824
|
+
* nothing else is a dead end.
|
|
3825
|
+
*
|
|
3826
|
+
* **Render entitlement from `subscription.hasAccess`, not from `status`.**
|
|
3827
|
+
* A lifetime purchase has no period to expire and a cancelled subscription
|
|
3828
|
+
* keeps access until the period already paid for ends; `hasAccess` is
|
|
3829
|
+
* computed by the same function the content gates use, so the portal and
|
|
3830
|
+
* the gate cannot disagree.
|
|
3831
|
+
*/
|
|
3832
|
+
async getSubscription(): Promise<CustomerSubscriptionResponse> {
|
|
3833
|
+
return this.client.request({
|
|
3834
|
+
path: "customer-portal/subscription",
|
|
3835
|
+
responseSchema: customerSubscriptionResponseSchema,
|
|
3836
|
+
headers: this.headers(),
|
|
3837
|
+
});
|
|
3838
|
+
}
|
|
3839
|
+
|
|
3840
|
+
/**
|
|
3841
|
+
* Cancel the subscription at the end of the paid period.
|
|
3842
|
+
*
|
|
3843
|
+
* Cancelling ends the RENEWAL, not the access: the customer has paid for
|
|
3844
|
+
* the period they are in and keeps everything until `accessUntil`. Ending
|
|
3845
|
+
* access on the click would take back something they already bought, which
|
|
3846
|
+
* is the most common way a subscription flow feels like a trap.
|
|
3847
|
+
*
|
|
3848
|
+
* Read `accessUntil` back to them — "active until 14 March" is the true
|
|
3849
|
+
* statement, not "cancelled".
|
|
3850
|
+
*/
|
|
3851
|
+
async cancelSubscription(): Promise<CustomerSubscriptionCancelResponse> {
|
|
3852
|
+
return this.client.request({
|
|
3853
|
+
method: "POST",
|
|
3854
|
+
path: "customer-portal/subscription/cancel",
|
|
3855
|
+
body: { resume: false },
|
|
3856
|
+
responseSchema: customerSubscriptionCancelResponseSchema,
|
|
3857
|
+
headers: this.headers(),
|
|
3858
|
+
});
|
|
3859
|
+
}
|
|
3860
|
+
|
|
3861
|
+
/**
|
|
3862
|
+
* Clear a pending cancellation.
|
|
3863
|
+
*
|
|
3864
|
+
* Between cancelling and the period ending nothing has actually been lost,
|
|
3865
|
+
* so changing your mind should cost one call rather than a re-purchase.
|
|
3866
|
+
* After the period ends there is nothing to resume and they subscribe again
|
|
3867
|
+
* like anyone else.
|
|
3868
|
+
*/
|
|
3869
|
+
async resumeSubscription(): Promise<CustomerSubscriptionCancelResponse> {
|
|
3870
|
+
return this.client.request({
|
|
3871
|
+
method: "POST",
|
|
3872
|
+
path: "customer-portal/subscription/cancel",
|
|
3873
|
+
body: { resume: true },
|
|
3874
|
+
responseSchema: customerSubscriptionCancelResponseSchema,
|
|
3875
|
+
headers: this.headers(),
|
|
3876
|
+
});
|
|
3877
|
+
}
|
|
3878
|
+
|
|
3879
|
+
/**
|
|
3880
|
+
* The content this subscription entitles them to — the answer to "what am
|
|
3881
|
+
* I actually getting for this?", which is the question asked right before
|
|
3882
|
+
* someone cancels.
|
|
3883
|
+
*
|
|
3884
|
+
* When `entitled` is false these are LOCKED previews, not an empty
|
|
3885
|
+
* entitlement: titles, excerpts and images, no bodies. Show them beside the
|
|
3886
|
+
* offer. Rendering an empty list would hide the pitch at exactly the moment
|
|
3887
|
+
* it is most relevant.
|
|
3888
|
+
*/
|
|
3889
|
+
async getSubscriberContent(opts?: {
|
|
3890
|
+
limit?: number;
|
|
3891
|
+
}): Promise<CustomerSubscriberContentResponse> {
|
|
3892
|
+
const params = new URLSearchParams();
|
|
3893
|
+
if (typeof opts?.limit === "number")
|
|
3894
|
+
params.set("limit", String(opts.limit));
|
|
3895
|
+
const qs = params.toString();
|
|
3896
|
+
return this.client.request({
|
|
3897
|
+
path: `customer-portal/subscription/content${qs ? `?${qs}` : ""}`,
|
|
3898
|
+
responseSchema: customerSubscriberContentResponseSchema,
|
|
3899
|
+
headers: this.headers(),
|
|
3900
|
+
});
|
|
3901
|
+
}
|
|
3902
|
+
|
|
3903
|
+
/**
|
|
3904
|
+
* Push config and the browsers this customer has registered.
|
|
3905
|
+
*
|
|
3906
|
+
* Returns `vapidPublicKey` too, because a page cannot subscribe without it.
|
|
3907
|
+
*/
|
|
3908
|
+
async getPushConfig(): Promise<CustomerPushConfig> {
|
|
3909
|
+
return this.client.request({
|
|
3910
|
+
path: "customer-portal/push",
|
|
3911
|
+
responseSchema: customerPushConfigSchema,
|
|
3912
|
+
headers: this.headers(),
|
|
3913
|
+
});
|
|
3914
|
+
}
|
|
3915
|
+
|
|
3916
|
+
/**
|
|
3917
|
+
* Register this browser.
|
|
3918
|
+
*
|
|
3919
|
+
* Pair with `subscribeToPush()` from `@businessdash/sdk/push-core`, which
|
|
3920
|
+
* handles the service worker, the permission prompt and the key encoding
|
|
3921
|
+
* and hands back exactly this payload.
|
|
3922
|
+
*
|
|
3923
|
+
* Upserted on the endpoint, so re-subscribing from the same browser
|
|
3924
|
+
* refreshes the row rather than creating a second one.
|
|
3925
|
+
*/
|
|
3926
|
+
async subscribePush(input: {
|
|
3927
|
+
endpoint: string;
|
|
3928
|
+
p256dh: string;
|
|
3929
|
+
auth: string;
|
|
3930
|
+
userAgent?: string | null;
|
|
3931
|
+
label?: string | null;
|
|
3932
|
+
categories?: string[] | null;
|
|
3933
|
+
}): Promise<{ id: string; created: boolean }> {
|
|
3934
|
+
return this.client.request({
|
|
3935
|
+
method: "POST",
|
|
3936
|
+
path: "customer-portal/push",
|
|
3937
|
+
body: input,
|
|
3938
|
+
responseSchema: z.object({ id: z.string(), created: z.boolean() }),
|
|
3939
|
+
headers: this.headers(),
|
|
3940
|
+
});
|
|
3941
|
+
}
|
|
3942
|
+
|
|
3943
|
+
/**
|
|
3944
|
+
* Rename a device, or narrow what it receives.
|
|
3945
|
+
*
|
|
3946
|
+
* `categories: []` means "nothing for now" and keeps the subscription
|
|
3947
|
+
* warm — browsers make re-granting a revoked permission deliberately
|
|
3948
|
+
* awkward, so an "off" that discards the subscription is hard to undo.
|
|
3949
|
+
*/
|
|
3950
|
+
async updatePushDevice(input: {
|
|
3951
|
+
id: string;
|
|
3952
|
+
label?: string | null;
|
|
3953
|
+
categories?: string[] | null;
|
|
3954
|
+
}): Promise<{ ok: boolean }> {
|
|
3955
|
+
return this.client.request({
|
|
3956
|
+
method: "PATCH",
|
|
3957
|
+
path: "customer-portal/push",
|
|
3958
|
+
body: input,
|
|
3959
|
+
responseSchema: z.object({ ok: z.boolean() }),
|
|
3960
|
+
headers: this.headers(),
|
|
3961
|
+
});
|
|
3962
|
+
}
|
|
3963
|
+
|
|
3964
|
+
/**
|
|
3965
|
+
* Forget a device. Takes an id or the endpoint.
|
|
3966
|
+
*
|
|
3967
|
+
* A browser tearing its own subscription down knows its endpoint, not our
|
|
3968
|
+
* row id — so pass what `unsubscribeFromPush()` returns.
|
|
3969
|
+
*/
|
|
3970
|
+
async unsubscribePush(input: {
|
|
3971
|
+
id?: string;
|
|
3972
|
+
endpoint?: string;
|
|
3973
|
+
}): Promise<{ ok: boolean; deletedId: string | null }> {
|
|
3974
|
+
return this.client.request({
|
|
3975
|
+
method: "DELETE",
|
|
3976
|
+
path: "customer-portal/push",
|
|
3977
|
+
body: input,
|
|
3978
|
+
responseSchema: z.object({
|
|
3979
|
+
ok: z.boolean(),
|
|
3980
|
+
deletedId: z.string().nullable(),
|
|
3981
|
+
}),
|
|
3982
|
+
headers: this.headers(),
|
|
3983
|
+
});
|
|
3984
|
+
}
|
|
3985
|
+
|
|
3986
|
+
async listProductReviews(): Promise<CustomerProductReviewList> {
|
|
3987
|
+
return this.client.request({
|
|
3988
|
+
path: "customer-portal/product-reviews",
|
|
3989
|
+
responseSchema: customerProductReviewListSchema,
|
|
3990
|
+
headers: this.headers(),
|
|
3991
|
+
});
|
|
3992
|
+
}
|
|
3993
|
+
|
|
3994
|
+
/**
|
|
3995
|
+
* Review a product this customer BOUGHT.
|
|
3996
|
+
*
|
|
3997
|
+
* The portal requires a matching order — it is standing inside the
|
|
3998
|
+
* customer's purchase history, which is the one thing the public storefront
|
|
3999
|
+
* does not know. That makes these verified-buyer reviews, and it stops a
|
|
4000
|
+
* competitor one-starring a catalogue they never bought.
|
|
4001
|
+
*
|
|
4002
|
+
* Rejects with `not_purchased` (403) when no order matches, and
|
|
4003
|
+
* `already_reviewed` (409) on a second review of the same product.
|
|
4004
|
+
* Moderation follows the ORG's policy, so the result may be
|
|
4005
|
+
* `awaitingModeration`.
|
|
4006
|
+
*/
|
|
4007
|
+
async submitProductReview(input: {
|
|
4008
|
+
productId: string;
|
|
4009
|
+
rating: number;
|
|
4010
|
+
content: string;
|
|
4011
|
+
}): Promise<CustomerProductReviewCreated> {
|
|
4012
|
+
return this.client.request({
|
|
4013
|
+
method: "POST",
|
|
4014
|
+
path: "customer-portal/product-reviews",
|
|
4015
|
+
body: input,
|
|
4016
|
+
responseSchema: customerProductReviewCreatedSchema,
|
|
4017
|
+
headers: this.headers(),
|
|
4018
|
+
});
|
|
4019
|
+
}
|
|
4020
|
+
|
|
3323
4021
|
/**
|
|
3324
4022
|
* List the digital downloads the customer is entitled to for a paid order
|
|
3325
4023
|
* (P10d). Each item is a ready-to-use URL — a short-lived presigned link for
|
|
@@ -3430,6 +4128,22 @@ export class BiabDevCustomerPortalClient {
|
|
|
3430
4128
|
* dispatch travel-time recompute layer ships, at which point the
|
|
3431
4129
|
* `eta` field switches sources without breaking this shape.
|
|
3432
4130
|
*/
|
|
4131
|
+
/**
|
|
4132
|
+
* Where the job and its technicians are in the dispatch lifecycle.
|
|
4133
|
+
*
|
|
4134
|
+
* Named for ETA historically; it is the DISPATCH STATUS surface, which is
|
|
4135
|
+
* why `getDispatchStatus` below is the same call under the name people
|
|
4136
|
+
* look for.
|
|
4137
|
+
*
|
|
4138
|
+
* Read `dispatchStatus` (job-level) for "is anyone on the way" and
|
|
4139
|
+
* `assignments[].dispatchStatus` for per-technician detail. They differ on
|
|
4140
|
+
* purpose: the job is `completed` only once the LAST assignee finishes.
|
|
4141
|
+
*
|
|
4142
|
+
* Nothing about the dispatch CASCADE is exposed — who was offered the job,
|
|
4143
|
+
* who declined, how many people were asked before someone accepted. That
|
|
4144
|
+
* is staff-internal, and a customer learning they were the fourth choice
|
|
4145
|
+
* helps nobody.
|
|
4146
|
+
*/
|
|
3433
4147
|
async getJobEta(jobId: string): Promise<CustomerJobEtaResponse> {
|
|
3434
4148
|
return this.client.request({
|
|
3435
4149
|
path: `customer-portal/jobs/${encodeURIComponent(jobId)}/eta`,
|
|
@@ -3438,6 +4152,17 @@ export class BiabDevCustomerPortalClient {
|
|
|
3438
4152
|
});
|
|
3439
4153
|
}
|
|
3440
4154
|
|
|
4155
|
+
/**
|
|
4156
|
+
* Dispatch status for a job — the same call as `getJobEta`, under the name
|
|
4157
|
+
* developers actually search for.
|
|
4158
|
+
*
|
|
4159
|
+
* An alias rather than a second route: two endpoints returning the same
|
|
4160
|
+
* thing is how they drift.
|
|
4161
|
+
*/
|
|
4162
|
+
async getDispatchStatus(jobId: string): Promise<CustomerJobEtaResponse> {
|
|
4163
|
+
return this.getJobEta(jobId);
|
|
4164
|
+
}
|
|
4165
|
+
|
|
3441
4166
|
/**
|
|
3442
4167
|
* Unified job activity feed: structured events (status changes,
|
|
3443
4168
|
* dispatch lifecycle, media uploads, quote/contract/invoice
|
|
@@ -3576,3 +4301,276 @@ export class BiabDevAuthClient {
|
|
|
3576
4301
|
});
|
|
3577
4302
|
}
|
|
3578
4303
|
}
|
|
4304
|
+
|
|
4305
|
+
/**
|
|
4306
|
+
* Booking, rescheduling, cancelling — and the conference call attached to it.
|
|
4307
|
+
*
|
|
4308
|
+
* ## Both sides of every flow
|
|
4309
|
+
*
|
|
4310
|
+
* An invitee acts with a signed TOKEN from their confirmation email; staff act
|
|
4311
|
+
* with the API key. Both reach the same service, so the same notifications fire
|
|
4312
|
+
* to the invitee AND every host either way, and the booking records which of
|
|
4313
|
+
* them made the change.
|
|
4314
|
+
*
|
|
4315
|
+
* ## Three tokens, not one
|
|
4316
|
+
*
|
|
4317
|
+
* A confirmed booking yields `manageToken`, `rescheduleToken` and `cancelToken`
|
|
4318
|
+
* separately. A cancel link in an email must not also be able to reschedule,
|
|
4319
|
+
* and neither should be able to read the whole booking. One token for all three
|
|
4320
|
+
* would make every emailed link as powerful as the strongest thing it enables.
|
|
4321
|
+
*
|
|
4322
|
+
* ## The conference link is re-minted on reschedule
|
|
4323
|
+
*
|
|
4324
|
+
* Moving a booking can issue a new meeting URL. Read `conferenceLink` back from
|
|
4325
|
+
* the reschedule result rather than reusing the one from confirmation —
|
|
4326
|
+
* otherwise the calendar invite you send points at a dead room.
|
|
4327
|
+
*/
|
|
4328
|
+
/**
|
|
4329
|
+
* Customer-portal invitations.
|
|
4330
|
+
*
|
|
4331
|
+
* For an org running its own admin tooling rather than the dashboard.
|
|
4332
|
+
*/
|
|
4333
|
+
export class BiabDevCustomerInvitesClient {
|
|
4334
|
+
constructor(private readonly client: BiabDevClient) {}
|
|
4335
|
+
|
|
4336
|
+
/**
|
|
4337
|
+
* Hand an invite link out again.
|
|
4338
|
+
*
|
|
4339
|
+
* **Rotates the token** — the previous link stops working. That is the
|
|
4340
|
+
* point rather than a side effect: if the reason for resending was "it went
|
|
4341
|
+
* to the wrong address", rotating IS the fix, and reusing the token would
|
|
4342
|
+
* leave the wrong recipient holding a working invitation. The cost is that
|
|
4343
|
+
* someone who finds the original email later gets a dead link, which is a
|
|
4344
|
+
* support message rather than an account.
|
|
4345
|
+
*
|
|
4346
|
+
* Rate limited to one send a minute per invitation, answering **429** with
|
|
4347
|
+
* a retry hint. Resend mails an address the caller chose, so an unbounded
|
|
4348
|
+
* one is a mail-bombing tool. Staff clicking the button in the dashboard
|
|
4349
|
+
* skip the wait; an API key does not, because an API key is the automated
|
|
4350
|
+
* caller the limit exists for.
|
|
4351
|
+
*
|
|
4352
|
+
* Refuses a **revoked** invitation (resending would quietly un-revoke it)
|
|
4353
|
+
* and a **fully-redeemed** one (it did its job; a new invitation should be
|
|
4354
|
+
* created as one, so the list keeps showing what happened).
|
|
4355
|
+
*/
|
|
4356
|
+
async resend(
|
|
4357
|
+
inviteId: string,
|
|
4358
|
+
opts?: { expiresInDays?: number | null },
|
|
4359
|
+
): Promise<CustomerInviteResendResponse> {
|
|
4360
|
+
return this.client.request({
|
|
4361
|
+
method: "POST",
|
|
4362
|
+
path: `customer-invites/${encodeURIComponent(inviteId)}/resend`,
|
|
4363
|
+
body:
|
|
4364
|
+
opts?.expiresInDays === undefined
|
|
4365
|
+
? {}
|
|
4366
|
+
: { expiresInDays: opts.expiresInDays },
|
|
4367
|
+
responseSchema: customerInviteResendResponseSchema,
|
|
4368
|
+
});
|
|
4369
|
+
}
|
|
4370
|
+
}
|
|
4371
|
+
|
|
4372
|
+
export class BiabDevSchedulingClient {
|
|
4373
|
+
constructor(
|
|
4374
|
+
private readonly client: BiabDevClient,
|
|
4375
|
+
private readonly siteId: string,
|
|
4376
|
+
) {}
|
|
4377
|
+
|
|
4378
|
+
private path(suffix: string): string {
|
|
4379
|
+
return `sites/${encodeURIComponent(this.siteId)}/scheduling/${suffix}`;
|
|
4380
|
+
}
|
|
4381
|
+
|
|
4382
|
+
/** What the org offers to book. */
|
|
4383
|
+
async listEventTypes(): Promise<SchedulingEventTypeList> {
|
|
4384
|
+
return this.client.request({
|
|
4385
|
+
path: this.path("event-types"),
|
|
4386
|
+
responseSchema: schedulingEventTypeListSchema,
|
|
4387
|
+
});
|
|
4388
|
+
}
|
|
4389
|
+
|
|
4390
|
+
/** One event type, plus the questions asked when booking it. */
|
|
4391
|
+
async getEventType(slug: string): Promise<SchedulingEventTypeDetail> {
|
|
4392
|
+
return this.client.request({
|
|
4393
|
+
path: this.path(`event-types/${encodeURIComponent(slug)}`),
|
|
4394
|
+
responseSchema: schedulingEventTypeDetailSchema,
|
|
4395
|
+
});
|
|
4396
|
+
}
|
|
4397
|
+
|
|
4398
|
+
/**
|
|
4399
|
+
* Free slots in a window.
|
|
4400
|
+
*
|
|
4401
|
+
* Ask for the window you are about to render, not the whole month: slot
|
|
4402
|
+
* computation walks every host's calendar and busy times, so a wide range
|
|
4403
|
+
* is a slow one.
|
|
4404
|
+
*/
|
|
4405
|
+
async getAvailableSlots(
|
|
4406
|
+
slug: string,
|
|
4407
|
+
params: { from: Date | string; to: Date | string },
|
|
4408
|
+
): Promise<SchedulingSlotsResponse> {
|
|
4409
|
+
const query = new URLSearchParams({
|
|
4410
|
+
from:
|
|
4411
|
+
typeof params.from === "string"
|
|
4412
|
+
? params.from
|
|
4413
|
+
: params.from.toISOString(),
|
|
4414
|
+
to: typeof params.to === "string" ? params.to : params.to.toISOString(),
|
|
4415
|
+
});
|
|
4416
|
+
return this.client.request({
|
|
4417
|
+
path: this.path(
|
|
4418
|
+
`event-types/${encodeURIComponent(slug)}/slots?${query.toString()}`,
|
|
4419
|
+
),
|
|
4420
|
+
responseSchema: schedulingSlotsResponseSchema,
|
|
4421
|
+
});
|
|
4422
|
+
}
|
|
4423
|
+
|
|
4424
|
+
/**
|
|
4425
|
+
* Book a slot.
|
|
4426
|
+
*
|
|
4427
|
+
* Returns `pending` rather than `confirmed` when the event type requires
|
|
4428
|
+
* approval — say "requested" in the UI for that case, because telling
|
|
4429
|
+
* someone their meeting is booked when staff still have to accept it is the
|
|
4430
|
+
* kind of thing they plan a day around.
|
|
4431
|
+
*/
|
|
4432
|
+
async confirmBooking(input: {
|
|
4433
|
+
eventTypeSlug: string;
|
|
4434
|
+
startAt: Date | string;
|
|
4435
|
+
invitee: {
|
|
4436
|
+
email: string;
|
|
4437
|
+
name: string;
|
|
4438
|
+
phone?: string | null;
|
|
4439
|
+
timezone: string;
|
|
4440
|
+
};
|
|
4441
|
+
answers?: Array<{ questionId: string; value: string }>;
|
|
4442
|
+
notes?: string | null;
|
|
4443
|
+
schedulingLinkId?: string;
|
|
4444
|
+
holderKey?: string | null;
|
|
4445
|
+
}): Promise<SchedulingBookingResult> {
|
|
4446
|
+
const body = {
|
|
4447
|
+
...input,
|
|
4448
|
+
startAt:
|
|
4449
|
+
typeof input.startAt === "string"
|
|
4450
|
+
? input.startAt
|
|
4451
|
+
: input.startAt.toISOString(),
|
|
4452
|
+
};
|
|
4453
|
+
return this.client.request({
|
|
4454
|
+
method: "POST",
|
|
4455
|
+
path: this.path("bookings"),
|
|
4456
|
+
body,
|
|
4457
|
+
responseSchema: schedulingBookingResultSchema,
|
|
4458
|
+
});
|
|
4459
|
+
}
|
|
4460
|
+
|
|
4461
|
+
/**
|
|
4462
|
+
* Read a booking with one of its signed tokens.
|
|
4463
|
+
*
|
|
4464
|
+
* `type` must match the token you hold — a cancel token will not open the
|
|
4465
|
+
* manage view. That is the point of issuing three.
|
|
4466
|
+
*/
|
|
4467
|
+
async getBooking(
|
|
4468
|
+
token: string,
|
|
4469
|
+
type: "manage" | "cancel" | "reschedule" = "manage",
|
|
4470
|
+
): Promise<SchedulingBookingView> {
|
|
4471
|
+
return this.client.request({
|
|
4472
|
+
path: this.path(
|
|
4473
|
+
`bookings/by-token/${encodeURIComponent(token)}?type=${encodeURIComponent(type)}`,
|
|
4474
|
+
),
|
|
4475
|
+
responseSchema: schedulingBookingViewSchema,
|
|
4476
|
+
});
|
|
4477
|
+
}
|
|
4478
|
+
|
|
4479
|
+
/**
|
|
4480
|
+
* Move a booking — the INVITEE side, with their reschedule token.
|
|
4481
|
+
*
|
|
4482
|
+
* Notifies the invitee and every host, and cancels the reminder jobs queued
|
|
4483
|
+
* against the old time so nobody gets "your meeting starts in 15 minutes"
|
|
4484
|
+
* for a slot that moved.
|
|
4485
|
+
*/
|
|
4486
|
+
async rescheduleBooking(input: {
|
|
4487
|
+
token: string;
|
|
4488
|
+
newStartAt: Date | string;
|
|
4489
|
+
reason?: string | null;
|
|
4490
|
+
}): Promise<SchedulingRescheduleResult> {
|
|
4491
|
+
return this.client.request({
|
|
4492
|
+
method: "POST",
|
|
4493
|
+
path: this.path(`bookings/by-token/${encodeURIComponent(input.token)}`),
|
|
4494
|
+
body: {
|
|
4495
|
+
action: "reschedule",
|
|
4496
|
+
newStartAt:
|
|
4497
|
+
typeof input.newStartAt === "string"
|
|
4498
|
+
? input.newStartAt
|
|
4499
|
+
: input.newStartAt.toISOString(),
|
|
4500
|
+
...(input.reason ? { reason: input.reason } : {}),
|
|
4501
|
+
},
|
|
4502
|
+
responseSchema: schedulingRescheduleResultSchema,
|
|
4503
|
+
});
|
|
4504
|
+
}
|
|
4505
|
+
|
|
4506
|
+
/**
|
|
4507
|
+
* Move a booking as STAFF, with the API key rather than an invitee token.
|
|
4508
|
+
*
|
|
4509
|
+
* Same service as the token path, so the invitee AND every host are
|
|
4510
|
+
* notified either way, and the reminder jobs for the old time are
|
|
4511
|
+
* cancelled either way. `actorUserId` records WHO moved it, which is what
|
|
4512
|
+
* lets a customer's history say "they rescheduled" rather than leaving an
|
|
4513
|
+
* unexplained change.
|
|
4514
|
+
*/
|
|
4515
|
+
async rescheduleBookingAsStaff(input: {
|
|
4516
|
+
bookingId: string;
|
|
4517
|
+
newStartAt: Date | string;
|
|
4518
|
+
reason?: string | null;
|
|
4519
|
+
actorUserId?: string | null;
|
|
4520
|
+
}): Promise<SchedulingRescheduleResult> {
|
|
4521
|
+
return this.client.request({
|
|
4522
|
+
method: "POST",
|
|
4523
|
+
path: this.path(`bookings/${encodeURIComponent(input.bookingId)}/manage`),
|
|
4524
|
+
body: {
|
|
4525
|
+
action: "reschedule",
|
|
4526
|
+
newStartAt:
|
|
4527
|
+
typeof input.newStartAt === "string"
|
|
4528
|
+
? input.newStartAt
|
|
4529
|
+
: input.newStartAt.toISOString(),
|
|
4530
|
+
...(input.reason ? { reason: input.reason } : {}),
|
|
4531
|
+
...(input.actorUserId ? { actorUserId: input.actorUserId } : {}),
|
|
4532
|
+
},
|
|
4533
|
+
responseSchema: schedulingRescheduleResultSchema,
|
|
4534
|
+
});
|
|
4535
|
+
}
|
|
4536
|
+
|
|
4537
|
+
/** Cancel a booking as STAFF. Notifies the invitee and every host. */
|
|
4538
|
+
async cancelBookingAsStaff(input: {
|
|
4539
|
+
bookingId: string;
|
|
4540
|
+
reason?: string | null;
|
|
4541
|
+
actorUserId?: string | null;
|
|
4542
|
+
}): Promise<SchedulingCancelResult> {
|
|
4543
|
+
return this.client.request({
|
|
4544
|
+
method: "POST",
|
|
4545
|
+
path: this.path(`bookings/${encodeURIComponent(input.bookingId)}/manage`),
|
|
4546
|
+
body: {
|
|
4547
|
+
action: "cancel",
|
|
4548
|
+
...(input.reason ? { reason: input.reason } : {}),
|
|
4549
|
+
...(input.actorUserId ? { actorUserId: input.actorUserId } : {}),
|
|
4550
|
+
},
|
|
4551
|
+
responseSchema: schedulingCancelResultSchema,
|
|
4552
|
+
});
|
|
4553
|
+
}
|
|
4554
|
+
|
|
4555
|
+
/**
|
|
4556
|
+
* Cancel a booking — the INVITEE side, with their cancel token.
|
|
4557
|
+
*
|
|
4558
|
+
* `reason` is passed to the hosts. Worth collecting: "something came up" and
|
|
4559
|
+
* "I booked the wrong service" lead to different follow-ups, and the second
|
|
4560
|
+
* is recoverable revenue.
|
|
4561
|
+
*/
|
|
4562
|
+
async cancelBooking(input: {
|
|
4563
|
+
token: string;
|
|
4564
|
+
reason?: string | null;
|
|
4565
|
+
}): Promise<SchedulingCancelResult> {
|
|
4566
|
+
return this.client.request({
|
|
4567
|
+
method: "POST",
|
|
4568
|
+
path: this.path(`bookings/by-token/${encodeURIComponent(input.token)}`),
|
|
4569
|
+
body: {
|
|
4570
|
+
action: "cancel",
|
|
4571
|
+
...(input.reason ? { reason: input.reason } : {}),
|
|
4572
|
+
},
|
|
4573
|
+
responseSchema: schedulingCancelResultSchema,
|
|
4574
|
+
});
|
|
4575
|
+
}
|
|
4576
|
+
}
|