@teamvortexsoftware/vortex-node-22-sdk 0.1.4 โ†’ 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,6 +53,9 @@ 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;
56
59
  };
57
60
  /**
58
61
  * User type for accepting invitations
@@ -82,6 +85,8 @@ type ApiRequestBody = AcceptInvitationRequest | AcceptInvitationRequestLegacy |
82
85
  type User = {
83
86
  id: string;
84
87
  email: string;
88
+ name?: string;
89
+ avatarUrl?: string;
85
90
  adminScopes?: string[];
86
91
  [key: string]: any;
87
92
  };
@@ -110,6 +115,73 @@ type ConfigureAutojoinRequest = {
110
115
  widgetId: string;
111
116
  metadata?: Record<string, any>;
112
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
+ };
113
185
 
114
186
  declare class Vortex {
115
187
  private apiKey;
@@ -217,6 +289,48 @@ declare class Vortex {
217
289
  * ```
218
290
  */
219
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>;
220
334
  }
221
335
 
222
- export { type AcceptInvitationRequest, type AcceptInvitationRequestLegacy, type AcceptUser, 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,6 +53,9 @@ 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;
56
59
  };
57
60
  /**
58
61
  * User type for accepting invitations
@@ -82,6 +85,8 @@ type ApiRequestBody = AcceptInvitationRequest | AcceptInvitationRequestLegacy |
82
85
  type User = {
83
86
  id: string;
84
87
  email: string;
88
+ name?: string;
89
+ avatarUrl?: string;
85
90
  adminScopes?: string[];
86
91
  [key: string]: any;
87
92
  };
@@ -110,6 +115,73 @@ type ConfigureAutojoinRequest = {
110
115
  widgetId: string;
111
116
  metadata?: Record<string, any>;
112
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
+ };
113
185
 
114
186
  declare class Vortex {
115
187
  private apiKey;
@@ -217,6 +289,48 @@ declare class Vortex {
217
289
  * ```
218
290
  */
219
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>;
220
334
  }
221
335
 
222
- export { type AcceptInvitationRequest, type AcceptInvitationRequestLegacy, type AcceptUser, 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")) {
@@ -189,7 +195,7 @@ var Vortex = class {
189
195
  const user2 = {};
190
196
  if (target.type === "email") {
191
197
  user2.email = target.value;
192
- } else if (target.type === "sms" || target.type === "phoneNumber") {
198
+ } else if (target.type === "phone" || target.type === "phoneNumber") {
193
199
  user2.phone = target.value;
194
200
  } else {
195
201
  user2.email = target.value;
@@ -289,6 +295,54 @@ var Vortex = class {
289
295
  body: params
290
296
  });
291
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
+ }
292
346
  };
293
347
  // Annotate the CommonJS export names for ESM import in node:
294
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 AcceptUser,\n AutojoinDomainsResponse,\n ConfigureAutojoinRequest,\n InvitationTarget,\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 /**\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 === 'sms' || 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"],"mappings":"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","names":["uuidStringify","crypto","user","response"]}
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")) {
@@ -153,7 +159,7 @@ var Vortex = class {
153
159
  const user2 = {};
154
160
  if (target.type === "email") {
155
161
  user2.email = target.value;
156
- } else if (target.type === "sms" || target.type === "phoneNumber") {
162
+ } else if (target.type === "phone" || target.type === "phoneNumber") {
157
163
  user2.phone = target.value;
158
164
  } else {
159
165
  user2.email = target.value;
@@ -253,6 +259,54 @@ var Vortex = class {
253
259
  body: params
254
260
  });
255
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
+ }
256
310
  };
257
311
  export {
258
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 AcceptUser,\n AutojoinDomainsResponse,\n ConfigureAutojoinRequest,\n InvitationTarget,\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 /**\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 === 'sms' || 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"],"mappings":"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","names":["user","response"]}
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.4",
5
+ "version": "0.3.0",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.mjs",
8
8
  "types": "./dist/index.d.ts",