@advizorconnect/sdk 0.1.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/CHANGELOG.md ADDED
@@ -0,0 +1,21 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 — 2026-03-02
4
+
5
+ Initial release.
6
+
7
+ ### Features
8
+
9
+ - **32 typed methods** across 7 API tag groups:
10
+ - Integration (2): list/get group advisors
11
+ - Sessions (13): full call lifecycle from create through end-and-capture
12
+ - Owner / API Keys (4): CRUD + rotation with overlap windows
13
+ - Widget (3): token minting, config, advisor listing
14
+ - Owner / Widget (2): security settings (allowed origins)
15
+ - Owner / Invites (5): create, list, rotate, revoke, send
16
+ - Owner / Groups (4): slug management, settings
17
+ - **`V1SdkError`** with structured fields: `status`, `message`, `code`, `requestId`, `retryable`
18
+ - **AbortController timeout** — 20s default, configurable via `timeoutMs`
19
+ - **Custom fetch injection** — pass your own `fetchImpl` for testing or custom HTTP clients
20
+ - **Generated types** from OpenAPI v1 spec via `openapi-typescript`
21
+ - **Tree-shakeable** ESM package with `sideEffects: false`
package/README.md ADDED
@@ -0,0 +1,191 @@
1
+ # @advizorconnect/sdk
2
+
3
+ Typed TypeScript SDK for the Advizor Connect v1 integration API. Build custom caller experiences on top of the Advizor Connect platform.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @advizorconnect/sdk
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { createV1Client } from '@advizorconnect/sdk';
15
+
16
+ const client = createV1Client({
17
+ baseUrl: 'https://api.advizor-connect.com',
18
+ apiKey: 'your-api-key', // from owner dashboard → Settings → API Keys
19
+ });
20
+
21
+ // List available advisors
22
+ const { advisors } = await client.listGroupAdvisors({
23
+ groupId: 'your-group-id',
24
+ onlineStatus: 'available',
25
+ });
26
+
27
+ // Create a session with the first available advisor
28
+ const { session } = await client.createSession({
29
+ groupId: 'your-group-id',
30
+ advisorId: advisors[0].id,
31
+ callerPhone: '+15551234567',
32
+ idempotencyKey: crypto.randomUUID(),
33
+ });
34
+
35
+ console.log('Session created:', session.id, session.status);
36
+ ```
37
+
38
+ ## Authentication
39
+
40
+ All API calls require a group API key passed as a Bearer token. Create API keys from the owner dashboard under **Settings → API Keys**.
41
+
42
+ ```typescript
43
+ const client = createV1Client({
44
+ baseUrl: 'https://api.advizor-connect.com',
45
+ apiKey: process.env.ADVIZOR_CONNECT_API_KEY!,
46
+ });
47
+ ```
48
+
49
+ ## Client Options
50
+
51
+ | Option | Type | Default | Description |
52
+ |--------|------|---------|-------------|
53
+ | `baseUrl` | `string` | *required* | API server URL |
54
+ | `apiKey` | `string` | *required* | Group API key (Bearer token) |
55
+ | `fetchImpl` | `typeof fetch` | `globalThis.fetch` | Custom fetch implementation |
56
+ | `timeoutMs` | `number` | `20000` | Request timeout in milliseconds |
57
+
58
+ ## API Methods
59
+
60
+ ### Integration
61
+
62
+ | Method | Description |
63
+ |--------|-------------|
64
+ | `listGroupAdvisors({ groupId, sort?, order?, onlineStatus?, minBillableMinutes30d? })` | List advisors in a group with optional filters |
65
+ | `getGroupAdvisor({ groupId, advisorId })` | Get a single advisor's details |
66
+
67
+ ### Sessions
68
+
69
+ | Method | Description |
70
+ |--------|-------------|
71
+ | `createSession({ groupId, advisorId, callerPhone, idempotencyKey, ... })` | Create a new call session |
72
+ | `getSession({ sessionId })` | Get session details and status |
73
+ | `getSessionLedger({ sessionId })` | Get billing ledger for a session |
74
+ | `sendOtp({ sessionId })` | Send OTP to caller's phone |
75
+ | `verifyPhone({ sessionId, otp })` | Verify caller phone with OTP code |
76
+ | `createStripeIntent({ sessionId, holdCents? })` | Create a Stripe PaymentIntent for hold |
77
+ | `createPayPalOrder({ sessionId, holdCents? })` | Create a PayPal order for hold |
78
+ | `authorizePayPalOrder({ sessionId, orderId })` | Authorize a PayPal order |
79
+ | `authorizeHold({ sessionId, paymentIntentId?, holdCents? })` | Confirm payment hold authorization |
80
+ | `startConnecting({ sessionId })` | Initiate telephony bridge |
81
+ | `markConnected({ sessionId })` | Mark session as connected (call active) |
82
+ | `tick({ sessionId, asOfIso? })` | Record a billing tick (~60s intervals) |
83
+ | `endAndCapture({ sessionId, reason? })` | End the call and capture payment |
84
+ | `submitSessionReview({ sessionId, rating, body?, authorName? })` | Submit a post-call review |
85
+
86
+ ### Owner / API Keys
87
+
88
+ | Method | Description |
89
+ |--------|-------------|
90
+ | `listApiKeys({ groupId })` | List all API keys for a group |
91
+ | `createApiKey({ groupId, name, scopes?, expiresAt? })` | Create a new API key |
92
+ | `rotateApiKey({ groupId, keyId, overlapSeconds?, name?, expiresAt? })` | Rotate an API key with overlap window |
93
+ | `revokeApiKey({ groupId, keyId })` | Revoke an API key |
94
+
95
+ ### Widget
96
+
97
+ | Method | Description |
98
+ |--------|-------------|
99
+ | `mintWidgetToken({ groupId })` | Mint a short-lived widget auth token |
100
+ | `getWidgetConfig({ groupId })` | Get widget configuration |
101
+ | `listWidgetAdvisors({ groupId })` | List advisors visible in the widget |
102
+ | `getWidgetSecurity({ groupId })` | Get widget security settings |
103
+ | `updateWidgetSecurity({ groupId, allowedOrigins })` | Update allowed origins for widget |
104
+
105
+ ### Owner / Invites
106
+
107
+ | Method | Description |
108
+ |--------|-------------|
109
+ | `createInvite({ groupId, email? })` | Create an advisor invite |
110
+ | `listInvites({ groupId, status? })` | List invites for a group |
111
+ | `rotateInvite({ groupId, inviteId })` | Rotate invite token |
112
+ | `revokeInvite({ groupId, inviteId })` | Revoke an invite |
113
+ | `sendInvite({ groupId, inviteId })` | Send invite email |
114
+
115
+ ### Owner / Groups
116
+
117
+ | Method | Description |
118
+ |--------|-------------|
119
+ | `getGroupBySlug({ slug })` | Look up a group by its URL slug |
120
+ | `updateGroupSlug({ groupId, slug })` | Change a group's URL slug |
121
+ | `getGroupSettings({ groupId })` | Get group settings |
122
+ | `updateGroupSettings({ groupId, requireCallerOtp? })` | Update group settings |
123
+
124
+ ## Error Handling
125
+
126
+ All API errors throw `V1SdkError` with structured fields:
127
+
128
+ ```typescript
129
+ import { V1SdkError } from '@advizorconnect/sdk';
130
+
131
+ try {
132
+ await client.createSession({ ... });
133
+ } catch (err) {
134
+ if (err instanceof V1SdkError) {
135
+ console.error('Status:', err.status); // HTTP status code
136
+ console.error('Message:', err.message); // Human-readable message
137
+ console.error('Code:', err.code); // Machine-readable error code
138
+ console.error('Request ID:', err.requestId); // For support reference
139
+ console.error('Retryable:', err.retryable); // Whether to retry
140
+
141
+ if (err.retryable) {
142
+ // Safe to retry with backoff
143
+ }
144
+ }
145
+ }
146
+ ```
147
+
148
+ ## TypeScript Types
149
+
150
+ The SDK exports generated types from the OpenAPI spec for full type safety:
151
+
152
+ ```typescript
153
+ import type {
154
+ IntegrationAdvisor,
155
+ SessionEnvelope,
156
+ ApiSession,
157
+ LedgerEvent,
158
+ ApiKeyRecord,
159
+ InviteRecord,
160
+ } from '@advizorconnect/sdk';
161
+ ```
162
+
163
+ For advanced usage, raw OpenAPI path/component types are also available:
164
+
165
+ ```typescript
166
+ import type { paths, components, operations } from '@advizorconnect/sdk';
167
+ ```
168
+
169
+ ## Session Lifecycle
170
+
171
+ A typical call session follows this sequence:
172
+
173
+ 1. **Create session** — `createSession()`
174
+ 2. **Verify caller** — `sendOtp()` → `verifyPhone()`
175
+ 3. **Authorize payment** — `createStripeIntent()` → `authorizeHold()`
176
+ 4. **Connect call** — `startConnecting()` → `markConnected()`
177
+ 5. **Bill during call** — `tick()` every ~60 seconds
178
+ 6. **End and capture** — `endAndCapture()`
179
+
180
+ See the [Session Lifecycle Guide](../../docs/sdk-session-lifecycle.md) for detailed code examples and state machine documentation.
181
+
182
+ For a complete working example, see the [Paimoney reference app](../../examples/paimoney/) — a production-grade Next.js application demonstrating the BFF proxy integration pattern.
183
+
184
+ ## Requirements
185
+
186
+ - Node.js 18+ (or any runtime with global `fetch`)
187
+ - API key from the owner dashboard
188
+
189
+ ## License
190
+
191
+ Proprietary. See LICENSE for details.
@@ -0,0 +1,483 @@
1
+ import type { components } from './generated/v1.js';
2
+ type FetchLike = typeof fetch;
3
+ export type V1ClientOptions = {
4
+ baseUrl: string;
5
+ apiKey: string;
6
+ fetchImpl?: FetchLike;
7
+ timeoutMs?: number;
8
+ };
9
+ export type ApiKeyRecord = components['schemas']['ApiKeyRecord'];
10
+ export type InviteRecord = components['schemas']['InviteRecord'];
11
+ export type GroupSettingsResponse = components['schemas']['GroupSettingsResponse'];
12
+ export type WidgetConfigResponse = components['schemas']['WidgetConfigResponse'];
13
+ export type WidgetAdvisor = components['schemas']['WidgetAdvisor'];
14
+ export type WidgetSecurityConfig = components['schemas']['WidgetSecurityConfig'];
15
+ export type IntegrationAdvisor = components['schemas']['IntegrationAdvisor'];
16
+ export type IntegrationAdvisorFeedResponse = components['schemas']['IntegrationAdvisorFeedResponse'];
17
+ export type SessionEnvelope = components['schemas']['SessionEnvelope'];
18
+ export type ApiSession = components['schemas']['ApiSession'];
19
+ export type LedgerEvent = components['schemas']['LedgerEvent'];
20
+ export type ErrorResponse = components['schemas']['ErrorResponse'];
21
+ export declare function createV1Client(options: V1ClientOptions): {
22
+ listGroupAdvisors(input: {
23
+ groupId: string;
24
+ sort?: "billable_minutes_30d" | "online_status" | "alphabetical";
25
+ order?: "asc" | "desc";
26
+ onlineStatus?: "online" | "offline" | "available" | "busy";
27
+ minBillableMinutes30d?: number;
28
+ }): Promise<{
29
+ groupId: string;
30
+ groupSlug: string;
31
+ advisors: components["schemas"]["IntegrationAdvisor"][];
32
+ count: number;
33
+ sort?: {
34
+ [key: string]: unknown;
35
+ };
36
+ filter?: {
37
+ [key: string]: unknown;
38
+ };
39
+ } & {
40
+ [key: string]: unknown;
41
+ }>;
42
+ getGroupAdvisor(input: {
43
+ groupId: string;
44
+ advisorId: string;
45
+ }): Promise<{
46
+ id: string;
47
+ groupId: string;
48
+ name: string;
49
+ avatarUrl?: string | null;
50
+ headline?: string | null;
51
+ status: "available" | "busy" | "offline";
52
+ available: boolean;
53
+ billableMinutes30d?: number;
54
+ lastLoginAt?: string | null;
55
+ } & {
56
+ [key: string]: unknown;
57
+ }>;
58
+ createSession(input: {
59
+ groupId: string;
60
+ advisorId: string;
61
+ callerPhone: string;
62
+ idempotencyKey: string;
63
+ callerUsername?: string;
64
+ callerEmail?: string;
65
+ }): Promise<{
66
+ session: components["schemas"]["ApiSession"];
67
+ } & {
68
+ [key: string]: unknown;
69
+ }>;
70
+ getSession(input: {
71
+ sessionId: string;
72
+ }): Promise<{
73
+ session: components["schemas"]["ApiSession"];
74
+ } & {
75
+ [key: string]: unknown;
76
+ }>;
77
+ getSessionLedger(input: {
78
+ sessionId: string;
79
+ }): Promise<{
80
+ events: components["schemas"]["LedgerEvent"][];
81
+ }>;
82
+ sendOtp(input: {
83
+ sessionId: string;
84
+ }): Promise<{
85
+ [key: string]: unknown;
86
+ }>;
87
+ verifyPhone(input: {
88
+ sessionId: string;
89
+ otp: string;
90
+ }): Promise<{
91
+ session: components["schemas"]["ApiSession"];
92
+ } & {
93
+ [key: string]: unknown;
94
+ }>;
95
+ createStripeIntent(input: {
96
+ sessionId: string;
97
+ holdCents?: 2500 | 5000;
98
+ }): Promise<{
99
+ paymentIntentId: string;
100
+ clientSecret: string;
101
+ holdCents: number;
102
+ currency: string;
103
+ }>;
104
+ createPayPalOrder(input: {
105
+ sessionId: string;
106
+ holdCents?: 2500 | 5000;
107
+ }): Promise<{
108
+ orderId: string;
109
+ holdCents: number;
110
+ currency: string;
111
+ }>;
112
+ authorizePayPalOrder(input: {
113
+ sessionId: string;
114
+ orderId: string;
115
+ }): Promise<{
116
+ session: components["schemas"]["ApiSession"];
117
+ } & {
118
+ [key: string]: unknown;
119
+ }>;
120
+ authorizeHold(input: {
121
+ sessionId: string;
122
+ paymentIntentId?: string;
123
+ holdCents?: number;
124
+ }): Promise<{
125
+ session: components["schemas"]["ApiSession"];
126
+ } & {
127
+ [key: string]: unknown;
128
+ }>;
129
+ startConnecting(input: {
130
+ sessionId: string;
131
+ }): Promise<{
132
+ session: components["schemas"]["ApiSession"];
133
+ } & {
134
+ [key: string]: unknown;
135
+ }>;
136
+ markConnected(input: {
137
+ sessionId: string;
138
+ }): Promise<{
139
+ session: components["schemas"]["ApiSession"];
140
+ } & {
141
+ [key: string]: unknown;
142
+ }>;
143
+ tick(input: {
144
+ sessionId: string;
145
+ asOfIso?: string;
146
+ }): Promise<{
147
+ session: components["schemas"]["ApiSession"];
148
+ } & {
149
+ [key: string]: unknown;
150
+ }>;
151
+ endAndCapture(input: {
152
+ sessionId: string;
153
+ reason?: string;
154
+ }): Promise<{
155
+ session: components["schemas"]["ApiSession"];
156
+ } & {
157
+ [key: string]: unknown;
158
+ }>;
159
+ submitSessionReview(input: {
160
+ sessionId: string;
161
+ rating: number;
162
+ body?: string;
163
+ authorName?: string;
164
+ }): Promise<{
165
+ ok: boolean;
166
+ duplicate?: boolean;
167
+ review?: {
168
+ [key: string]: unknown;
169
+ };
170
+ }>;
171
+ listApiKeys(input: {
172
+ groupId: string;
173
+ }): Promise<{
174
+ keys: components["schemas"]["ApiKeyRecord"][];
175
+ availableScopes: string[];
176
+ }>;
177
+ createApiKey(input: {
178
+ groupId: string;
179
+ name: string;
180
+ scopes?: string[];
181
+ expiresAt?: string;
182
+ }): Promise<{
183
+ key: components["schemas"]["ApiKeyRecord"];
184
+ apiKey: string;
185
+ availableScopes: string[];
186
+ }>;
187
+ rotateApiKey(input: {
188
+ groupId: string;
189
+ keyId: string;
190
+ overlapSeconds?: number;
191
+ name?: string;
192
+ expiresAt?: string;
193
+ }): Promise<{
194
+ key: components["schemas"]["ApiKeyRecord"];
195
+ apiKey: string;
196
+ previousKey: {
197
+ id: string;
198
+ expiresAt?: string | null;
199
+ };
200
+ }>;
201
+ revokeApiKey(input: {
202
+ groupId: string;
203
+ keyId: string;
204
+ }): Promise<{
205
+ ok: boolean;
206
+ revokedAt?: string | null;
207
+ expiresAt?: string | null;
208
+ }>;
209
+ mintWidgetToken(input: {
210
+ groupId: string;
211
+ }): Promise<{
212
+ token: string;
213
+ expiresAt: string;
214
+ mode: "preview" | "embed";
215
+ }>;
216
+ getWidgetConfig(input: {
217
+ groupId: string;
218
+ }): Promise<{
219
+ groupId: string;
220
+ groupSlug: string;
221
+ groupName: string;
222
+ theme: {
223
+ [key: string]: unknown;
224
+ };
225
+ requireCallerOtp: boolean;
226
+ embedTemplate: {
227
+ [key: string]: unknown;
228
+ };
229
+ }>;
230
+ listWidgetAdvisors(input: {
231
+ groupId: string;
232
+ }): Promise<{
233
+ groupId: string;
234
+ groupSlug: string;
235
+ advisors: components["schemas"]["WidgetAdvisor"][];
236
+ count: number;
237
+ }>;
238
+ getWidgetSecurity(input: {
239
+ groupId: string;
240
+ }): Promise<{
241
+ security: components["schemas"]["WidgetSecurityConfig"];
242
+ previewOrigin: string;
243
+ }>;
244
+ updateWidgetSecurity(input: {
245
+ groupId: string;
246
+ allowedOrigins: string[];
247
+ }): Promise<{
248
+ security: components["schemas"]["WidgetSecurityConfig"];
249
+ previewOrigin: string;
250
+ }>;
251
+ createInvite(input: {
252
+ groupId: string;
253
+ email?: string;
254
+ }): Promise<{
255
+ invite: components["schemas"]["InviteRecord"];
256
+ }>;
257
+ listInvites(input: {
258
+ groupId: string;
259
+ status?: string;
260
+ }): Promise<{
261
+ invites: components["schemas"]["InviteRecord"][];
262
+ }>;
263
+ rotateInvite(input: {
264
+ groupId: string;
265
+ inviteId: string;
266
+ }): Promise<{
267
+ ok: boolean;
268
+ inviteId: string;
269
+ invite: components["schemas"]["InviteRecord"];
270
+ }>;
271
+ revokeInvite(input: {
272
+ groupId: string;
273
+ inviteId: string;
274
+ }): Promise<{
275
+ ok: boolean;
276
+ inviteId: string;
277
+ revokedAt?: string | null;
278
+ }>;
279
+ sendInvite(input: {
280
+ groupId: string;
281
+ inviteId: string;
282
+ }): Promise<{
283
+ ok: boolean;
284
+ inviteId: string;
285
+ resendEmailId: string;
286
+ }>;
287
+ getGroupBySlug(input: {
288
+ slug: string;
289
+ }): Promise<{
290
+ group: {
291
+ id: string;
292
+ slug: string;
293
+ name: string;
294
+ theme?: {
295
+ [key: string]: unknown;
296
+ } | null;
297
+ isPersonal?: boolean;
298
+ requireCallerOtp?: boolean;
299
+ };
300
+ requestedSlug: string;
301
+ canonicalSlug: string;
302
+ isAlias: boolean;
303
+ }>;
304
+ updateGroupSlug(input: {
305
+ groupId: string;
306
+ slug: string;
307
+ }): Promise<{
308
+ group: {
309
+ [key: string]: unknown;
310
+ };
311
+ }>;
312
+ getGroupSettings(input: {
313
+ groupId: string;
314
+ }): Promise<{
315
+ settings: components["schemas"]["GroupSettingsResponse"];
316
+ }>;
317
+ updateGroupSettings(input: {
318
+ groupId: string;
319
+ requireCallerOtp?: boolean;
320
+ }): Promise<{
321
+ settings: components["schemas"]["GroupSettingsResponse"];
322
+ }>;
323
+ listOwnerAdvisors(input: {
324
+ groupId: string;
325
+ }): Promise<{
326
+ advisors: {
327
+ [key: string]: unknown;
328
+ }[];
329
+ }>;
330
+ moderateAdvisor(input: {
331
+ groupId: string;
332
+ advisorId: string;
333
+ action: "approve" | "reject" | "suspend" | "unsuspend";
334
+ reason?: string;
335
+ note?: string;
336
+ }): Promise<{
337
+ ok: boolean;
338
+ advisor: {
339
+ [key: string]: unknown;
340
+ };
341
+ }>;
342
+ listAdvisorModerationEvents(input: {
343
+ groupId: string;
344
+ advisorId: string;
345
+ before?: string;
346
+ limit?: number;
347
+ }): Promise<{
348
+ events: {
349
+ [key: string]: unknown;
350
+ }[];
351
+ }>;
352
+ listGroupModerationEvents(input: {
353
+ groupId: string;
354
+ before?: string;
355
+ limit?: number;
356
+ }): Promise<{
357
+ events: {
358
+ [key: string]: unknown;
359
+ }[];
360
+ }>;
361
+ removeAdvisor(input: {
362
+ groupId: string;
363
+ advisorId: string;
364
+ reason?: string;
365
+ }): Promise<{
366
+ ok: boolean;
367
+ }>;
368
+ updateGroupTheme(input: {
369
+ groupId: string;
370
+ theme: Record<string, unknown>;
371
+ }): Promise<{
372
+ group: {
373
+ [key: string]: unknown;
374
+ };
375
+ }>;
376
+ getSetupState(input: {
377
+ groupId: string;
378
+ }): Promise<{
379
+ setupState: {
380
+ [key: string]: unknown;
381
+ };
382
+ }>;
383
+ updateSetupState(input: {
384
+ groupId: string;
385
+ setupState: Record<string, unknown>;
386
+ }): Promise<{
387
+ setupState: {
388
+ [key: string]: unknown;
389
+ };
390
+ }>;
391
+ getAnalyticsOverview(input: {
392
+ groupId: string;
393
+ }): Promise<{
394
+ [key: string]: unknown;
395
+ }>;
396
+ getAnalyticsRevenue(input: {
397
+ groupId: string;
398
+ }): Promise<{
399
+ [key: string]: unknown;
400
+ }>;
401
+ getAnalyticsSessions(input: {
402
+ groupId: string;
403
+ page?: number;
404
+ limit?: number;
405
+ }): Promise<{
406
+ [key: string]: unknown;
407
+ }>;
408
+ getWidgetEventAnalytics(input: {
409
+ groupId: string;
410
+ since?: string;
411
+ limit?: number;
412
+ }): Promise<{
413
+ events: {
414
+ [key: string]: unknown;
415
+ }[];
416
+ }>;
417
+ listOwnerPayouts(input: {
418
+ groupId: string;
419
+ page?: number;
420
+ limit?: number;
421
+ }): Promise<{
422
+ payouts: {
423
+ [key: string]: unknown;
424
+ }[];
425
+ total: number;
426
+ page: number;
427
+ limit: number;
428
+ }>;
429
+ batchCreateOwnerInvites(input: {
430
+ groupId: string;
431
+ emails: string[];
432
+ }): Promise<{
433
+ results: {
434
+ [key: string]: unknown;
435
+ }[];
436
+ }>;
437
+ previewInvite(input: {
438
+ token: string;
439
+ }): Promise<{
440
+ [key: string]: unknown;
441
+ }>;
442
+ acceptInvite(input: {
443
+ token: string;
444
+ }): Promise<{
445
+ [key: string]: unknown;
446
+ }>;
447
+ createWidgetCallback(input: {
448
+ groupId: string;
449
+ body: Record<string, unknown>;
450
+ }): Promise<{
451
+ [key: string]: unknown;
452
+ }>;
453
+ getWidgetCallbackStatus(input: {
454
+ groupId: string;
455
+ phone: string;
456
+ }): Promise<{
457
+ [key: string]: unknown;
458
+ }>;
459
+ ingestWidgetEvent(input: {
460
+ groupId: string;
461
+ eventType: string;
462
+ sessionId: string;
463
+ eventData?: Record<string, unknown>;
464
+ }): Promise<{
465
+ ok: boolean;
466
+ }>;
467
+ getGroupSitemap(input: {
468
+ groupSlug: string;
469
+ }): Promise<never>;
470
+ getAdvisorJsonLd(input: {
471
+ groupSlug: string;
472
+ advisorId: string;
473
+ }): Promise<never>;
474
+ getTaxonomy(): Promise<{
475
+ [key: string]: unknown;
476
+ }>;
477
+ generateWidgetEmbedCode(input: {
478
+ groupId: string;
479
+ }): Promise<{
480
+ [key: string]: unknown;
481
+ }>;
482
+ };
483
+ export {};
package/dist/client.js ADDED
@@ -0,0 +1,469 @@
1
+ import { V1SdkError } from './errors.js';
2
+ function withQuery(path, query) {
3
+ if (!query)
4
+ return path;
5
+ const params = new URLSearchParams();
6
+ for (const [k, v] of Object.entries(query)) {
7
+ if (v == null)
8
+ continue;
9
+ params.set(k, String(v));
10
+ }
11
+ const qs = params.toString();
12
+ return qs ? `${path}?${qs}` : path;
13
+ }
14
+ async function parseResponseBody(res) {
15
+ const text = await res.text();
16
+ if (!text)
17
+ return null;
18
+ try {
19
+ return JSON.parse(text);
20
+ }
21
+ catch {
22
+ return text;
23
+ }
24
+ }
25
+ export function createV1Client(options) {
26
+ const baseUrl = options.baseUrl.replace(/\/$/, '');
27
+ const fetchImpl = options.fetchImpl ?? fetch;
28
+ const timeoutMs = options.timeoutMs ?? 20_000;
29
+ async function request(input) {
30
+ const controller = new AbortController();
31
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
32
+ try {
33
+ const res = await fetchImpl(withQuery(`${baseUrl}${input.path}`, input.query), {
34
+ method: input.method,
35
+ signal: controller.signal,
36
+ headers: {
37
+ Authorization: `Bearer ${options.apiKey}`,
38
+ 'Content-Type': 'application/json',
39
+ },
40
+ body: input.body == null ? undefined : JSON.stringify(input.body),
41
+ });
42
+ const body = await parseResponseBody(res);
43
+ if (!res.ok) {
44
+ const envelope = body && typeof body === 'object' ? body : null;
45
+ const message = (envelope && typeof envelope.message === 'string' && envelope.message)
46
+ || (envelope && typeof envelope.error === 'string' && envelope.error)
47
+ || `Request failed (${res.status})`;
48
+ throw new V1SdkError({
49
+ status: res.status,
50
+ message,
51
+ body,
52
+ code: envelope && typeof envelope.code === 'string' ? envelope.code : null,
53
+ requestId: envelope && typeof envelope.requestId === 'string' ? envelope.requestId : null,
54
+ retryable: envelope && typeof envelope.retryable === 'boolean' ? envelope.retryable : null,
55
+ });
56
+ }
57
+ return body;
58
+ }
59
+ finally {
60
+ clearTimeout(timeout);
61
+ }
62
+ }
63
+ const e = encodeURIComponent;
64
+ return {
65
+ // ── Integration (2) ──────────────────────────────────────────────
66
+ listGroupAdvisors(input) {
67
+ return request({
68
+ method: 'GET',
69
+ path: `/v1/integration/groups/${e(input.groupId)}/advisors`,
70
+ query: {
71
+ sort: input.sort,
72
+ order: input.order,
73
+ onlineStatus: input.onlineStatus,
74
+ minBillableMinutes30d: input.minBillableMinutes30d,
75
+ },
76
+ });
77
+ },
78
+ getGroupAdvisor(input) {
79
+ return request({
80
+ method: 'GET',
81
+ path: `/v1/integration/groups/${e(input.groupId)}/advisors/${e(input.advisorId)}`,
82
+ });
83
+ },
84
+ // ── Sessions (13) ────────────────────────────────────────────────
85
+ createSession(input) {
86
+ return request({
87
+ method: 'POST',
88
+ path: '/v1/sessions',
89
+ body: input,
90
+ });
91
+ },
92
+ getSession(input) {
93
+ return request({
94
+ method: 'GET',
95
+ path: `/v1/sessions/${e(input.sessionId)}`,
96
+ });
97
+ },
98
+ getSessionLedger(input) {
99
+ return request({
100
+ method: 'GET',
101
+ path: `/v1/sessions/${e(input.sessionId)}/ledger`,
102
+ });
103
+ },
104
+ sendOtp(input) {
105
+ return request({
106
+ method: 'POST',
107
+ path: `/v1/sessions/${e(input.sessionId)}/otp/send`,
108
+ });
109
+ },
110
+ verifyPhone(input) {
111
+ return request({
112
+ method: 'POST',
113
+ path: `/v1/sessions/${e(input.sessionId)}/verify-phone`,
114
+ body: { otp: input.otp },
115
+ });
116
+ },
117
+ createStripeIntent(input) {
118
+ return request({
119
+ method: 'POST',
120
+ path: `/v1/sessions/${e(input.sessionId)}/payments/create-intent`,
121
+ body: input.holdCents != null ? { holdCents: input.holdCents } : {},
122
+ });
123
+ },
124
+ createPayPalOrder(input) {
125
+ return request({
126
+ method: 'POST',
127
+ path: `/v1/sessions/${e(input.sessionId)}/payments/paypal/create-order`,
128
+ body: input.holdCents != null ? { holdCents: input.holdCents } : {},
129
+ });
130
+ },
131
+ authorizePayPalOrder(input) {
132
+ return request({
133
+ method: 'POST',
134
+ path: `/v1/sessions/${e(input.sessionId)}/payments/paypal/authorize`,
135
+ body: { orderId: input.orderId },
136
+ });
137
+ },
138
+ authorizeHold(input) {
139
+ return request({
140
+ method: 'POST',
141
+ path: `/v1/sessions/${e(input.sessionId)}/authorize-hold`,
142
+ body: {
143
+ paymentIntentId: input.paymentIntentId,
144
+ holdCents: input.holdCents,
145
+ },
146
+ });
147
+ },
148
+ startConnecting(input) {
149
+ return request({
150
+ method: 'POST',
151
+ path: `/v1/sessions/${e(input.sessionId)}/start-connecting`,
152
+ });
153
+ },
154
+ markConnected(input) {
155
+ return request({
156
+ method: 'POST',
157
+ path: `/v1/sessions/${e(input.sessionId)}/mark-connected`,
158
+ });
159
+ },
160
+ tick(input) {
161
+ return request({
162
+ method: 'POST',
163
+ path: `/v1/sessions/${e(input.sessionId)}/tick`,
164
+ body: input.asOfIso ? { asOfIso: input.asOfIso } : {},
165
+ });
166
+ },
167
+ endAndCapture(input) {
168
+ return request({
169
+ method: 'POST',
170
+ path: `/v1/sessions/${e(input.sessionId)}/end-and-capture`,
171
+ body: input.reason ? { reason: input.reason } : {},
172
+ });
173
+ },
174
+ submitSessionReview(input) {
175
+ return request({
176
+ method: 'POST',
177
+ path: `/v1/sessions/${e(input.sessionId)}/reviews`,
178
+ body: {
179
+ rating: input.rating,
180
+ body: input.body,
181
+ authorName: input.authorName,
182
+ },
183
+ });
184
+ },
185
+ // ── Owner / API Keys (4) ─────────────────────────────────────────
186
+ listApiKeys(input) {
187
+ return request({
188
+ method: 'GET',
189
+ path: `/v1/owner/groups/${e(input.groupId)}/api-keys`,
190
+ });
191
+ },
192
+ createApiKey(input) {
193
+ return request({
194
+ method: 'POST',
195
+ path: `/v1/owner/groups/${e(input.groupId)}/api-keys`,
196
+ body: { name: input.name, scopes: input.scopes, expiresAt: input.expiresAt },
197
+ });
198
+ },
199
+ rotateApiKey(input) {
200
+ return request({
201
+ method: 'POST',
202
+ path: `/v1/owner/groups/${e(input.groupId)}/api-keys/${e(input.keyId)}/rotate`,
203
+ body: {
204
+ overlapSeconds: input.overlapSeconds,
205
+ name: input.name,
206
+ expiresAt: input.expiresAt,
207
+ },
208
+ });
209
+ },
210
+ revokeApiKey(input) {
211
+ return request({
212
+ method: 'POST',
213
+ path: `/v1/owner/groups/${e(input.groupId)}/api-keys/${e(input.keyId)}/revoke`,
214
+ });
215
+ },
216
+ // ── Widget (3) + Owner / Widget (2) ──────────────────────────────
217
+ mintWidgetToken(input) {
218
+ return request({
219
+ method: 'POST',
220
+ path: `/v1/widget/${e(input.groupId)}/auth/token`,
221
+ });
222
+ },
223
+ getWidgetConfig(input) {
224
+ return request({
225
+ method: 'GET',
226
+ path: `/v1/widget/${e(input.groupId)}/config`,
227
+ });
228
+ },
229
+ listWidgetAdvisors(input) {
230
+ return request({
231
+ method: 'GET',
232
+ path: `/v1/widget/${e(input.groupId)}/advisors`,
233
+ });
234
+ },
235
+ getWidgetSecurity(input) {
236
+ return request({
237
+ method: 'GET',
238
+ path: `/v1/owner/groups/${e(input.groupId)}/widget/security`,
239
+ });
240
+ },
241
+ updateWidgetSecurity(input) {
242
+ return request({
243
+ method: 'PUT',
244
+ path: `/v1/owner/groups/${e(input.groupId)}/widget/security`,
245
+ body: { allowedOrigins: input.allowedOrigins },
246
+ });
247
+ },
248
+ // ── Owner / Invites (5) ──────────────────────────────────────────
249
+ createInvite(input) {
250
+ return request({
251
+ method: 'POST',
252
+ path: `/v1/owner/groups/${e(input.groupId)}/invites`,
253
+ body: input.email ? { email: input.email } : {},
254
+ });
255
+ },
256
+ listInvites(input) {
257
+ return request({
258
+ method: 'GET',
259
+ path: `/v1/owner/groups/${e(input.groupId)}/invites`,
260
+ query: { status: input.status },
261
+ });
262
+ },
263
+ rotateInvite(input) {
264
+ return request({
265
+ method: 'POST',
266
+ path: `/v1/owner/groups/${e(input.groupId)}/invites/${e(input.inviteId)}/rotate`,
267
+ });
268
+ },
269
+ revokeInvite(input) {
270
+ return request({
271
+ method: 'POST',
272
+ path: `/v1/owner/groups/${e(input.groupId)}/invites/${e(input.inviteId)}/revoke`,
273
+ });
274
+ },
275
+ sendInvite(input) {
276
+ return request({
277
+ method: 'POST',
278
+ path: `/v1/owner/groups/${e(input.groupId)}/invites/${e(input.inviteId)}/send`,
279
+ });
280
+ },
281
+ // ── Owner / Groups (4) ───────────────────────────────────────────
282
+ getGroupBySlug(input) {
283
+ return request({
284
+ method: 'GET',
285
+ path: `/v1/groups/by-slug/${e(input.slug)}`,
286
+ });
287
+ },
288
+ updateGroupSlug(input) {
289
+ return request({
290
+ method: 'PATCH',
291
+ path: `/v1/owner/groups/${e(input.groupId)}/slug`,
292
+ body: { slug: input.slug },
293
+ });
294
+ },
295
+ getGroupSettings(input) {
296
+ return request({
297
+ method: 'GET',
298
+ path: `/v1/owner/groups/${e(input.groupId)}/settings`,
299
+ });
300
+ },
301
+ updateGroupSettings(input) {
302
+ return request({
303
+ method: 'PATCH',
304
+ path: `/v1/owner/groups/${e(input.groupId)}/settings`,
305
+ body: {
306
+ requireCallerOtp: input.requireCallerOtp,
307
+ },
308
+ });
309
+ },
310
+ // ── Owner / Advisors (5) ────────────────────────────────────────
311
+ listOwnerAdvisors(input) {
312
+ return request({
313
+ method: 'GET',
314
+ path: `/v1/owner/groups/${e(input.groupId)}/advisors`,
315
+ });
316
+ },
317
+ moderateAdvisor(input) {
318
+ return request({
319
+ method: 'POST',
320
+ path: `/v1/owner/groups/${e(input.groupId)}/advisors/${e(input.advisorId)}/moderate`,
321
+ body: { action: input.action, reason: input.reason, note: input.note },
322
+ });
323
+ },
324
+ listAdvisorModerationEvents(input) {
325
+ return request({
326
+ method: 'GET',
327
+ path: `/v1/owner/groups/${e(input.groupId)}/advisors/${e(input.advisorId)}/events`,
328
+ query: { before: input.before, limit: input.limit },
329
+ });
330
+ },
331
+ listGroupModerationEvents(input) {
332
+ return request({
333
+ method: 'GET',
334
+ path: `/v1/owner/groups/${e(input.groupId)}/events`,
335
+ query: { before: input.before, limit: input.limit },
336
+ });
337
+ },
338
+ removeAdvisor(input) {
339
+ return request({
340
+ method: 'POST',
341
+ path: `/v1/owner/groups/${e(input.groupId)}/advisors/${e(input.advisorId)}`,
342
+ body: input.reason ? { reason: input.reason } : {},
343
+ });
344
+ },
345
+ // ── Owner / Setup & Theme (3) ───────────────────────────────────
346
+ updateGroupTheme(input) {
347
+ return request({
348
+ method: 'PATCH',
349
+ path: `/v1/owner/groups/${e(input.groupId)}/theme`,
350
+ body: { theme: input.theme },
351
+ });
352
+ },
353
+ getSetupState(input) {
354
+ return request({
355
+ method: 'GET',
356
+ path: `/v1/owner/groups/${e(input.groupId)}/setup-state`,
357
+ });
358
+ },
359
+ updateSetupState(input) {
360
+ return request({
361
+ method: 'PATCH',
362
+ path: `/v1/owner/groups/${e(input.groupId)}/setup-state`,
363
+ body: { setupState: input.setupState },
364
+ });
365
+ },
366
+ // ── Owner / Analytics (4) ────────────────────────────────────────
367
+ getAnalyticsOverview(input) {
368
+ return request({
369
+ method: 'GET',
370
+ path: `/v1/owner/groups/${e(input.groupId)}/analytics/overview`,
371
+ });
372
+ },
373
+ getAnalyticsRevenue(input) {
374
+ return request({
375
+ method: 'GET',
376
+ path: `/v1/owner/groups/${e(input.groupId)}/analytics/revenue`,
377
+ });
378
+ },
379
+ getAnalyticsSessions(input) {
380
+ return request({
381
+ method: 'GET',
382
+ path: `/v1/owner/groups/${e(input.groupId)}/analytics/sessions`,
383
+ query: { page: input.page, limit: input.limit },
384
+ });
385
+ },
386
+ getWidgetEventAnalytics(input) {
387
+ return request({
388
+ method: 'GET',
389
+ path: `/v1/owner/groups/${e(input.groupId)}/analytics/widget-events`,
390
+ query: { since: input.since, limit: input.limit },
391
+ });
392
+ },
393
+ // ── Owner / Payouts (1) ──────────────────────────────────────────
394
+ listOwnerPayouts(input) {
395
+ return request({
396
+ method: 'GET',
397
+ path: `/v1/owner/groups/${e(input.groupId)}/payouts`,
398
+ query: { page: input.page, limit: input.limit },
399
+ });
400
+ },
401
+ // ── Owner / Invites (Batch + Public invite flow) ────────────────
402
+ batchCreateOwnerInvites(input) {
403
+ return request({
404
+ method: 'POST',
405
+ path: `/v1/owner/groups/${e(input.groupId)}/invites/batch`,
406
+ body: { emails: input.emails },
407
+ });
408
+ },
409
+ previewInvite(input) {
410
+ return request({
411
+ method: 'GET',
412
+ path: `/v1/invites/${e(input.token)}/preview`,
413
+ });
414
+ },
415
+ acceptInvite(input) {
416
+ return request({
417
+ method: 'POST',
418
+ path: `/v1/invites/${e(input.token)}/accept`,
419
+ });
420
+ },
421
+ // ── Widget events + callbacks (3) ────────────────────────────────
422
+ createWidgetCallback(input) {
423
+ return request({
424
+ method: 'POST',
425
+ path: `/v1/widget/${e(input.groupId)}/callbacks`,
426
+ body: input.body,
427
+ });
428
+ },
429
+ getWidgetCallbackStatus(input) {
430
+ return request({
431
+ method: 'GET',
432
+ path: `/v1/widget/${e(input.groupId)}/callbacks/status`,
433
+ query: { phone: input.phone },
434
+ });
435
+ },
436
+ ingestWidgetEvent(input) {
437
+ return request({
438
+ method: 'POST',
439
+ path: `/v1/widget/${e(input.groupId)}/events`,
440
+ body: { eventType: input.eventType, sessionId: input.sessionId, eventData: input.eventData },
441
+ });
442
+ },
443
+ // ── SEO + taxonomy + widget generation (4) ───────────────────────
444
+ getGroupSitemap(input) {
445
+ return request({
446
+ method: 'GET',
447
+ path: `/v1/seo/groups/${e(input.groupSlug)}/sitemap.xml`,
448
+ });
449
+ },
450
+ getAdvisorJsonLd(input) {
451
+ return request({
452
+ method: 'GET',
453
+ path: `/v1/seo/groups/${e(input.groupSlug)}/advisors/${e(input.advisorId)}/jsonld`,
454
+ });
455
+ },
456
+ getTaxonomy() {
457
+ return request({
458
+ method: 'GET',
459
+ path: '/v1/taxonomy',
460
+ });
461
+ },
462
+ generateWidgetEmbedCode(input) {
463
+ return request({
464
+ method: 'POST',
465
+ path: `/v1/owner/groups/${e(input.groupId)}/widget/generate`,
466
+ });
467
+ },
468
+ };
469
+ }
@@ -0,0 +1,15 @@
1
+ export declare class V1SdkError extends Error {
2
+ status: number;
3
+ body: unknown;
4
+ code: string | null;
5
+ requestId: string | null;
6
+ retryable: boolean | null;
7
+ constructor(input: {
8
+ status: number;
9
+ message: string;
10
+ body: unknown;
11
+ code?: string | null;
12
+ requestId?: string | null;
13
+ retryable?: boolean | null;
14
+ });
15
+ }
package/dist/errors.js ADDED
@@ -0,0 +1,16 @@
1
+ export class V1SdkError extends Error {
2
+ status;
3
+ body;
4
+ code;
5
+ requestId;
6
+ retryable;
7
+ constructor(input) {
8
+ super(input.message);
9
+ this.name = 'V1SdkError';
10
+ this.status = input.status;
11
+ this.body = input.body;
12
+ this.code = input.code ?? null;
13
+ this.requestId = input.requestId ?? null;
14
+ this.retryable = input.retryable ?? null;
15
+ }
16
+ }
@@ -0,0 +1,7 @@
1
+ export { V1SdkError } from './errors.js';
2
+ export { createV1Client } from './client.js';
3
+ export type { V1ClientOptions } from './client.js';
4
+ export { withRetry } from './retry.js';
5
+ export type { RetryOptions } from './retry.js';
6
+ export type { ApiKeyRecord, InviteRecord, GroupSettingsResponse, WidgetConfigResponse, WidgetAdvisor, WidgetSecurityConfig, IntegrationAdvisor, IntegrationAdvisorFeedResponse, SessionEnvelope, ApiSession, LedgerEvent, ErrorResponse, } from './client.js';
7
+ export type { paths, components, operations } from './generated/v1.js';
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { V1SdkError } from './errors.js';
2
+ export { createV1Client } from './client.js';
3
+ export { withRetry } from './retry.js';
@@ -0,0 +1,23 @@
1
+ export type RetryOptions = {
2
+ /** Maximum number of attempts (including the first). Default: 3 */
3
+ maxAttempts?: number;
4
+ /** Base backoff delay in milliseconds. Default: 1000 */
5
+ backoffMs?: number;
6
+ /** Maximum backoff delay in milliseconds. Default: 30000 */
7
+ maxBackoffMs?: number;
8
+ };
9
+ /**
10
+ * Retry an async function with exponential backoff and jitter.
11
+ *
12
+ * Only retries on `V1SdkError` where `retryable === true`.
13
+ * All other errors are thrown immediately.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * const session = await withRetry(
18
+ * () => client.getSession({ sessionId: 'sess_abc' }),
19
+ * { maxAttempts: 5, backoffMs: 500 },
20
+ * );
21
+ * ```
22
+ */
23
+ export declare function withRetry<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>;
package/dist/retry.js ADDED
@@ -0,0 +1,39 @@
1
+ import { V1SdkError } from './errors.js';
2
+ /**
3
+ * Retry an async function with exponential backoff and jitter.
4
+ *
5
+ * Only retries on `V1SdkError` where `retryable === true`.
6
+ * All other errors are thrown immediately.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * const session = await withRetry(
11
+ * () => client.getSession({ sessionId: 'sess_abc' }),
12
+ * { maxAttempts: 5, backoffMs: 500 },
13
+ * );
14
+ * ```
15
+ */
16
+ export async function withRetry(fn, options) {
17
+ const maxAttempts = options?.maxAttempts ?? 3;
18
+ const backoffMs = options?.backoffMs ?? 1000;
19
+ const maxBackoffMs = options?.maxBackoffMs ?? 30_000;
20
+ let lastError;
21
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
22
+ try {
23
+ return await fn();
24
+ }
25
+ catch (err) {
26
+ lastError = err;
27
+ const isRetryable = err instanceof V1SdkError && err.retryable === true;
28
+ if (!isRetryable || attempt === maxAttempts) {
29
+ throw err;
30
+ }
31
+ // Exponential backoff with jitter: delay = min(base * 2^(attempt-1) + jitter, max)
32
+ const exponentialDelay = backoffMs * Math.pow(2, attempt - 1);
33
+ const jitter = Math.random() * backoffMs;
34
+ const delay = Math.min(exponentialDelay + jitter, maxBackoffMs);
35
+ await new Promise((resolve) => setTimeout(resolve, delay));
36
+ }
37
+ }
38
+ throw lastError;
39
+ }
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@advizorconnect/sdk",
3
+ "version": "0.1.0",
4
+ "description": "Typed SDK for Advizor Connect v1 integration APIs",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "default": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md",
18
+ "CHANGELOG.md"
19
+ ],
20
+ "sideEffects": false,
21
+ "scripts": {
22
+ "build": "tsc -p tsconfig.json"
23
+ },
24
+ "publishConfig": {
25
+ "access": "public"
26
+ }
27
+ }