@teamvortexsoftware/vortex-node-22-sdk 0.1.3 โ†’ 0.3.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/README.md CHANGED
@@ -4,6 +4,21 @@ This package provides the Vortex Node/Typescript SDK.
4
4
 
5
5
  With this module, you can both generate a JWT for use with the Vortex Widget and make API calls to the Vortex API.
6
6
 
7
+ ## Features
8
+
9
+ ### Invitation Delivery Types
10
+
11
+ Vortex supports multiple delivery methods for invitations:
12
+
13
+ - **`email`** - Email invitations sent by Vortex (includes reminders and nudges)
14
+ - **`phone`** - Phone invitations sent by the user/customer
15
+ - **`share`** - Shareable invitation links for social sharing
16
+ - **`internal`** - Internal invitations managed entirely by your application
17
+ - No email/SMS communication triggered by Vortex
18
+ - Target value can be any customer-defined identifier (UUID, string, number)
19
+ - Useful for in-app invitation flows where you handle the delivery
20
+ - Example use case: In-app notifications, dashboard invites, etc.
21
+
7
22
  ## Use Cases and Examples
8
23
 
9
24
  ### Install the NodeJS SDK
@@ -45,7 +60,9 @@ app.get('/vortex-jwt', (req, res) => {
45
60
  user: {
46
61
  id: 'user-123',
47
62
  email: 'user@example.com',
48
- adminScopes: ['autojoin'], // Optional: grants admin privileges for autojoining
63
+ name: 'Jane Doe', // Optional: user's display name
64
+ avatarUrl: 'https://example.com/avatars/jane.jpg', // Optional: user's avatar URL
65
+ adminScopes: ['autojoin'], // Optional: grants admin privileges for autojoining
49
66
  },
50
67
  });
51
68
 
@@ -57,6 +74,30 @@ app.listen(port, () => {
57
74
  });
58
75
  ```
59
76
 
77
+ #### User Profile Information (Optional)
78
+
79
+ You can optionally include the user's name and avatar URL in JWTs. This information will be stored and returned when fetching invitations created by this user.
80
+
81
+ ```ts
82
+ const token = vortex.generateJwt({
83
+ user: {
84
+ id: 'user-123',
85
+ email: 'user@example.com',
86
+ name: 'Jane Doe', // Optional
87
+ avatarUrl: 'https://example.com/avatars/jane.jpg', // Optional
88
+ },
89
+ });
90
+ ```
91
+
92
+ **Requirements:**
93
+ - `name`: Optional string (max 200 characters)
94
+ - `avatarUrl`: Optional HTTPS URL (max 2000 characters)
95
+ - Both fields are optional and can be omitted
96
+
97
+ **Validation:**
98
+ - Invalid or non-HTTPS avatar URLs will be ignored with a warning
99
+ - Authentication will succeed even with invalid avatar URLs
100
+
60
101
  You can also add extra properties to the JWT payload:
61
102
 
62
103
  ```ts
@@ -87,7 +128,7 @@ const userId = 'users-id-in-my-system';
87
128
  const identifiers = [
88
129
  { type: 'email', value: 'users@emailaddress.com' },
89
130
  { type: 'email', value: 'someother@address.com' },
90
- { type: 'sms', value: '18008675309' },
131
+ { type: 'phone', value: '18008675309' },
91
132
  ];
92
133
 
93
134
  // groups are specific to your product. This list should be the groups that the current requesting user is a part of. It is up to you to define them if you so choose. Based on the values here, we can determine whether or not the user is allowed to invite others to a particular group
@@ -219,7 +260,7 @@ app.get('/invite/landing', async (req, res) => {
219
260
 
220
261
  ### View invitations by target (email address for example)
221
262
 
222
- Depending on your use case, you may want to accept all outstanding invitations to a given user when they sign up for your service. If you don't want to auto accept, you may want to present the new user with a list of all invitations that target them. Either way, the example below shows how you fetch these invitations once you know how to identify (via email, sms or others in the future) a new user to your product.
263
+ Depending on your use case, you may want to accept all outstanding invitations to a given user when they sign up for your service. If you don't want to auto accept, you may want to present the new user with a list of all invitations that target them. Either way, the example below shows how you fetch these invitations once you know how to identify (via email, phone or others in the future) a new user to your product.
223
264
 
224
265
  ```ts
225
266
  app.get('/invitations/by-email', async (req, res) => {
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  type InvitationTarget = {
2
- type: 'email' | 'sms';
2
+ type: 'email' | 'phone';
3
3
  value: string;
4
4
  };
5
5
  /**
@@ -40,7 +40,7 @@ type InvitationResult = {
40
40
  createdAt: string;
41
41
  deactivated: boolean;
42
42
  deliveryCount: number;
43
- deliveryTypes: ('email' | 'sms' | 'share')[];
43
+ deliveryTypes: ('email' | 'phone' | 'share' | 'internal')[];
44
44
  foreignCreatorId: string;
45
45
  invitationType: 'single_use' | 'multi_use';
46
46
  modifiedAt: string | null;
@@ -53,21 +53,40 @@ type InvitationResult = {
53
53
  accepts: InvitationAcceptance[];
54
54
  expired: boolean;
55
55
  expires?: string;
56
+ source?: string;
57
+ creatorName?: string | null;
58
+ creatorAvatarUrl?: string | null;
59
+ };
60
+ /**
61
+ * User type for accepting invitations
62
+ * Requires either email or phone (or both)
63
+ */
64
+ type AcceptUser = {
65
+ email?: string;
66
+ phone?: string;
67
+ name?: string;
56
68
  };
57
69
  type AcceptInvitationRequest = {
70
+ invitationIds: string[];
71
+ user: AcceptUser;
72
+ };
73
+ type AcceptInvitationRequestLegacy = {
58
74
  invitationIds: string[];
59
75
  target: InvitationTarget;
60
76
  };
61
77
  type ApiResponseJson = InvitationResult | {
62
78
  invitations: InvitationResult[];
63
79
  } | {};
64
- type ApiRequestBody = AcceptInvitationRequest | null;
80
+ type ApiRequestBody = AcceptInvitationRequest | AcceptInvitationRequestLegacy | null;
65
81
  /**
66
82
  * User type for JWT generation
83
+ * Requires both id and email
67
84
  */
68
85
  type User = {
69
86
  id: string;
70
87
  email: string;
88
+ name?: string;
89
+ avatarUrl?: string;
71
90
  adminScopes?: string[];
72
91
  [key: string]: any;
73
92
  };
@@ -96,6 +115,73 @@ type ConfigureAutojoinRequest = {
96
115
  widgetId: string;
97
116
  metadata?: Record<string, any>;
98
117
  };
118
+ /**
119
+ * Target types for creating invitations
120
+ */
121
+ type CreateInvitationTargetType = 'email' | 'phone' | 'internal';
122
+ /**
123
+ * Target for creating an invitation
124
+ */
125
+ type CreateInvitationTarget = {
126
+ type: CreateInvitationTargetType;
127
+ value: string;
128
+ };
129
+ /**
130
+ * Information about the user creating the invitation (the inviter)
131
+ */
132
+ type Inviter = {
133
+ /** The internal user ID of the person creating the invitation (from your system) */
134
+ userId: string;
135
+ /** The email address of the person creating the invitation */
136
+ userEmail?: string;
137
+ /** The display name of the person creating the invitation */
138
+ name?: string;
139
+ /** Avatar URL for the person creating the invitation */
140
+ avatarUrl?: string;
141
+ };
142
+ /**
143
+ * Group information for creating invitations
144
+ */
145
+ type CreateInvitationGroup = {
146
+ /** The type of the group/scope (e.g., "team", "organization", "project") */
147
+ type: string;
148
+ /** The ID of the group/scope in your system */
149
+ groupId: string;
150
+ /** The display name of the group/scope */
151
+ name: string;
152
+ };
153
+ /**
154
+ * Request body for creating an invitation via the public API
155
+ */
156
+ type CreateInvitationRequest = {
157
+ /** The ID of the widget configuration to use for this invitation */
158
+ widgetConfigurationId: string;
159
+ /** The target of the invitation (who is being invited) */
160
+ target: CreateInvitationTarget;
161
+ /** Information about the user creating the invitation */
162
+ inviter: Inviter;
163
+ /** Groups/scopes to associate with this invitation */
164
+ groups?: CreateInvitationGroup[];
165
+ /** The source of the invitation for analytics (e.g., "api", "backend", "pymk") */
166
+ source?: string;
167
+ /** Template variables for email customization */
168
+ templateVariables?: Record<string, string>;
169
+ /** Custom metadata to attach to the invitation (passed through to webhooks) */
170
+ metadata?: Record<string, any>;
171
+ };
172
+ /**
173
+ * Response from creating an invitation
174
+ */
175
+ type CreateInvitationResponse = {
176
+ /** The ID of the created invitation */
177
+ id: string;
178
+ /** The short link for the invitation */
179
+ shortLink: string;
180
+ /** The status of the invitation */
181
+ status: string;
182
+ /** When the invitation was created */
183
+ createdAt: string;
184
+ };
99
185
 
100
186
  declare class Vortex {
101
187
  private apiKey;
@@ -131,10 +217,34 @@ declare class Vortex {
131
217
  getInvitationsByTarget(targetType: 'email' | 'username' | 'phoneNumber', targetValue: string): Promise<InvitationResult[]>;
132
218
  getInvitation(invitationId: string): Promise<InvitationResult>;
133
219
  revokeInvitation(invitationId: string): Promise<{}>;
134
- acceptInvitations(invitationIds: string[], target: {
135
- type: 'email' | 'username' | 'phoneNumber';
136
- value: string;
137
- }): Promise<InvitationResult>;
220
+ /**
221
+ * Accept invitations using the new User format (preferred)
222
+ * @param invitationIds - Array of invitation IDs to accept
223
+ * @param user - User object with email or phone (and optional name)
224
+ * @returns Invitation result
225
+ * @example
226
+ * ```typescript
227
+ * await vortex.acceptInvitations(['inv-123'], { email: 'user@example.com', name: 'John' });
228
+ * ```
229
+ */
230
+ acceptInvitations(invitationIds: string[], user: AcceptUser): Promise<InvitationResult>;
231
+ /**
232
+ * Accept invitations using legacy target format (deprecated)
233
+ * @deprecated Use the User format instead: acceptInvitations(invitationIds, { email: 'user@example.com' })
234
+ * @param invitationIds - Array of invitation IDs to accept
235
+ * @param target - Legacy target object with type and value
236
+ * @returns Invitation result
237
+ */
238
+ acceptInvitations(invitationIds: string[], target: InvitationTarget): Promise<InvitationResult>;
239
+ /**
240
+ * Accept invitations using multiple legacy targets (deprecated)
241
+ * Will call the accept endpoint once per target
242
+ * @deprecated Use the User format instead: acceptInvitations(invitationIds, { email: 'user@example.com' })
243
+ * @param invitationIds - Array of invitation IDs to accept
244
+ * @param targets - Array of legacy target objects
245
+ * @returns Invitation result from the last acceptance
246
+ */
247
+ acceptInvitations(invitationIds: string[], targets: InvitationTarget[]): Promise<InvitationResult>;
138
248
  deleteInvitationsByGroup(groupType: string, groupId: string): Promise<{}>;
139
249
  getInvitationsByGroup(groupType: string, groupId: string): Promise<InvitationResult[]>;
140
250
  reinvite(invitationId: string): Promise<InvitationResult>;
@@ -179,6 +289,48 @@ declare class Vortex {
179
289
  * ```
180
290
  */
181
291
  configureAutojoin(params: ConfigureAutojoinRequest): Promise<AutojoinDomainsResponse>;
292
+ /**
293
+ * Create an invitation from your backend
294
+ *
295
+ * This method allows you to create invitations programmatically using your API key,
296
+ * without requiring a user JWT token. This is useful for server-side invitation
297
+ * creation, such as "People You May Know" flows or admin-initiated invitations.
298
+ *
299
+ * @param params - Invitation parameters
300
+ * @param params.widgetConfigurationId - The widget configuration ID to use
301
+ * @param params.target - The target of the invitation (who is being invited)
302
+ * @param params.target.type - 'email', 'phone', or 'internal'
303
+ * @param params.target.value - Email address, phone number, or internal user ID
304
+ * @param params.inviter - Information about the user creating the invitation
305
+ * @param params.inviter.userId - Your internal user ID for the inviter
306
+ * @param params.inviter.userEmail - Optional email of the inviter
307
+ * @param params.inviter.name - Optional display name of the inviter
308
+ * @param params.groups - Optional groups/scopes to associate with the invitation
309
+ * @param params.source - Optional source for analytics (defaults to 'api')
310
+ * @param params.templateVariables - Optional template variables for email customization
311
+ * @param params.metadata - Optional metadata passed through to webhooks
312
+ * @returns Created invitation with ID, short link, status, and creation timestamp
313
+ *
314
+ * @example
315
+ * ```typescript
316
+ * // Create an email invitation
317
+ * const invitation = await vortex.createInvitation({
318
+ * widgetConfigurationId: 'widget-config-123',
319
+ * target: { type: 'email', value: 'invitee@example.com' },
320
+ * inviter: { userId: 'user-456', userEmail: 'inviter@example.com', name: 'John Doe' },
321
+ * groups: [{ type: 'team', groupId: 'team-789', name: 'Engineering' }],
322
+ * });
323
+ *
324
+ * // Create an internal invitation (PYMK flow - no email sent)
325
+ * const pymkInvitation = await vortex.createInvitation({
326
+ * widgetConfigurationId: 'widget-config-123',
327
+ * target: { type: 'internal', value: 'internal-user-id-abc' },
328
+ * inviter: { userId: 'user-456' },
329
+ * source: 'pymk',
330
+ * });
331
+ * ```
332
+ */
333
+ createInvitation(params: CreateInvitationRequest): Promise<CreateInvitationResponse>;
182
334
  }
183
335
 
184
- export { type AcceptInvitationRequest, type ApiRequestBody, type ApiResponseJson, type AutojoinDomain, type AutojoinDomainsResponse, type ConfigureAutojoinRequest, type GroupInput, type InvitationAcceptance, type InvitationGroup, type InvitationResult, type InvitationTarget, type User, Vortex };
336
+ export { type AcceptInvitationRequest, type AcceptInvitationRequestLegacy, type AcceptUser, type ApiRequestBody, type ApiResponseJson, type AutojoinDomain, type AutojoinDomainsResponse, type ConfigureAutojoinRequest, type CreateInvitationGroup, type CreateInvitationRequest, type CreateInvitationResponse, type CreateInvitationTarget, type CreateInvitationTargetType, type GroupInput, type InvitationAcceptance, type InvitationGroup, type InvitationResult, type InvitationTarget, type Inviter, type User, Vortex };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  type InvitationTarget = {
2
- type: 'email' | 'sms';
2
+ type: 'email' | 'phone';
3
3
  value: string;
4
4
  };
5
5
  /**
@@ -40,7 +40,7 @@ type InvitationResult = {
40
40
  createdAt: string;
41
41
  deactivated: boolean;
42
42
  deliveryCount: number;
43
- deliveryTypes: ('email' | 'sms' | 'share')[];
43
+ deliveryTypes: ('email' | 'phone' | 'share' | 'internal')[];
44
44
  foreignCreatorId: string;
45
45
  invitationType: 'single_use' | 'multi_use';
46
46
  modifiedAt: string | null;
@@ -53,21 +53,40 @@ type InvitationResult = {
53
53
  accepts: InvitationAcceptance[];
54
54
  expired: boolean;
55
55
  expires?: string;
56
+ source?: string;
57
+ creatorName?: string | null;
58
+ creatorAvatarUrl?: string | null;
59
+ };
60
+ /**
61
+ * User type for accepting invitations
62
+ * Requires either email or phone (or both)
63
+ */
64
+ type AcceptUser = {
65
+ email?: string;
66
+ phone?: string;
67
+ name?: string;
56
68
  };
57
69
  type AcceptInvitationRequest = {
70
+ invitationIds: string[];
71
+ user: AcceptUser;
72
+ };
73
+ type AcceptInvitationRequestLegacy = {
58
74
  invitationIds: string[];
59
75
  target: InvitationTarget;
60
76
  };
61
77
  type ApiResponseJson = InvitationResult | {
62
78
  invitations: InvitationResult[];
63
79
  } | {};
64
- type ApiRequestBody = AcceptInvitationRequest | null;
80
+ type ApiRequestBody = AcceptInvitationRequest | AcceptInvitationRequestLegacy | null;
65
81
  /**
66
82
  * User type for JWT generation
83
+ * Requires both id and email
67
84
  */
68
85
  type User = {
69
86
  id: string;
70
87
  email: string;
88
+ name?: string;
89
+ avatarUrl?: string;
71
90
  adminScopes?: string[];
72
91
  [key: string]: any;
73
92
  };
@@ -96,6 +115,73 @@ type ConfigureAutojoinRequest = {
96
115
  widgetId: string;
97
116
  metadata?: Record<string, any>;
98
117
  };
118
+ /**
119
+ * Target types for creating invitations
120
+ */
121
+ type CreateInvitationTargetType = 'email' | 'phone' | 'internal';
122
+ /**
123
+ * Target for creating an invitation
124
+ */
125
+ type CreateInvitationTarget = {
126
+ type: CreateInvitationTargetType;
127
+ value: string;
128
+ };
129
+ /**
130
+ * Information about the user creating the invitation (the inviter)
131
+ */
132
+ type Inviter = {
133
+ /** The internal user ID of the person creating the invitation (from your system) */
134
+ userId: string;
135
+ /** The email address of the person creating the invitation */
136
+ userEmail?: string;
137
+ /** The display name of the person creating the invitation */
138
+ name?: string;
139
+ /** Avatar URL for the person creating the invitation */
140
+ avatarUrl?: string;
141
+ };
142
+ /**
143
+ * Group information for creating invitations
144
+ */
145
+ type CreateInvitationGroup = {
146
+ /** The type of the group/scope (e.g., "team", "organization", "project") */
147
+ type: string;
148
+ /** The ID of the group/scope in your system */
149
+ groupId: string;
150
+ /** The display name of the group/scope */
151
+ name: string;
152
+ };
153
+ /**
154
+ * Request body for creating an invitation via the public API
155
+ */
156
+ type CreateInvitationRequest = {
157
+ /** The ID of the widget configuration to use for this invitation */
158
+ widgetConfigurationId: string;
159
+ /** The target of the invitation (who is being invited) */
160
+ target: CreateInvitationTarget;
161
+ /** Information about the user creating the invitation */
162
+ inviter: Inviter;
163
+ /** Groups/scopes to associate with this invitation */
164
+ groups?: CreateInvitationGroup[];
165
+ /** The source of the invitation for analytics (e.g., "api", "backend", "pymk") */
166
+ source?: string;
167
+ /** Template variables for email customization */
168
+ templateVariables?: Record<string, string>;
169
+ /** Custom metadata to attach to the invitation (passed through to webhooks) */
170
+ metadata?: Record<string, any>;
171
+ };
172
+ /**
173
+ * Response from creating an invitation
174
+ */
175
+ type CreateInvitationResponse = {
176
+ /** The ID of the created invitation */
177
+ id: string;
178
+ /** The short link for the invitation */
179
+ shortLink: string;
180
+ /** The status of the invitation */
181
+ status: string;
182
+ /** When the invitation was created */
183
+ createdAt: string;
184
+ };
99
185
 
100
186
  declare class Vortex {
101
187
  private apiKey;
@@ -131,10 +217,34 @@ declare class Vortex {
131
217
  getInvitationsByTarget(targetType: 'email' | 'username' | 'phoneNumber', targetValue: string): Promise<InvitationResult[]>;
132
218
  getInvitation(invitationId: string): Promise<InvitationResult>;
133
219
  revokeInvitation(invitationId: string): Promise<{}>;
134
- acceptInvitations(invitationIds: string[], target: {
135
- type: 'email' | 'username' | 'phoneNumber';
136
- value: string;
137
- }): Promise<InvitationResult>;
220
+ /**
221
+ * Accept invitations using the new User format (preferred)
222
+ * @param invitationIds - Array of invitation IDs to accept
223
+ * @param user - User object with email or phone (and optional name)
224
+ * @returns Invitation result
225
+ * @example
226
+ * ```typescript
227
+ * await vortex.acceptInvitations(['inv-123'], { email: 'user@example.com', name: 'John' });
228
+ * ```
229
+ */
230
+ acceptInvitations(invitationIds: string[], user: AcceptUser): Promise<InvitationResult>;
231
+ /**
232
+ * Accept invitations using legacy target format (deprecated)
233
+ * @deprecated Use the User format instead: acceptInvitations(invitationIds, { email: 'user@example.com' })
234
+ * @param invitationIds - Array of invitation IDs to accept
235
+ * @param target - Legacy target object with type and value
236
+ * @returns Invitation result
237
+ */
238
+ acceptInvitations(invitationIds: string[], target: InvitationTarget): Promise<InvitationResult>;
239
+ /**
240
+ * Accept invitations using multiple legacy targets (deprecated)
241
+ * Will call the accept endpoint once per target
242
+ * @deprecated Use the User format instead: acceptInvitations(invitationIds, { email: 'user@example.com' })
243
+ * @param invitationIds - Array of invitation IDs to accept
244
+ * @param targets - Array of legacy target objects
245
+ * @returns Invitation result from the last acceptance
246
+ */
247
+ acceptInvitations(invitationIds: string[], targets: InvitationTarget[]): Promise<InvitationResult>;
138
248
  deleteInvitationsByGroup(groupType: string, groupId: string): Promise<{}>;
139
249
  getInvitationsByGroup(groupType: string, groupId: string): Promise<InvitationResult[]>;
140
250
  reinvite(invitationId: string): Promise<InvitationResult>;
@@ -179,6 +289,48 @@ declare class Vortex {
179
289
  * ```
180
290
  */
181
291
  configureAutojoin(params: ConfigureAutojoinRequest): Promise<AutojoinDomainsResponse>;
292
+ /**
293
+ * Create an invitation from your backend
294
+ *
295
+ * This method allows you to create invitations programmatically using your API key,
296
+ * without requiring a user JWT token. This is useful for server-side invitation
297
+ * creation, such as "People You May Know" flows or admin-initiated invitations.
298
+ *
299
+ * @param params - Invitation parameters
300
+ * @param params.widgetConfigurationId - The widget configuration ID to use
301
+ * @param params.target - The target of the invitation (who is being invited)
302
+ * @param params.target.type - 'email', 'phone', or 'internal'
303
+ * @param params.target.value - Email address, phone number, or internal user ID
304
+ * @param params.inviter - Information about the user creating the invitation
305
+ * @param params.inviter.userId - Your internal user ID for the inviter
306
+ * @param params.inviter.userEmail - Optional email of the inviter
307
+ * @param params.inviter.name - Optional display name of the inviter
308
+ * @param params.groups - Optional groups/scopes to associate with the invitation
309
+ * @param params.source - Optional source for analytics (defaults to 'api')
310
+ * @param params.templateVariables - Optional template variables for email customization
311
+ * @param params.metadata - Optional metadata passed through to webhooks
312
+ * @returns Created invitation with ID, short link, status, and creation timestamp
313
+ *
314
+ * @example
315
+ * ```typescript
316
+ * // Create an email invitation
317
+ * const invitation = await vortex.createInvitation({
318
+ * widgetConfigurationId: 'widget-config-123',
319
+ * target: { type: 'email', value: 'invitee@example.com' },
320
+ * inviter: { userId: 'user-456', userEmail: 'inviter@example.com', name: 'John Doe' },
321
+ * groups: [{ type: 'team', groupId: 'team-789', name: 'Engineering' }],
322
+ * });
323
+ *
324
+ * // Create an internal invitation (PYMK flow - no email sent)
325
+ * const pymkInvitation = await vortex.createInvitation({
326
+ * widgetConfigurationId: 'widget-config-123',
327
+ * target: { type: 'internal', value: 'internal-user-id-abc' },
328
+ * inviter: { userId: 'user-456' },
329
+ * source: 'pymk',
330
+ * });
331
+ * ```
332
+ */
333
+ createInvitation(params: CreateInvitationRequest): Promise<CreateInvitationResponse>;
182
334
  }
183
335
 
184
- export { type AcceptInvitationRequest, type ApiRequestBody, type ApiResponseJson, type AutojoinDomain, type AutojoinDomainsResponse, type ConfigureAutojoinRequest, type GroupInput, type InvitationAcceptance, type InvitationGroup, type InvitationResult, type InvitationTarget, type User, Vortex };
336
+ export { type AcceptInvitationRequest, type AcceptInvitationRequestLegacy, type AcceptUser, type ApiRequestBody, type ApiResponseJson, type AutojoinDomain, type AutojoinDomainsResponse, type ConfigureAutojoinRequest, type CreateInvitationGroup, type CreateInvitationRequest, type CreateInvitationResponse, type CreateInvitationTarget, type CreateInvitationTargetType, type GroupInput, type InvitationAcceptance, type InvitationGroup, type InvitationResult, type InvitationTarget, type Inviter, type User, Vortex };
package/dist/index.js CHANGED
@@ -84,6 +84,12 @@ var Vortex = class {
84
84
  // Include identifiers array for widget compatibility (VrtxAutojoin checks this)
85
85
  identifiers: user.email ? [{ type: "email", value: user.email }] : []
86
86
  };
87
+ if (user.name) {
88
+ payload.name = user.name;
89
+ }
90
+ if (user.avatarUrl) {
91
+ payload.avatarUrl = user.avatarUrl;
92
+ }
87
93
  if (user.adminScopes) {
88
94
  payload.adminScopes = user.adminScopes;
89
95
  if (user.adminScopes.includes("autojoin")) {
@@ -165,12 +171,54 @@ var Vortex = class {
165
171
  path: `/api/v1/invitations/${invitationId}`
166
172
  });
167
173
  }
168
- async acceptInvitations(invitationIds, target) {
174
+ // Implementation
175
+ async acceptInvitations(invitationIds, userOrTarget) {
176
+ if (Array.isArray(userOrTarget)) {
177
+ console.warn(
178
+ '[Vortex SDK] DEPRECATED: Passing an array of targets is deprecated. Use the User format instead: acceptInvitations(invitationIds, { email: "user@example.com" })'
179
+ );
180
+ let lastResult;
181
+ for (const target of userOrTarget) {
182
+ lastResult = await this.acceptInvitations(invitationIds, target);
183
+ }
184
+ if (!lastResult) {
185
+ throw new Error("No targets provided");
186
+ }
187
+ return lastResult;
188
+ }
189
+ const isLegacyTarget = "type" in userOrTarget && "value" in userOrTarget;
190
+ if (isLegacyTarget) {
191
+ console.warn(
192
+ '[Vortex SDK] DEPRECATED: Passing a target object is deprecated. Use the User format instead: acceptInvitations(invitationIds, { email: "user@example.com" })'
193
+ );
194
+ const target = userOrTarget;
195
+ const user2 = {};
196
+ if (target.type === "email") {
197
+ user2.email = target.value;
198
+ } else if (target.type === "phone" || target.type === "phoneNumber") {
199
+ user2.phone = target.value;
200
+ } else {
201
+ user2.email = target.value;
202
+ }
203
+ const response2 = await this.vortexApiRequest({
204
+ method: "POST",
205
+ body: {
206
+ invitationIds,
207
+ user: user2
208
+ },
209
+ path: `/api/v1/invitations/accept`
210
+ });
211
+ return response2;
212
+ }
213
+ const user = userOrTarget;
214
+ if (!user.email && !user.phone) {
215
+ throw new Error("User must have either email or phone");
216
+ }
169
217
  const response = await this.vortexApiRequest({
170
218
  method: "POST",
171
219
  body: {
172
220
  invitationIds,
173
- target
221
+ user
174
222
  },
175
223
  path: `/api/v1/invitations/accept`
176
224
  });
@@ -247,6 +295,54 @@ var Vortex = class {
247
295
  body: params
248
296
  });
249
297
  }
298
+ /**
299
+ * Create an invitation from your backend
300
+ *
301
+ * This method allows you to create invitations programmatically using your API key,
302
+ * without requiring a user JWT token. This is useful for server-side invitation
303
+ * creation, such as "People You May Know" flows or admin-initiated invitations.
304
+ *
305
+ * @param params - Invitation parameters
306
+ * @param params.widgetConfigurationId - The widget configuration ID to use
307
+ * @param params.target - The target of the invitation (who is being invited)
308
+ * @param params.target.type - 'email', 'phone', or 'internal'
309
+ * @param params.target.value - Email address, phone number, or internal user ID
310
+ * @param params.inviter - Information about the user creating the invitation
311
+ * @param params.inviter.userId - Your internal user ID for the inviter
312
+ * @param params.inviter.userEmail - Optional email of the inviter
313
+ * @param params.inviter.name - Optional display name of the inviter
314
+ * @param params.groups - Optional groups/scopes to associate with the invitation
315
+ * @param params.source - Optional source for analytics (defaults to 'api')
316
+ * @param params.templateVariables - Optional template variables for email customization
317
+ * @param params.metadata - Optional metadata passed through to webhooks
318
+ * @returns Created invitation with ID, short link, status, and creation timestamp
319
+ *
320
+ * @example
321
+ * ```typescript
322
+ * // Create an email invitation
323
+ * const invitation = await vortex.createInvitation({
324
+ * widgetConfigurationId: 'widget-config-123',
325
+ * target: { type: 'email', value: 'invitee@example.com' },
326
+ * inviter: { userId: 'user-456', userEmail: 'inviter@example.com', name: 'John Doe' },
327
+ * groups: [{ type: 'team', groupId: 'team-789', name: 'Engineering' }],
328
+ * });
329
+ *
330
+ * // Create an internal invitation (PYMK flow - no email sent)
331
+ * const pymkInvitation = await vortex.createInvitation({
332
+ * widgetConfigurationId: 'widget-config-123',
333
+ * target: { type: 'internal', value: 'internal-user-id-abc' },
334
+ * inviter: { userId: 'user-456' },
335
+ * source: 'pymk',
336
+ * });
337
+ * ```
338
+ */
339
+ async createInvitation(params) {
340
+ return this.vortexApiRequest({
341
+ method: "POST",
342
+ path: "/api/v1/invitations",
343
+ body: params
344
+ });
345
+ }
250
346
  };
251
347
  // Annotate the CommonJS export names for ESM import in node:
252
348
  0 && (module.exports = {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/vortex.ts"],"sourcesContent":["export * from './vortex';\nexport * from './types';","import crypto from 'node:crypto';\nimport { stringify as uuidStringify } from 'uuid';\nimport {\n ApiRequestBody,\n ApiResponseJson,\n InvitationResult,\n AcceptInvitationRequest,\n User,\n AutojoinDomainsResponse,\n ConfigureAutojoinRequest,\n} from './types';\n\nexport class Vortex {\n constructor(private apiKey: string) {}\n\n /**\n * Generate a JWT token for a user\n *\n * @param params - Object containing user and optional additional properties\n * @param params.user - User object with id, email, and optional adminScopes\n * @returns JWT token string\n *\n * @example\n * ```typescript\n * const token = vortex.generateJwt({\n * user: {\n * id: \"user-123\",\n * email: \"user@example.com\",\n * adminScopes: ['autojoin']\n * }\n * });\n * ```\n */\n generateJwt(params: { user: User; [key: string]: any }): string {\n const { user, ...rest } = params;\n const [prefix, encodedId, key] = this.apiKey.split('.'); // prefix is just VRTX\n if (!prefix || !encodedId || !key) {\n throw new Error('Invalid API key format');\n }\n if (prefix !== 'VRTX') {\n throw new Error('Invalid API key prefix');\n }\n const id = uuidStringify(Buffer.from(encodedId, 'base64url'));\n\n const expires = Math.floor(Date.now() / 1000) + 3600;\n\n // ๐Ÿ” Step 1: Derive signing key from API key + ID\n const signingKey = crypto.createHmac('sha256', key).update(id).digest(); // <- raw Buffer\n\n // ๐Ÿงฑ Step 2: Build header + payload\n const header = {\n iat: Math.floor(Date.now() / 1000),\n alg: 'HS256',\n typ: 'JWT',\n kid: id,\n };\n\n // Build payload with user data\n const payload: any = {\n userId: user.id,\n userEmail: user.email,\n expires,\n // Include identifiers array for widget compatibility (VrtxAutojoin checks this)\n identifiers: user.email ? [{ type: 'email', value: user.email }] : [],\n };\n\n // Add adminScopes if present\n if (user.adminScopes) {\n payload.adminScopes = user.adminScopes;\n // Add widget compatibility fields for autojoin admin\n if (user.adminScopes.includes('autojoin')) {\n payload.userIsAutojoinAdmin = true;\n payload.role = 'admin'; // VrtxAutojoin checks parsedJwt.role === 'admin'\n }\n }\n\n // Add any additional properties from rest\n if (rest && Object.keys(rest).length > 0) {\n Object.assign(payload, rest);\n }\n\n // ๐Ÿงฑ Step 3: Base64URL encode\n const headerB64 = Buffer.from(JSON.stringify(header)).toString('base64url');\n const payloadB64 = Buffer.from(JSON.stringify(payload)).toString('base64url');\n\n // ๐Ÿงพ Step 4: Sign\n const toSign = `${headerB64}.${payloadB64}`;\n const signature = Buffer.from(\n crypto.createHmac('sha256', signingKey).update(toSign).digest()\n ).toString('base64url');\n const jwt = `${toSign}.${signature}`;\n return jwt;\n }\n\n async vortexApiRequest(options: {\n method: 'GET' | 'POST' | 'PUT' | 'DELETE';\n path: string;\n body?: ApiRequestBody;\n queryParams?: Record<string, string | number | boolean>;\n }): Promise<ApiResponseJson> {\n const { method, path, body, queryParams } = options;\n const url = new URL(\n `${process.env.VORTEX_API_BASE_URL || 'https://api.vortexsoftware.com'}${path}`\n );\n if (queryParams) {\n Object.entries(queryParams).forEach(([key, value]) => {\n url.searchParams.append(key, String(value));\n });\n }\n const results = await fetch(url.toString(), {\n method,\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n if (!results.ok) {\n const errorBody = await results.text();\n throw new Error(\n `Vortex API request failed: ${results.status} ${results.statusText} - ${errorBody}`\n );\n }\n\n // Check if response has content to parse\n const contentLength = results.headers.get('content-length');\n const contentType = results.headers.get('content-type');\n\n // If no content or content-length is 0, return empty object\n if (contentLength === '0' || (!contentType?.includes('application/json') && !contentLength)) {\n return {};\n }\n\n // Try to get text first to check if there's actually content\n const responseText = await results.text();\n if (!responseText.trim()) {\n return {};\n }\n\n // Parse JSON if there's content\n try {\n return JSON.parse(responseText);\n } catch (error) {\n // If JSON parsing fails, return the text or empty object\n return {};\n }\n }\n\n async getInvitationsByTarget(\n targetType: 'email' | 'username' | 'phoneNumber',\n targetValue: string\n ): Promise<InvitationResult[]> {\n const response = (await this.vortexApiRequest({\n method: 'GET',\n path: '/api/v1/invitations',\n queryParams: {\n targetType,\n targetValue,\n },\n })) as { invitations: InvitationResult[] };\n return response.invitations;\n }\n\n async getInvitation(invitationId: string): Promise<InvitationResult> {\n return this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/${invitationId}`,\n }) as Promise<InvitationResult>;\n }\n\n async revokeInvitation(invitationId: string): Promise<{}> {\n return this.vortexApiRequest({\n method: 'DELETE',\n path: `/api/v1/invitations/${invitationId}`,\n }) as Promise<{}>;\n }\n\n async acceptInvitations(\n invitationIds: string[],\n target: { type: 'email' | 'username' | 'phoneNumber'; value: string }\n ): Promise<InvitationResult> {\n const response = (await this.vortexApiRequest({\n method: 'POST',\n body: {\n invitationIds,\n target,\n } as AcceptInvitationRequest,\n path: `/api/v1/invitations/accept`,\n })) as InvitationResult;\n return response;\n }\n\n async deleteInvitationsByGroup(groupType: string, groupId: string): Promise<{}> {\n return this.vortexApiRequest({\n method: 'DELETE',\n path: `/api/v1/invitations/by-group/${groupType}/${groupId}`,\n }) as Promise<{}>;\n }\n\n async getInvitationsByGroup(groupType: string, groupId: string): Promise<InvitationResult[]> {\n const response = (await this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/by-group/${groupType}/${groupId}`,\n })) as { invitations: InvitationResult[] };\n return response.invitations;\n }\n\n async reinvite(invitationId: string): Promise<InvitationResult> {\n return this.vortexApiRequest({\n method: 'POST',\n path: `/api/v1/invitations/${invitationId}/reinvite`,\n }) as Promise<InvitationResult>;\n }\n\n /**\n * Get autojoin domains configured for a specific scope\n *\n * @param scopeType - The type of scope (e.g., \"organization\", \"team\", \"project\")\n * @param scope - The scope identifier (customer's group ID)\n * @returns Autojoin domains and associated invitation\n *\n * @example\n * ```typescript\n * const result = await vortex.getAutojoinDomains('organization', 'acme-org');\n * console.log(result.autojoinDomains); // [{ id: '...', domain: 'acme.com' }]\n * ```\n */\n async getAutojoinDomains(scopeType: string, scope: string): Promise<AutojoinDomainsResponse> {\n return this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/by-scope/${encodeURIComponent(scopeType)}/${encodeURIComponent(scope)}/autojoin`,\n }) as Promise<AutojoinDomainsResponse>;\n }\n\n /**\n * Configure autojoin domains for a specific scope\n *\n * This endpoint syncs autojoin domains - it will add new domains, remove domains\n * not in the provided list, and deactivate the autojoin invitation if all domains\n * are removed (empty array).\n *\n * @param params - Configuration parameters\n * @param params.scope - The scope identifier (customer's group ID)\n * @param params.scopeType - The type of scope (e.g., \"organization\", \"team\")\n * @param params.scopeName - Optional display name for the scope\n * @param params.domains - Array of domains to configure for autojoin\n * @param params.widgetId - The widget configuration ID\n * @returns Updated autojoin domains and associated invitation\n *\n * @example\n * ```typescript\n * const result = await vortex.configureAutojoin({\n * scope: 'acme-org',\n * scopeType: 'organization',\n * scopeName: 'Acme Corporation',\n * domains: ['acme.com', 'acme.org'],\n * widgetId: 'widget-123',\n * });\n * ```\n */\n async configureAutojoin(params: ConfigureAutojoinRequest): Promise<AutojoinDomainsResponse> {\n return this.vortexApiRequest({\n method: 'POST',\n path: '/api/v1/invitations/autojoin',\n body: params as unknown as ApiRequestBody,\n }) as Promise<AutojoinDomainsResponse>;\n }\n}\n"],"mappings":"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","names":["uuidStringify","crypto"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/vortex.ts"],"sourcesContent":["export * from './vortex';\nexport * from './types';","import crypto from 'node:crypto';\nimport { stringify as uuidStringify } from 'uuid';\nimport {\n ApiRequestBody,\n ApiResponseJson,\n InvitationResult,\n AcceptInvitationRequest,\n User,\n AcceptUser,\n AutojoinDomainsResponse,\n ConfigureAutojoinRequest,\n InvitationTarget,\n CreateInvitationRequest,\n CreateInvitationResponse,\n} from './types';\n\nexport class Vortex {\n constructor(private apiKey: string) {}\n\n /**\n * Generate a JWT token for a user\n *\n * @param params - Object containing user and optional additional properties\n * @param params.user - User object with id, email, and optional adminScopes\n * @returns JWT token string\n *\n * @example\n * ```typescript\n * const token = vortex.generateJwt({\n * user: {\n * id: \"user-123\",\n * email: \"user@example.com\",\n * adminScopes: ['autojoin']\n * }\n * });\n * ```\n */\n generateJwt(params: { user: User; [key: string]: any }): string {\n const { user, ...rest } = params;\n const [prefix, encodedId, key] = this.apiKey.split('.'); // prefix is just VRTX\n if (!prefix || !encodedId || !key) {\n throw new Error('Invalid API key format');\n }\n if (prefix !== 'VRTX') {\n throw new Error('Invalid API key prefix');\n }\n const id = uuidStringify(Buffer.from(encodedId, 'base64url'));\n\n const expires = Math.floor(Date.now() / 1000) + 3600;\n\n // ๐Ÿ” Step 1: Derive signing key from API key + ID\n const signingKey = crypto.createHmac('sha256', key).update(id).digest(); // <- raw Buffer\n\n // ๐Ÿงฑ Step 2: Build header + payload\n const header = {\n iat: Math.floor(Date.now() / 1000),\n alg: 'HS256',\n typ: 'JWT',\n kid: id,\n };\n\n // Build payload with user data\n const payload: any = {\n userId: user.id,\n userEmail: user.email,\n expires,\n // Include identifiers array for widget compatibility (VrtxAutojoin checks this)\n identifiers: user.email ? [{ type: 'email', value: user.email }] : [],\n };\n\n // Add name if present\n if (user.name) {\n payload.name = user.name;\n }\n\n // Add avatarUrl if present\n if (user.avatarUrl) {\n payload.avatarUrl = user.avatarUrl;\n }\n\n // Add adminScopes if present\n if (user.adminScopes) {\n payload.adminScopes = user.adminScopes;\n // Add widget compatibility fields for autojoin admin\n if (user.adminScopes.includes('autojoin')) {\n payload.userIsAutojoinAdmin = true;\n payload.role = 'admin'; // VrtxAutojoin checks parsedJwt.role === 'admin'\n }\n }\n\n // Add any additional properties from rest\n if (rest && Object.keys(rest).length > 0) {\n Object.assign(payload, rest);\n }\n\n // ๐Ÿงฑ Step 3: Base64URL encode\n const headerB64 = Buffer.from(JSON.stringify(header)).toString('base64url');\n const payloadB64 = Buffer.from(JSON.stringify(payload)).toString('base64url');\n\n // ๐Ÿงพ Step 4: Sign\n const toSign = `${headerB64}.${payloadB64}`;\n const signature = Buffer.from(\n crypto.createHmac('sha256', signingKey).update(toSign).digest()\n ).toString('base64url');\n const jwt = `${toSign}.${signature}`;\n return jwt;\n }\n\n async vortexApiRequest(options: {\n method: 'GET' | 'POST' | 'PUT' | 'DELETE';\n path: string;\n body?: ApiRequestBody;\n queryParams?: Record<string, string | number | boolean>;\n }): Promise<ApiResponseJson> {\n const { method, path, body, queryParams } = options;\n const url = new URL(\n `${process.env.VORTEX_API_BASE_URL || 'https://api.vortexsoftware.com'}${path}`\n );\n if (queryParams) {\n Object.entries(queryParams).forEach(([key, value]) => {\n url.searchParams.append(key, String(value));\n });\n }\n const results = await fetch(url.toString(), {\n method,\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n if (!results.ok) {\n const errorBody = await results.text();\n throw new Error(\n `Vortex API request failed: ${results.status} ${results.statusText} - ${errorBody}`\n );\n }\n\n // Check if response has content to parse\n const contentLength = results.headers.get('content-length');\n const contentType = results.headers.get('content-type');\n\n // If no content or content-length is 0, return empty object\n if (contentLength === '0' || (!contentType?.includes('application/json') && !contentLength)) {\n return {};\n }\n\n // Try to get text first to check if there's actually content\n const responseText = await results.text();\n if (!responseText.trim()) {\n return {};\n }\n\n // Parse JSON if there's content\n try {\n return JSON.parse(responseText);\n } catch (error) {\n // If JSON parsing fails, return the text or empty object\n return {};\n }\n }\n\n async getInvitationsByTarget(\n targetType: 'email' | 'username' | 'phoneNumber',\n targetValue: string\n ): Promise<InvitationResult[]> {\n const response = (await this.vortexApiRequest({\n method: 'GET',\n path: '/api/v1/invitations',\n queryParams: {\n targetType,\n targetValue,\n },\n })) as { invitations: InvitationResult[] };\n return response.invitations;\n }\n\n async getInvitation(invitationId: string): Promise<InvitationResult> {\n return this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/${invitationId}`,\n }) as Promise<InvitationResult>;\n }\n\n async revokeInvitation(invitationId: string): Promise<{}> {\n return this.vortexApiRequest({\n method: 'DELETE',\n path: `/api/v1/invitations/${invitationId}`,\n }) as Promise<{}>;\n }\n\n /**\n * Accept invitations using the new User format (preferred)\n * @param invitationIds - Array of invitation IDs to accept\n * @param user - User object with email or phone (and optional name)\n * @returns Invitation result\n * @example\n * ```typescript\n * await vortex.acceptInvitations(['inv-123'], { email: 'user@example.com', name: 'John' });\n * ```\n */\n async acceptInvitations(invitationIds: string[], user: AcceptUser): Promise<InvitationResult>;\n\n /**\n * Accept invitations using legacy target format (deprecated)\n * @deprecated Use the User format instead: acceptInvitations(invitationIds, { email: 'user@example.com' })\n * @param invitationIds - Array of invitation IDs to accept\n * @param target - Legacy target object with type and value\n * @returns Invitation result\n */\n async acceptInvitations(\n invitationIds: string[],\n target: InvitationTarget\n ): Promise<InvitationResult>;\n\n /**\n * Accept invitations using multiple legacy targets (deprecated)\n * Will call the accept endpoint once per target\n * @deprecated Use the User format instead: acceptInvitations(invitationIds, { email: 'user@example.com' })\n * @param invitationIds - Array of invitation IDs to accept\n * @param targets - Array of legacy target objects\n * @returns Invitation result from the last acceptance\n */\n async acceptInvitations(\n invitationIds: string[],\n targets: InvitationTarget[]\n ): Promise<InvitationResult>;\n\n // Implementation\n async acceptInvitations(\n invitationIds: string[],\n userOrTarget: AcceptUser | InvitationTarget | InvitationTarget[]\n ): Promise<InvitationResult> {\n // Handle array of targets (legacy, call once per target)\n if (Array.isArray(userOrTarget)) {\n console.warn(\n '[Vortex SDK] DEPRECATED: Passing an array of targets is deprecated. Use the User format instead: acceptInvitations(invitationIds, { email: \"user@example.com\" })'\n );\n let lastResult: InvitationResult | undefined;\n for (const target of userOrTarget) {\n lastResult = await this.acceptInvitations(invitationIds, target);\n }\n if (!lastResult) {\n throw new Error('No targets provided');\n }\n return lastResult;\n }\n\n // Check if it's a legacy target format (has 'type' and 'value' properties)\n const isLegacyTarget = 'type' in userOrTarget && 'value' in userOrTarget;\n\n if (isLegacyTarget) {\n console.warn(\n '[Vortex SDK] DEPRECATED: Passing a target object is deprecated. Use the User format instead: acceptInvitations(invitationIds, { email: \"user@example.com\" })'\n );\n\n // Convert legacy target to User format\n const target = userOrTarget as InvitationTarget;\n const user: AcceptUser = {};\n\n if (target.type === 'email') {\n user.email = target.value;\n } else if (target.type === 'phone' || target.type === 'phoneNumber') {\n user.phone = target.value;\n } else {\n // For other types (like 'username'), try to use as email\n user.email = target.value;\n }\n\n // Make request with User format\n const response = (await this.vortexApiRequest({\n method: 'POST',\n body: {\n invitationIds,\n user,\n } as AcceptInvitationRequest,\n path: `/api/v1/invitations/accept`,\n })) as InvitationResult;\n return response;\n }\n\n // New User format\n const user = userOrTarget as AcceptUser;\n\n // Validate that either email or phone is provided\n if (!user.email && !user.phone) {\n throw new Error('User must have either email or phone');\n }\n\n const response = (await this.vortexApiRequest({\n method: 'POST',\n body: {\n invitationIds,\n user,\n } as AcceptInvitationRequest,\n path: `/api/v1/invitations/accept`,\n })) as InvitationResult;\n return response;\n }\n\n async deleteInvitationsByGroup(groupType: string, groupId: string): Promise<{}> {\n return this.vortexApiRequest({\n method: 'DELETE',\n path: `/api/v1/invitations/by-group/${groupType}/${groupId}`,\n }) as Promise<{}>;\n }\n\n async getInvitationsByGroup(groupType: string, groupId: string): Promise<InvitationResult[]> {\n const response = (await this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/by-group/${groupType}/${groupId}`,\n })) as { invitations: InvitationResult[] };\n return response.invitations;\n }\n\n async reinvite(invitationId: string): Promise<InvitationResult> {\n return this.vortexApiRequest({\n method: 'POST',\n path: `/api/v1/invitations/${invitationId}/reinvite`,\n }) as Promise<InvitationResult>;\n }\n\n /**\n * Get autojoin domains configured for a specific scope\n *\n * @param scopeType - The type of scope (e.g., \"organization\", \"team\", \"project\")\n * @param scope - The scope identifier (customer's group ID)\n * @returns Autojoin domains and associated invitation\n *\n * @example\n * ```typescript\n * const result = await vortex.getAutojoinDomains('organization', 'acme-org');\n * console.log(result.autojoinDomains); // [{ id: '...', domain: 'acme.com' }]\n * ```\n */\n async getAutojoinDomains(scopeType: string, scope: string): Promise<AutojoinDomainsResponse> {\n return this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/by-scope/${encodeURIComponent(scopeType)}/${encodeURIComponent(scope)}/autojoin`,\n }) as Promise<AutojoinDomainsResponse>;\n }\n\n /**\n * Configure autojoin domains for a specific scope\n *\n * This endpoint syncs autojoin domains - it will add new domains, remove domains\n * not in the provided list, and deactivate the autojoin invitation if all domains\n * are removed (empty array).\n *\n * @param params - Configuration parameters\n * @param params.scope - The scope identifier (customer's group ID)\n * @param params.scopeType - The type of scope (e.g., \"organization\", \"team\")\n * @param params.scopeName - Optional display name for the scope\n * @param params.domains - Array of domains to configure for autojoin\n * @param params.widgetId - The widget configuration ID\n * @returns Updated autojoin domains and associated invitation\n *\n * @example\n * ```typescript\n * const result = await vortex.configureAutojoin({\n * scope: 'acme-org',\n * scopeType: 'organization',\n * scopeName: 'Acme Corporation',\n * domains: ['acme.com', 'acme.org'],\n * widgetId: 'widget-123',\n * });\n * ```\n */\n async configureAutojoin(params: ConfigureAutojoinRequest): Promise<AutojoinDomainsResponse> {\n return this.vortexApiRequest({\n method: 'POST',\n path: '/api/v1/invitations/autojoin',\n body: params as unknown as ApiRequestBody,\n }) as Promise<AutojoinDomainsResponse>;\n }\n\n /**\n * Create an invitation from your backend\n *\n * This method allows you to create invitations programmatically using your API key,\n * without requiring a user JWT token. This is useful for server-side invitation\n * creation, such as \"People You May Know\" flows or admin-initiated invitations.\n *\n * @param params - Invitation parameters\n * @param params.widgetConfigurationId - The widget configuration ID to use\n * @param params.target - The target of the invitation (who is being invited)\n * @param params.target.type - 'email', 'phone', or 'internal'\n * @param params.target.value - Email address, phone number, or internal user ID\n * @param params.inviter - Information about the user creating the invitation\n * @param params.inviter.userId - Your internal user ID for the inviter\n * @param params.inviter.userEmail - Optional email of the inviter\n * @param params.inviter.name - Optional display name of the inviter\n * @param params.groups - Optional groups/scopes to associate with the invitation\n * @param params.source - Optional source for analytics (defaults to 'api')\n * @param params.templateVariables - Optional template variables for email customization\n * @param params.metadata - Optional metadata passed through to webhooks\n * @returns Created invitation with ID, short link, status, and creation timestamp\n *\n * @example\n * ```typescript\n * // Create an email invitation\n * const invitation = await vortex.createInvitation({\n * widgetConfigurationId: 'widget-config-123',\n * target: { type: 'email', value: 'invitee@example.com' },\n * inviter: { userId: 'user-456', userEmail: 'inviter@example.com', name: 'John Doe' },\n * groups: [{ type: 'team', groupId: 'team-789', name: 'Engineering' }],\n * });\n *\n * // Create an internal invitation (PYMK flow - no email sent)\n * const pymkInvitation = await vortex.createInvitation({\n * widgetConfigurationId: 'widget-config-123',\n * target: { type: 'internal', value: 'internal-user-id-abc' },\n * inviter: { userId: 'user-456' },\n * source: 'pymk',\n * });\n * ```\n */\n async createInvitation(params: CreateInvitationRequest): Promise<CreateInvitationResponse> {\n return this.vortexApiRequest({\n method: 'POST',\n path: '/api/v1/invitations',\n body: params as unknown as ApiRequestBody,\n }) as Promise<CreateInvitationResponse>;\n }\n}\n"],"mappings":"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","names":["uuidStringify","crypto","user","response"]}
package/dist/index.mjs CHANGED
@@ -48,6 +48,12 @@ var Vortex = class {
48
48
  // Include identifiers array for widget compatibility (VrtxAutojoin checks this)
49
49
  identifiers: user.email ? [{ type: "email", value: user.email }] : []
50
50
  };
51
+ if (user.name) {
52
+ payload.name = user.name;
53
+ }
54
+ if (user.avatarUrl) {
55
+ payload.avatarUrl = user.avatarUrl;
56
+ }
51
57
  if (user.adminScopes) {
52
58
  payload.adminScopes = user.adminScopes;
53
59
  if (user.adminScopes.includes("autojoin")) {
@@ -129,12 +135,54 @@ var Vortex = class {
129
135
  path: `/api/v1/invitations/${invitationId}`
130
136
  });
131
137
  }
132
- async acceptInvitations(invitationIds, target) {
138
+ // Implementation
139
+ async acceptInvitations(invitationIds, userOrTarget) {
140
+ if (Array.isArray(userOrTarget)) {
141
+ console.warn(
142
+ '[Vortex SDK] DEPRECATED: Passing an array of targets is deprecated. Use the User format instead: acceptInvitations(invitationIds, { email: "user@example.com" })'
143
+ );
144
+ let lastResult;
145
+ for (const target of userOrTarget) {
146
+ lastResult = await this.acceptInvitations(invitationIds, target);
147
+ }
148
+ if (!lastResult) {
149
+ throw new Error("No targets provided");
150
+ }
151
+ return lastResult;
152
+ }
153
+ const isLegacyTarget = "type" in userOrTarget && "value" in userOrTarget;
154
+ if (isLegacyTarget) {
155
+ console.warn(
156
+ '[Vortex SDK] DEPRECATED: Passing a target object is deprecated. Use the User format instead: acceptInvitations(invitationIds, { email: "user@example.com" })'
157
+ );
158
+ const target = userOrTarget;
159
+ const user2 = {};
160
+ if (target.type === "email") {
161
+ user2.email = target.value;
162
+ } else if (target.type === "phone" || target.type === "phoneNumber") {
163
+ user2.phone = target.value;
164
+ } else {
165
+ user2.email = target.value;
166
+ }
167
+ const response2 = await this.vortexApiRequest({
168
+ method: "POST",
169
+ body: {
170
+ invitationIds,
171
+ user: user2
172
+ },
173
+ path: `/api/v1/invitations/accept`
174
+ });
175
+ return response2;
176
+ }
177
+ const user = userOrTarget;
178
+ if (!user.email && !user.phone) {
179
+ throw new Error("User must have either email or phone");
180
+ }
133
181
  const response = await this.vortexApiRequest({
134
182
  method: "POST",
135
183
  body: {
136
184
  invitationIds,
137
- target
185
+ user
138
186
  },
139
187
  path: `/api/v1/invitations/accept`
140
188
  });
@@ -211,6 +259,54 @@ var Vortex = class {
211
259
  body: params
212
260
  });
213
261
  }
262
+ /**
263
+ * Create an invitation from your backend
264
+ *
265
+ * This method allows you to create invitations programmatically using your API key,
266
+ * without requiring a user JWT token. This is useful for server-side invitation
267
+ * creation, such as "People You May Know" flows or admin-initiated invitations.
268
+ *
269
+ * @param params - Invitation parameters
270
+ * @param params.widgetConfigurationId - The widget configuration ID to use
271
+ * @param params.target - The target of the invitation (who is being invited)
272
+ * @param params.target.type - 'email', 'phone', or 'internal'
273
+ * @param params.target.value - Email address, phone number, or internal user ID
274
+ * @param params.inviter - Information about the user creating the invitation
275
+ * @param params.inviter.userId - Your internal user ID for the inviter
276
+ * @param params.inviter.userEmail - Optional email of the inviter
277
+ * @param params.inviter.name - Optional display name of the inviter
278
+ * @param params.groups - Optional groups/scopes to associate with the invitation
279
+ * @param params.source - Optional source for analytics (defaults to 'api')
280
+ * @param params.templateVariables - Optional template variables for email customization
281
+ * @param params.metadata - Optional metadata passed through to webhooks
282
+ * @returns Created invitation with ID, short link, status, and creation timestamp
283
+ *
284
+ * @example
285
+ * ```typescript
286
+ * // Create an email invitation
287
+ * const invitation = await vortex.createInvitation({
288
+ * widgetConfigurationId: 'widget-config-123',
289
+ * target: { type: 'email', value: 'invitee@example.com' },
290
+ * inviter: { userId: 'user-456', userEmail: 'inviter@example.com', name: 'John Doe' },
291
+ * groups: [{ type: 'team', groupId: 'team-789', name: 'Engineering' }],
292
+ * });
293
+ *
294
+ * // Create an internal invitation (PYMK flow - no email sent)
295
+ * const pymkInvitation = await vortex.createInvitation({
296
+ * widgetConfigurationId: 'widget-config-123',
297
+ * target: { type: 'internal', value: 'internal-user-id-abc' },
298
+ * inviter: { userId: 'user-456' },
299
+ * source: 'pymk',
300
+ * });
301
+ * ```
302
+ */
303
+ async createInvitation(params) {
304
+ return this.vortexApiRequest({
305
+ method: "POST",
306
+ path: "/api/v1/invitations",
307
+ body: params
308
+ });
309
+ }
214
310
  };
215
311
  export {
216
312
  Vortex
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/vortex.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport { stringify as uuidStringify } from 'uuid';\nimport {\n ApiRequestBody,\n ApiResponseJson,\n InvitationResult,\n AcceptInvitationRequest,\n User,\n AutojoinDomainsResponse,\n ConfigureAutojoinRequest,\n} from './types';\n\nexport class Vortex {\n constructor(private apiKey: string) {}\n\n /**\n * Generate a JWT token for a user\n *\n * @param params - Object containing user and optional additional properties\n * @param params.user - User object with id, email, and optional adminScopes\n * @returns JWT token string\n *\n * @example\n * ```typescript\n * const token = vortex.generateJwt({\n * user: {\n * id: \"user-123\",\n * email: \"user@example.com\",\n * adminScopes: ['autojoin']\n * }\n * });\n * ```\n */\n generateJwt(params: { user: User; [key: string]: any }): string {\n const { user, ...rest } = params;\n const [prefix, encodedId, key] = this.apiKey.split('.'); // prefix is just VRTX\n if (!prefix || !encodedId || !key) {\n throw new Error('Invalid API key format');\n }\n if (prefix !== 'VRTX') {\n throw new Error('Invalid API key prefix');\n }\n const id = uuidStringify(Buffer.from(encodedId, 'base64url'));\n\n const expires = Math.floor(Date.now() / 1000) + 3600;\n\n // ๐Ÿ” Step 1: Derive signing key from API key + ID\n const signingKey = crypto.createHmac('sha256', key).update(id).digest(); // <- raw Buffer\n\n // ๐Ÿงฑ Step 2: Build header + payload\n const header = {\n iat: Math.floor(Date.now() / 1000),\n alg: 'HS256',\n typ: 'JWT',\n kid: id,\n };\n\n // Build payload with user data\n const payload: any = {\n userId: user.id,\n userEmail: user.email,\n expires,\n // Include identifiers array for widget compatibility (VrtxAutojoin checks this)\n identifiers: user.email ? [{ type: 'email', value: user.email }] : [],\n };\n\n // Add adminScopes if present\n if (user.adminScopes) {\n payload.adminScopes = user.adminScopes;\n // Add widget compatibility fields for autojoin admin\n if (user.adminScopes.includes('autojoin')) {\n payload.userIsAutojoinAdmin = true;\n payload.role = 'admin'; // VrtxAutojoin checks parsedJwt.role === 'admin'\n }\n }\n\n // Add any additional properties from rest\n if (rest && Object.keys(rest).length > 0) {\n Object.assign(payload, rest);\n }\n\n // ๐Ÿงฑ Step 3: Base64URL encode\n const headerB64 = Buffer.from(JSON.stringify(header)).toString('base64url');\n const payloadB64 = Buffer.from(JSON.stringify(payload)).toString('base64url');\n\n // ๐Ÿงพ Step 4: Sign\n const toSign = `${headerB64}.${payloadB64}`;\n const signature = Buffer.from(\n crypto.createHmac('sha256', signingKey).update(toSign).digest()\n ).toString('base64url');\n const jwt = `${toSign}.${signature}`;\n return jwt;\n }\n\n async vortexApiRequest(options: {\n method: 'GET' | 'POST' | 'PUT' | 'DELETE';\n path: string;\n body?: ApiRequestBody;\n queryParams?: Record<string, string | number | boolean>;\n }): Promise<ApiResponseJson> {\n const { method, path, body, queryParams } = options;\n const url = new URL(\n `${process.env.VORTEX_API_BASE_URL || 'https://api.vortexsoftware.com'}${path}`\n );\n if (queryParams) {\n Object.entries(queryParams).forEach(([key, value]) => {\n url.searchParams.append(key, String(value));\n });\n }\n const results = await fetch(url.toString(), {\n method,\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n if (!results.ok) {\n const errorBody = await results.text();\n throw new Error(\n `Vortex API request failed: ${results.status} ${results.statusText} - ${errorBody}`\n );\n }\n\n // Check if response has content to parse\n const contentLength = results.headers.get('content-length');\n const contentType = results.headers.get('content-type');\n\n // If no content or content-length is 0, return empty object\n if (contentLength === '0' || (!contentType?.includes('application/json') && !contentLength)) {\n return {};\n }\n\n // Try to get text first to check if there's actually content\n const responseText = await results.text();\n if (!responseText.trim()) {\n return {};\n }\n\n // Parse JSON if there's content\n try {\n return JSON.parse(responseText);\n } catch (error) {\n // If JSON parsing fails, return the text or empty object\n return {};\n }\n }\n\n async getInvitationsByTarget(\n targetType: 'email' | 'username' | 'phoneNumber',\n targetValue: string\n ): Promise<InvitationResult[]> {\n const response = (await this.vortexApiRequest({\n method: 'GET',\n path: '/api/v1/invitations',\n queryParams: {\n targetType,\n targetValue,\n },\n })) as { invitations: InvitationResult[] };\n return response.invitations;\n }\n\n async getInvitation(invitationId: string): Promise<InvitationResult> {\n return this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/${invitationId}`,\n }) as Promise<InvitationResult>;\n }\n\n async revokeInvitation(invitationId: string): Promise<{}> {\n return this.vortexApiRequest({\n method: 'DELETE',\n path: `/api/v1/invitations/${invitationId}`,\n }) as Promise<{}>;\n }\n\n async acceptInvitations(\n invitationIds: string[],\n target: { type: 'email' | 'username' | 'phoneNumber'; value: string }\n ): Promise<InvitationResult> {\n const response = (await this.vortexApiRequest({\n method: 'POST',\n body: {\n invitationIds,\n target,\n } as AcceptInvitationRequest,\n path: `/api/v1/invitations/accept`,\n })) as InvitationResult;\n return response;\n }\n\n async deleteInvitationsByGroup(groupType: string, groupId: string): Promise<{}> {\n return this.vortexApiRequest({\n method: 'DELETE',\n path: `/api/v1/invitations/by-group/${groupType}/${groupId}`,\n }) as Promise<{}>;\n }\n\n async getInvitationsByGroup(groupType: string, groupId: string): Promise<InvitationResult[]> {\n const response = (await this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/by-group/${groupType}/${groupId}`,\n })) as { invitations: InvitationResult[] };\n return response.invitations;\n }\n\n async reinvite(invitationId: string): Promise<InvitationResult> {\n return this.vortexApiRequest({\n method: 'POST',\n path: `/api/v1/invitations/${invitationId}/reinvite`,\n }) as Promise<InvitationResult>;\n }\n\n /**\n * Get autojoin domains configured for a specific scope\n *\n * @param scopeType - The type of scope (e.g., \"organization\", \"team\", \"project\")\n * @param scope - The scope identifier (customer's group ID)\n * @returns Autojoin domains and associated invitation\n *\n * @example\n * ```typescript\n * const result = await vortex.getAutojoinDomains('organization', 'acme-org');\n * console.log(result.autojoinDomains); // [{ id: '...', domain: 'acme.com' }]\n * ```\n */\n async getAutojoinDomains(scopeType: string, scope: string): Promise<AutojoinDomainsResponse> {\n return this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/by-scope/${encodeURIComponent(scopeType)}/${encodeURIComponent(scope)}/autojoin`,\n }) as Promise<AutojoinDomainsResponse>;\n }\n\n /**\n * Configure autojoin domains for a specific scope\n *\n * This endpoint syncs autojoin domains - it will add new domains, remove domains\n * not in the provided list, and deactivate the autojoin invitation if all domains\n * are removed (empty array).\n *\n * @param params - Configuration parameters\n * @param params.scope - The scope identifier (customer's group ID)\n * @param params.scopeType - The type of scope (e.g., \"organization\", \"team\")\n * @param params.scopeName - Optional display name for the scope\n * @param params.domains - Array of domains to configure for autojoin\n * @param params.widgetId - The widget configuration ID\n * @returns Updated autojoin domains and associated invitation\n *\n * @example\n * ```typescript\n * const result = await vortex.configureAutojoin({\n * scope: 'acme-org',\n * scopeType: 'organization',\n * scopeName: 'Acme Corporation',\n * domains: ['acme.com', 'acme.org'],\n * widgetId: 'widget-123',\n * });\n * ```\n */\n async configureAutojoin(params: ConfigureAutojoinRequest): Promise<AutojoinDomainsResponse> {\n return this.vortexApiRequest({\n method: 'POST',\n path: '/api/v1/invitations/autojoin',\n body: params as unknown as ApiRequestBody,\n }) as Promise<AutojoinDomainsResponse>;\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/vortex.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport { stringify as uuidStringify } from 'uuid';\nimport {\n ApiRequestBody,\n ApiResponseJson,\n InvitationResult,\n AcceptInvitationRequest,\n User,\n AcceptUser,\n AutojoinDomainsResponse,\n ConfigureAutojoinRequest,\n InvitationTarget,\n CreateInvitationRequest,\n CreateInvitationResponse,\n} from './types';\n\nexport class Vortex {\n constructor(private apiKey: string) {}\n\n /**\n * Generate a JWT token for a user\n *\n * @param params - Object containing user and optional additional properties\n * @param params.user - User object with id, email, and optional adminScopes\n * @returns JWT token string\n *\n * @example\n * ```typescript\n * const token = vortex.generateJwt({\n * user: {\n * id: \"user-123\",\n * email: \"user@example.com\",\n * adminScopes: ['autojoin']\n * }\n * });\n * ```\n */\n generateJwt(params: { user: User; [key: string]: any }): string {\n const { user, ...rest } = params;\n const [prefix, encodedId, key] = this.apiKey.split('.'); // prefix is just VRTX\n if (!prefix || !encodedId || !key) {\n throw new Error('Invalid API key format');\n }\n if (prefix !== 'VRTX') {\n throw new Error('Invalid API key prefix');\n }\n const id = uuidStringify(Buffer.from(encodedId, 'base64url'));\n\n const expires = Math.floor(Date.now() / 1000) + 3600;\n\n // ๐Ÿ” Step 1: Derive signing key from API key + ID\n const signingKey = crypto.createHmac('sha256', key).update(id).digest(); // <- raw Buffer\n\n // ๐Ÿงฑ Step 2: Build header + payload\n const header = {\n iat: Math.floor(Date.now() / 1000),\n alg: 'HS256',\n typ: 'JWT',\n kid: id,\n };\n\n // Build payload with user data\n const payload: any = {\n userId: user.id,\n userEmail: user.email,\n expires,\n // Include identifiers array for widget compatibility (VrtxAutojoin checks this)\n identifiers: user.email ? [{ type: 'email', value: user.email }] : [],\n };\n\n // Add name if present\n if (user.name) {\n payload.name = user.name;\n }\n\n // Add avatarUrl if present\n if (user.avatarUrl) {\n payload.avatarUrl = user.avatarUrl;\n }\n\n // Add adminScopes if present\n if (user.adminScopes) {\n payload.adminScopes = user.adminScopes;\n // Add widget compatibility fields for autojoin admin\n if (user.adminScopes.includes('autojoin')) {\n payload.userIsAutojoinAdmin = true;\n payload.role = 'admin'; // VrtxAutojoin checks parsedJwt.role === 'admin'\n }\n }\n\n // Add any additional properties from rest\n if (rest && Object.keys(rest).length > 0) {\n Object.assign(payload, rest);\n }\n\n // ๐Ÿงฑ Step 3: Base64URL encode\n const headerB64 = Buffer.from(JSON.stringify(header)).toString('base64url');\n const payloadB64 = Buffer.from(JSON.stringify(payload)).toString('base64url');\n\n // ๐Ÿงพ Step 4: Sign\n const toSign = `${headerB64}.${payloadB64}`;\n const signature = Buffer.from(\n crypto.createHmac('sha256', signingKey).update(toSign).digest()\n ).toString('base64url');\n const jwt = `${toSign}.${signature}`;\n return jwt;\n }\n\n async vortexApiRequest(options: {\n method: 'GET' | 'POST' | 'PUT' | 'DELETE';\n path: string;\n body?: ApiRequestBody;\n queryParams?: Record<string, string | number | boolean>;\n }): Promise<ApiResponseJson> {\n const { method, path, body, queryParams } = options;\n const url = new URL(\n `${process.env.VORTEX_API_BASE_URL || 'https://api.vortexsoftware.com'}${path}`\n );\n if (queryParams) {\n Object.entries(queryParams).forEach(([key, value]) => {\n url.searchParams.append(key, String(value));\n });\n }\n const results = await fetch(url.toString(), {\n method,\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n if (!results.ok) {\n const errorBody = await results.text();\n throw new Error(\n `Vortex API request failed: ${results.status} ${results.statusText} - ${errorBody}`\n );\n }\n\n // Check if response has content to parse\n const contentLength = results.headers.get('content-length');\n const contentType = results.headers.get('content-type');\n\n // If no content or content-length is 0, return empty object\n if (contentLength === '0' || (!contentType?.includes('application/json') && !contentLength)) {\n return {};\n }\n\n // Try to get text first to check if there's actually content\n const responseText = await results.text();\n if (!responseText.trim()) {\n return {};\n }\n\n // Parse JSON if there's content\n try {\n return JSON.parse(responseText);\n } catch (error) {\n // If JSON parsing fails, return the text or empty object\n return {};\n }\n }\n\n async getInvitationsByTarget(\n targetType: 'email' | 'username' | 'phoneNumber',\n targetValue: string\n ): Promise<InvitationResult[]> {\n const response = (await this.vortexApiRequest({\n method: 'GET',\n path: '/api/v1/invitations',\n queryParams: {\n targetType,\n targetValue,\n },\n })) as { invitations: InvitationResult[] };\n return response.invitations;\n }\n\n async getInvitation(invitationId: string): Promise<InvitationResult> {\n return this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/${invitationId}`,\n }) as Promise<InvitationResult>;\n }\n\n async revokeInvitation(invitationId: string): Promise<{}> {\n return this.vortexApiRequest({\n method: 'DELETE',\n path: `/api/v1/invitations/${invitationId}`,\n }) as Promise<{}>;\n }\n\n /**\n * Accept invitations using the new User format (preferred)\n * @param invitationIds - Array of invitation IDs to accept\n * @param user - User object with email or phone (and optional name)\n * @returns Invitation result\n * @example\n * ```typescript\n * await vortex.acceptInvitations(['inv-123'], { email: 'user@example.com', name: 'John' });\n * ```\n */\n async acceptInvitations(invitationIds: string[], user: AcceptUser): Promise<InvitationResult>;\n\n /**\n * Accept invitations using legacy target format (deprecated)\n * @deprecated Use the User format instead: acceptInvitations(invitationIds, { email: 'user@example.com' })\n * @param invitationIds - Array of invitation IDs to accept\n * @param target - Legacy target object with type and value\n * @returns Invitation result\n */\n async acceptInvitations(\n invitationIds: string[],\n target: InvitationTarget\n ): Promise<InvitationResult>;\n\n /**\n * Accept invitations using multiple legacy targets (deprecated)\n * Will call the accept endpoint once per target\n * @deprecated Use the User format instead: acceptInvitations(invitationIds, { email: 'user@example.com' })\n * @param invitationIds - Array of invitation IDs to accept\n * @param targets - Array of legacy target objects\n * @returns Invitation result from the last acceptance\n */\n async acceptInvitations(\n invitationIds: string[],\n targets: InvitationTarget[]\n ): Promise<InvitationResult>;\n\n // Implementation\n async acceptInvitations(\n invitationIds: string[],\n userOrTarget: AcceptUser | InvitationTarget | InvitationTarget[]\n ): Promise<InvitationResult> {\n // Handle array of targets (legacy, call once per target)\n if (Array.isArray(userOrTarget)) {\n console.warn(\n '[Vortex SDK] DEPRECATED: Passing an array of targets is deprecated. Use the User format instead: acceptInvitations(invitationIds, { email: \"user@example.com\" })'\n );\n let lastResult: InvitationResult | undefined;\n for (const target of userOrTarget) {\n lastResult = await this.acceptInvitations(invitationIds, target);\n }\n if (!lastResult) {\n throw new Error('No targets provided');\n }\n return lastResult;\n }\n\n // Check if it's a legacy target format (has 'type' and 'value' properties)\n const isLegacyTarget = 'type' in userOrTarget && 'value' in userOrTarget;\n\n if (isLegacyTarget) {\n console.warn(\n '[Vortex SDK] DEPRECATED: Passing a target object is deprecated. Use the User format instead: acceptInvitations(invitationIds, { email: \"user@example.com\" })'\n );\n\n // Convert legacy target to User format\n const target = userOrTarget as InvitationTarget;\n const user: AcceptUser = {};\n\n if (target.type === 'email') {\n user.email = target.value;\n } else if (target.type === 'phone' || target.type === 'phoneNumber') {\n user.phone = target.value;\n } else {\n // For other types (like 'username'), try to use as email\n user.email = target.value;\n }\n\n // Make request with User format\n const response = (await this.vortexApiRequest({\n method: 'POST',\n body: {\n invitationIds,\n user,\n } as AcceptInvitationRequest,\n path: `/api/v1/invitations/accept`,\n })) as InvitationResult;\n return response;\n }\n\n // New User format\n const user = userOrTarget as AcceptUser;\n\n // Validate that either email or phone is provided\n if (!user.email && !user.phone) {\n throw new Error('User must have either email or phone');\n }\n\n const response = (await this.vortexApiRequest({\n method: 'POST',\n body: {\n invitationIds,\n user,\n } as AcceptInvitationRequest,\n path: `/api/v1/invitations/accept`,\n })) as InvitationResult;\n return response;\n }\n\n async deleteInvitationsByGroup(groupType: string, groupId: string): Promise<{}> {\n return this.vortexApiRequest({\n method: 'DELETE',\n path: `/api/v1/invitations/by-group/${groupType}/${groupId}`,\n }) as Promise<{}>;\n }\n\n async getInvitationsByGroup(groupType: string, groupId: string): Promise<InvitationResult[]> {\n const response = (await this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/by-group/${groupType}/${groupId}`,\n })) as { invitations: InvitationResult[] };\n return response.invitations;\n }\n\n async reinvite(invitationId: string): Promise<InvitationResult> {\n return this.vortexApiRequest({\n method: 'POST',\n path: `/api/v1/invitations/${invitationId}/reinvite`,\n }) as Promise<InvitationResult>;\n }\n\n /**\n * Get autojoin domains configured for a specific scope\n *\n * @param scopeType - The type of scope (e.g., \"organization\", \"team\", \"project\")\n * @param scope - The scope identifier (customer's group ID)\n * @returns Autojoin domains and associated invitation\n *\n * @example\n * ```typescript\n * const result = await vortex.getAutojoinDomains('organization', 'acme-org');\n * console.log(result.autojoinDomains); // [{ id: '...', domain: 'acme.com' }]\n * ```\n */\n async getAutojoinDomains(scopeType: string, scope: string): Promise<AutojoinDomainsResponse> {\n return this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/by-scope/${encodeURIComponent(scopeType)}/${encodeURIComponent(scope)}/autojoin`,\n }) as Promise<AutojoinDomainsResponse>;\n }\n\n /**\n * Configure autojoin domains for a specific scope\n *\n * This endpoint syncs autojoin domains - it will add new domains, remove domains\n * not in the provided list, and deactivate the autojoin invitation if all domains\n * are removed (empty array).\n *\n * @param params - Configuration parameters\n * @param params.scope - The scope identifier (customer's group ID)\n * @param params.scopeType - The type of scope (e.g., \"organization\", \"team\")\n * @param params.scopeName - Optional display name for the scope\n * @param params.domains - Array of domains to configure for autojoin\n * @param params.widgetId - The widget configuration ID\n * @returns Updated autojoin domains and associated invitation\n *\n * @example\n * ```typescript\n * const result = await vortex.configureAutojoin({\n * scope: 'acme-org',\n * scopeType: 'organization',\n * scopeName: 'Acme Corporation',\n * domains: ['acme.com', 'acme.org'],\n * widgetId: 'widget-123',\n * });\n * ```\n */\n async configureAutojoin(params: ConfigureAutojoinRequest): Promise<AutojoinDomainsResponse> {\n return this.vortexApiRequest({\n method: 'POST',\n path: '/api/v1/invitations/autojoin',\n body: params as unknown as ApiRequestBody,\n }) as Promise<AutojoinDomainsResponse>;\n }\n\n /**\n * Create an invitation from your backend\n *\n * This method allows you to create invitations programmatically using your API key,\n * without requiring a user JWT token. This is useful for server-side invitation\n * creation, such as \"People You May Know\" flows or admin-initiated invitations.\n *\n * @param params - Invitation parameters\n * @param params.widgetConfigurationId - The widget configuration ID to use\n * @param params.target - The target of the invitation (who is being invited)\n * @param params.target.type - 'email', 'phone', or 'internal'\n * @param params.target.value - Email address, phone number, or internal user ID\n * @param params.inviter - Information about the user creating the invitation\n * @param params.inviter.userId - Your internal user ID for the inviter\n * @param params.inviter.userEmail - Optional email of the inviter\n * @param params.inviter.name - Optional display name of the inviter\n * @param params.groups - Optional groups/scopes to associate with the invitation\n * @param params.source - Optional source for analytics (defaults to 'api')\n * @param params.templateVariables - Optional template variables for email customization\n * @param params.metadata - Optional metadata passed through to webhooks\n * @returns Created invitation with ID, short link, status, and creation timestamp\n *\n * @example\n * ```typescript\n * // Create an email invitation\n * const invitation = await vortex.createInvitation({\n * widgetConfigurationId: 'widget-config-123',\n * target: { type: 'email', value: 'invitee@example.com' },\n * inviter: { userId: 'user-456', userEmail: 'inviter@example.com', name: 'John Doe' },\n * groups: [{ type: 'team', groupId: 'team-789', name: 'Engineering' }],\n * });\n *\n * // Create an internal invitation (PYMK flow - no email sent)\n * const pymkInvitation = await vortex.createInvitation({\n * widgetConfigurationId: 'widget-config-123',\n * target: { type: 'internal', value: 'internal-user-id-abc' },\n * inviter: { userId: 'user-456' },\n * source: 'pymk',\n * });\n * ```\n */\n async createInvitation(params: CreateInvitationRequest): Promise<CreateInvitationResponse> {\n return this.vortexApiRequest({\n method: 'POST',\n path: '/api/v1/invitations',\n body: params as unknown as ApiRequestBody,\n }) as Promise<CreateInvitationResponse>;\n }\n}\n"],"mappings":"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","names":["user","response"]}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@teamvortexsoftware/vortex-node-22-sdk",
3
3
  "description": "Vortex Node 22 SDK",
4
4
  "author": "@teamvortexsoftware",
5
- "version": "0.1.3",
5
+ "version": "0.3.0",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.mjs",
8
8
  "types": "./dist/index.d.ts",
@@ -36,7 +36,7 @@
36
36
  "jest": "29.7.0",
37
37
  "ts-jest": "29.4.6",
38
38
  "tsup": "8.5.1",
39
- "typescript-eslint": "8.50.0"
39
+ "typescript-eslint": "8.50.1"
40
40
  },
41
41
  "dependencies": {
42
42
  "uuid": "11.1.0"