vortex-ruby-sdk 1.15.0 → 1.18.0.pre.20260427181620

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2fa76d2aa711c84370eb93e563946ee45c78c12d2a5c54d6b4de13ebb49f3c6a
4
- data.tar.gz: 74e291d07ca213da127b5cb35e8964e63664d101ffff8fe06aa3e586b8efc42f
3
+ metadata.gz: 0c1d76a55b4ddd17c08d1dcbd45a393e9bed146e1e76dcc0526bcd80fa57710e
4
+ data.tar.gz: 6ccf49c2301554141b1591e674147b58f9841dd5471106d057611c5731685fab
5
5
  SHA512:
6
- metadata.gz: ef4b13b91543a141f041d2bfcc812b48e55a63998a7d8da7e445107daec73bb10e3d047532dfce015ed51a8160ddf5df28f78ca46285311fefa3a6f692ad40af
7
- data.tar.gz: 9a8723995e0cdfae388688385544242e30c2c280192938ac59aebfa724f5ebf0f3508ea1eb44765c92d8ec936885bdef30ee0b828fab6ecc4c4746b29a19860f
6
+ metadata.gz: 98cf940bc167c1c6a5d81875829a6281a15bd570ff17bff5430fce7f881d761840979507533e8926deddb4bb8bae970b5d80a2a0b9c80bc61d42efa4e51350d3
7
+ data.tar.gz: 53de4c4f581d6fc5a251887f4159a013f712da9e5f86a13cd442c00eef8deaaaa7af1e206374bb3163819ada40d5cb9509d99a6d4727b102a8f728c7da15b878
data/README.md CHANGED
@@ -1,345 +1,782 @@
1
- # Vortex Ruby SDK
1
+ # vortex-ruby-sdk
2
2
 
3
- A Ruby SDK for the Vortex invitation system, providing seamless integration with the same functionality and API compatibility as other Vortex SDKs (Node.js, Python, Java, Go).
3
+ <!-- AUTO-GENERATED FROM SDK MANIFEST DO NOT EDIT DIRECTLY -->
4
4
 
5
- ## Features
5
+ ![Version](https://img.shields.io/badge/version-1.18.0-blue)
6
+ ![Language](https://img.shields.io/badge/language-ruby-green)
6
7
 
7
- - **JWT Generation**: Identical algorithm to other SDKs for complete compatibility
8
- - **Simplified JWT Format**: New streamlined payload with `userEmail` and `adminScopes`
9
- - **Backward Compatible**: Legacy JWT format still supported
10
- - **Complete API Coverage**: All invitation management operations
11
- - **Framework Integration**: Built-in Rails and Sinatra helpers
12
- - **Same Route Structure**: Ensures React provider compatibility
13
- - **Comprehensive Testing**: Full test coverage with RSpec
14
- - **Type Safety**: Clear method signatures and documentation
15
- - **Multiple Delivery Types**: Support for `email`, `phone`, `share`, and `internal` invitation delivery
16
- - `internal` invitations allow for customer-managed, in-app invitation flows with no external communication
8
+ **Invitation infrastructure for modern apps**
17
9
 
18
- ## Installation
10
+ Vortex handles the complete invitation lifecycle — sending invites via email/SMS/share links, tracking clicks and conversions, managing referral programs, and optimizing your invitation flows with A/B testing.
11
+ [Learn more about Vortex →](https://tryvortex.com)
12
+
13
+ ## Why This SDK?
14
+
15
+ This backend SDK securely signs user data for Vortex components. Your API key stays on your server, while the signed token is passed to the frontend where Vortex components render the invitation UI.
16
+
17
+ - Keep your API key secure — it never touches the browser
18
+ - Sign user identity for attribution — know who sent each invitation
19
+ - Control what data components can access via scoped tokens
20
+ - Verify webhook signatures for secure event handling
21
+
22
+ ## How It Works
23
+
24
+ Vortex uses a split architecture: your backend signs tokens with the SDK, and your frontend renders components that use those tokens to securely interact with Vortex.
25
+
26
+ ```
27
+ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
28
+ │ Your Server │ │ User Browser │ │ Vortex Cloud │
29
+ │ (this SDK) │ │ (component) │ │ │
30
+ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘
31
+ │ │ │
32
+ │ 1. generate_token │ │
33
+ │◄──────────────────────│ │
34
+ │ │ │
35
+ │ 2. Return token │ │
36
+ │──────────────────────►│ │
37
+ │ │ │
38
+ │ │ 3. Component calls │
39
+ │ │ API with token │
40
+ │ │──────────────────────►│
41
+ │ │ │
42
+ │ │ 4. Render UI, │
43
+ │ │ send invitations │
44
+ │ │◄──────────────────────│
45
+ │ │ │
46
+ ```
47
+
48
+ ### Integration Flow
19
49
 
20
- Add this line to your application's Gemfile:
50
+ **1. Install the backend SDK** `[backend]`
51
+
52
+ Add this SDK to your Ruby project
21
53
 
22
54
  ```ruby
23
55
  gem 'vortex-ruby-sdk'
24
56
  ```
25
57
 
26
- And then execute:
58
+ **2. Initialize the client** `[backend]`
27
59
 
28
- ```bash
29
- bundle install
60
+ Create a Vortex client with your API key (keep this on the server!)
61
+
62
+ ```ruby
63
+ require 'vortex'
64
+
65
+ client = Vortex::Client.new(ENV['VORTEX_API_KEY'])
30
66
  ```
31
67
 
32
- Or install it yourself as:
68
+ **3. Generate a token for the current user** `[backend]`
33
69
 
34
- ```bash
35
- gem install vortex-ruby-sdk
70
+ When a user loads a page with a Vortex component, generate a signed token on your server
71
+
72
+ ```ruby
73
+ token = client.generate_token(user: { id: current_user.id })
36
74
  ```
37
75
 
38
- ## Basic Usage
76
+ **4. Pass the token to your frontend** `[backend]`
77
+
78
+ Include the token in your page response or API response
79
+
80
+ ```ruby
81
+ render json: { vortex_token: token }
82
+ ```
83
+
84
+ **5. Render a Vortex component with the token** `[frontend]`
85
+
86
+ Use the React/Angular/Web Component with the token
87
+
88
+ ```ruby
89
+ import { VortexInvite } from "@teamvortexsoftware/vortex-react";
90
+
91
+ <VortexInvite token={vortexToken} />
92
+ ```
93
+
94
+ **6. Vortex handles the rest** `[vortex]`
95
+
96
+ The component securely communicates with Vortex servers, displays the invitation UI, sends emails/SMS, tracks conversions, and reports analytics
97
+
98
+ ### Security Model
99
+
100
+ > ⚠️ **Important:** Your Vortex API key is a secret that grants full access to your account. It must never be exposed to browsers or client-side code.
101
+
102
+ By signing tokens on your server, you:
103
+
104
+ - Keep your API key secret (it never leaves your server)
105
+ - Control exactly what user data is shared with components
106
+ - Ensure invitations are attributed to real, authenticated users
107
+ - Prevent abuse — users can only send invitations as themselves
108
+
109
+ #### When Signing is Optional
110
+
111
+ Token signing is controlled by your component configuration in the Vortex dashboard. If "Require Secure Token" is enabled, requests without a valid token will be rejected. If disabled (e.g., for public referral programs), components work without backend signing.
112
+
113
+ ---
114
+
115
+ ## Quick Start
116
+
117
+ Generate a secure token for Vortex components
39
118
 
40
119
  ```ruby
41
120
  require 'vortex'
42
121
 
43
- # Initialize the client
44
122
  client = Vortex::Client.new(ENV['VORTEX_API_KEY'])
45
123
 
46
- # Create a user object
47
- user = {
48
- id: 'user-123',
49
- email: 'user@example.com',
50
- user_name: 'Jane Doe', # Optional: user's display name
51
- user_avatar_url: 'https://example.com/avatars/jane.jpg', # Optional: user's avatar URL
52
- admin_scopes: ['autojoin'] # Optional: grants autojoin admin privileges
53
- }
54
-
55
- # Generate JWT
56
- jwt = client.generate_jwt(user: user)
57
-
58
- # Get invitations by target
59
- invitations = client.get_invitations_by_target('email', 'user@example.com')
60
-
61
- # Accept an invitation
62
- client.accept_invitation('inv-123', { email: 'user@example.com' })
63
-
64
- # Accept with new vs existing user tracking
65
- # is_existing: true if user was already registered, false if new signup
66
- client.accept_invitation('inv-123', {
67
- email: 'user@example.com',
68
- is_existing: false # New user signup
69
- })
70
-
71
- # Get invitations by scope
72
- scope_invitations = client.get_invitations_by_scope('team', 'team1')
124
+ # Generate a token for the current user
125
+ token = client.generate_token(user: { id: 'user-123', email: 'user@example.com' })
126
+
127
+ # Pass the token to your frontend component
128
+
73
129
  ```
74
130
 
75
- ## Token Generation (for Widgets)
131
+ ## Installation
132
+
133
+ ```bash
134
+ gem install vortex-ruby-sdk
135
+ ```
76
136
 
77
- Use `generate_token` for widget authentication. This method generates a signed JWT token that can be passed to Vortex widgets via the `token` prop.
137
+ ## Initialization
78
138
 
79
139
  ```ruby
80
- # Sign just the user (minimum for secure invitation attribution)
81
- token = client.generate_token({ user: { id: 'user-123' } })
82
-
83
- # Sign full payload with component, scope, and variables
84
- token = client.generate_token({
85
- component: 'widget-abc',
86
- user: { id: 'user-123', name: 'Peter', email: 'peter@example.com' },
87
- scope: 'workspace_456',
88
- vars: { company_name: 'Acme' }
89
- })
90
-
91
- # Custom expiration (default is 5 minutes)
92
- payload = { user: { id: 'user-123' } }
93
- token = client.generate_token(payload, { expires_in: '1h' }) # Supports "5m", "1h", "24h", "7d"
94
- token = client.generate_token(payload, { expires_in: 3600 }) # Or seconds as integer
140
+ client = Vortex::Client.new(ENV['VORTEX_API_KEY'])
95
141
  ```
96
142
 
97
- ## Rails Integration
143
+ ### Environment Variables
144
+
145
+ | Variable | Required | Description |
146
+ | ---------------- | -------- | ------------------- |
147
+ | `VORTEX_API_KEY` | ✓ | Your Vortex API key |
148
+
149
+ ## Core Methods
150
+
151
+ These are the methods you'll use most often.
152
+
153
+ ### `generate_token()`
98
154
 
99
- Create a controller with Vortex routes:
155
+ Generate a signed token for use with Vortex widgets. This method generates a signed JWT token containing your payload data. The token can be passed to widgets via the `token` prop to authenticate and authorize the request.
156
+
157
+ **Signature:**
100
158
 
101
159
  ```ruby
102
- # app/controllers/vortex_controller.rb
103
- class VortexController < ApplicationController
104
- include Vortex::Rails::Controller
105
-
106
- private
107
-
108
- def authenticate_vortex_user
109
- # Return user data hash or nil
110
- admin_scopes = []
111
- admin_scopes << 'autojoin' if current_user.admin?
112
-
113
- {
114
- id: current_user.id,
115
- email: current_user.email,
116
- admin_scopes: admin_scopes
117
- }
118
- end
160
+ generate_token(payload, options = nil)
161
+ ```
119
162
 
120
- def authorize_vortex_operation(operation, user)
121
- # Implement your authorization logic
122
- case operation
123
- when 'JWT', 'GET_INVITATIONS'
124
- true
125
- when 'REVOKE_INVITATION'
126
- user[:admin_scopes]&.include?('autojoin')
127
- else
128
- false
129
- end
130
- end
163
+ **Parameters:**
131
164
 
132
- def vortex_client
133
- @vortex_client ||= Vortex::Client.new(Rails.application.credentials.vortex_api_key)
134
- end
135
- end
165
+ | Name | Type | Required | Description |
166
+ | --------- | ------ | -------- | ------------------------------------------------- |
167
+ | `payload` | `Hash` | ✓ | Data to sign (user, component, scope, vars, etc.) |
168
+ | `options` | `Hash` | ✓ | Optional configuration |
169
+
170
+ **Returns:** `String`
171
+ — Signed JWT token
172
+
173
+ **Example:**
174
+
175
+ ```ruby
176
+ # token = client.generate_token({ user: { id: 'user-123' } })
177
+ #
178
+ # token = client.generate_token({
179
+ # component: 'widget-abc',
180
+ # user: { id: 'user-123', name: 'Peter', email: 'peter@example.com' },
181
+ # scope: 'workspace_456',
182
+ # vars: { company_name: 'Acme' }
183
+ # })
184
+ #
185
+ # token = client.generate_token(payload, { expires_in: '1h' })
186
+ # token = client.generate_token(payload, { expires_in: 3600 }) # seconds
136
187
  ```
137
188
 
138
- Add routes to `config/routes.rb`:
189
+ _Added in v0.8.0_
190
+
191
+ ---
192
+
193
+ ### `get_invitation()`
194
+
195
+ Get a specific invitation by ID
196
+
197
+ **Signature:**
139
198
 
140
199
  ```ruby
141
- Rails.application.routes.draw do
142
- scope '/api/vortex', controller: 'vortex' do
143
- post 'jwt', action: 'generate_jwt'
144
- get 'invitations', action: 'get_invitations_by_target'
145
- get 'invitations/:invitation_id', action: 'get_invitation'
146
- delete 'invitations/:invitation_id', action: 'revoke_invitation'
147
- post 'invitations/accept', action: 'accept_invitations'
148
- get 'invitations/by-scope/:scope_type/:scope', action: 'get_invitations_by_scope'
149
- delete 'invitations/by-scope/:scope_type/:scope', action: 'delete_invitations_by_scope'
150
- post 'invitations/:invitation_id/reinvite', action: 'reinvite'
151
- end
152
- end
200
+ get_invitation(invitation_id)
153
201
  ```
154
202
 
155
- ## Sinatra Integration
203
+ **Parameters:**
204
+
205
+ | Name | Type | Required | Description |
206
+ | --------------- | -------- | -------- | ----------------- |
207
+ | `invitation_id` | `String` | ✓ | The invitation ID |
208
+
209
+ **Returns:** `String`
210
+ — The invitation data
211
+
212
+ _Added in v0.1.0_
213
+
214
+ ---
215
+
216
+ ### `accept_invitation()`
217
+
218
+ Accept a single invitation (recommended method) This is the recommended method for accepting invitations.
219
+
220
+ **Signature:**
156
221
 
157
222
  ```ruby
158
- require 'sinatra/base'
159
- require 'vortex/sinatra'
223
+ accept_invitation(invitation_id, user)
224
+ ```
160
225
 
161
- class MyApp < Sinatra::Base
162
- register Vortex::Sinatra
226
+ **Parameters:**
163
227
 
164
- configure do
165
- set :vortex_api_key, ENV['VORTEX_API_KEY']
166
- end
228
+ | Name | Type | Required | Description |
229
+ | --------------- | -------- | -------- | ----------------------------------- |
230
+ | `invitation_id` | `String` | ✓ | Single invitation ID to accept |
231
+ | `user` | `Hash` | ✓ | User hash with :email and/or :phone |
167
232
 
168
- def authenticate_vortex_user
169
- # Implement authentication logic
170
- user_id = request.env['HTTP_X_USER_ID']
171
- return nil unless user_id
233
+ **Returns:** `String`
234
+ The accepted invitation result
172
235
 
173
- {
174
- id: user_id,
175
- email: 'user@example.com',
176
- admin_scopes: [] # Optional
177
- }
178
- end
236
+ **Example:**
179
237
 
180
- def authorize_vortex_operation(operation, user)
181
- # Implement authorization logic
182
- user != nil
183
- end
184
- end
238
+ ```ruby
239
+ # user = { email: 'user@example.com', name: 'John Doe' }
240
+ # result = client.accept_invitation('inv-123', user)
185
241
  ```
186
242
 
187
- ## API Methods
243
+ _Added in v0.6.0_
244
+
245
+ ---
246
+
247
+ ## All Methods
248
+
249
+ <details>
250
+ <summary>Click to expand full method reference</summary>
251
+
252
+ ### `get_invitations_by_target()`
188
253
 
189
- All methods match the functionality of other Vortex SDKs:
254
+ Get invitations by target
190
255
 
191
- ### JWT Generation
256
+ **Signature:**
192
257
 
193
- - `generate_jwt(user:, extra: nil)` - Generate JWT token
194
- - `user`: Hash with `:id`, `:email`, and optional `:admin_scopes` array
195
- - `extra`: Optional hash with additional properties to include in JWT payload
258
+ ```ruby
259
+ get_invitations_by_target(target_type, target_value)
260
+ ```
261
+
262
+ **Parameters:**
196
263
 
197
- ### Invitation Management
264
+ | Name | Type | Required | Description |
265
+ | -------------- | -------- | -------- | --------------------------------------------- |
266
+ | `target_type` | `String` | ✓ | Type of target (email, sms) |
267
+ | `target_value` | `String` | ✓ | Value of target (email address, phone number) |
198
268
 
199
- - `get_invitations_by_target(target_type, target_value)` - Get invitations by target
200
- - `get_invitation(invitation_id)` - Get specific invitation
201
- - `revoke_invitation(invitation_id)` - Revoke invitation
202
- - `accept_invitation(invitation_id, user)` - Accept an invitation
203
- - `get_invitations_by_scope(scope_type, scope)` - Get scope invitations
204
- - `delete_invitations_by_scope(scope_type, scope)` - Delete scope invitations
205
- - `reinvite(invitation_id)` - Reinvite user
206
- - `sync_internal_invitation(creator_id, target_value, action, component_id)` - Sync internal invitation action
269
+ **Returns:** `String`
270
+ List of invitations
207
271
 
208
- ## Route Structure
272
+ _Added in v0.1.0_
209
273
 
210
- The SDK provides these routes (same as other SDKs for React provider compatibility):
274
+ ---
211
275
 
212
- - `POST /api/vortex/jwt`
213
- - `GET /api/vortex/invitations?targetType=email&targetValue=user@example.com`
214
- - `GET /api/vortex/invitations/:invitation_id`
215
- - `DELETE /api/vortex/invitations/:invitation_id`
216
- - `POST /api/vortex/invitations/accept`
217
- - `GET /api/vortex/invitations/by-scope/:scope_type/:scope`
218
- - `DELETE /api/vortex/invitations/by-scope/:scope_type/:scope`
219
- - `POST /api/vortex/invitations/:invitation_id/reinvite`
220
- - `POST /api/vortex/invitations/sync-internal-invitation`
276
+ ### `revoke_invitation()`
221
277
 
222
- ## Sync Internal Invitation
278
+ Revoke (delete) an invitation
223
279
 
224
- If you're using `internal` delivery type invitations and managing the invitation flow within your own application, you can sync invitation decisions back to Vortex when users accept or decline invitations in your system.
280
+ **Signature:**
225
281
 
226
282
  ```ruby
227
- # Sync an internal invitation action
228
- result = client.sync_internal_invitation(
229
- 'user-123', # creator_id - The inviter's user ID in your system
230
- 'user-456', # target_value - The invitee's user ID in your system
231
- 'accepted', # action - "accepted" or "declined"
232
- 'component-uuid' # component_id - The widget component UUID
233
- )
234
-
235
- puts "Processed: #{result['processed']}"
236
- puts "Invitation IDs: #{result['invitationIds']}"
283
+ revoke_invitation(invitation_id)
237
284
  ```
238
285
 
239
286
  **Parameters:**
240
287
 
241
- - `creator_id` (String) The inviter's user ID in your system
242
- - `target_value` (String) The invitee's user ID in your system
243
- - `action` ("accepted" | "declined") The invitation decision
244
- - `component_id` (String) — The widget component UUID
288
+ | Name | Type | Required | Description |
289
+ | --------------- | -------- | -------- | --------------------------- |
290
+ | `invitation_id` | `String` | ✓ | The invitation ID to revoke |
245
291
 
246
- **Response:**
292
+ **Returns:** `String`
293
+ — Success response
247
294
 
248
- - `processed` (Integer) — Count of invitations processed
249
- - `invitationIds` (Array<String>) — IDs of processed invitations
295
+ _Added in v0.1.0_
250
296
 
251
- **Use cases:**
297
+ ---
252
298
 
253
- - You handle invitation delivery through your own in-app notifications or UI
254
- - Users accept/decline invitations within your application
255
- - You need to keep Vortex updated with the invitation status
299
+ ### `accept_invitations()`
256
300
 
257
- ## JWT Payload Structure
301
+ Accept invitations using the new User format (preferred) Supports three formats: 1. User hash (preferred): { email: '...', phone: '...', name: '...' } 2. Target hash (deprecated): { type: 'email', value: '...' } 3. Array of targets (deprecated): [{ type: 'email', value: '...' }, ...]
258
302
 
259
- The SDK generates JWTs with the following payload structure:
303
+ **Signature:**
260
304
 
261
305
  ```ruby
262
- {
263
- userId: 'user-123',
264
- userEmail: 'user@example.com',
265
- adminScopes: ['autojoin'], # Full array included if admin_scopes provided
266
- expires: 1234567890
267
- }
306
+ accept_invitations(invitation_ids, user_or_target)
268
307
  ```
269
308
 
270
- Additional properties from the `extra` parameter are merged into the payload.
309
+ **Parameters:**
271
310
 
272
- ## Error Handling
311
+ | Name | Type | Required | Description |
312
+ | ---------------- | --------------- | -------- | ------------------------------------------------------------ |
313
+ | `invitation_ids` | `Array<String>` | ✓ | List of invitation IDs to accept |
314
+ | `user_or_target` | `Hash, Array` | ✓ | User hash with :email/:phone/:name keys, OR legacy target(s) |
273
315
 
274
- All methods raise `Vortex::VortexError` on failures:
316
+ **Returns:** `String`
317
+ — The accepted invitation result
318
+
319
+ **Example:**
275
320
 
276
321
  ```ruby
277
- begin
278
- jwt = client.generate_jwt(
279
- user: {
280
- id: 'user-123',
281
- email: 'user@example.com'
282
- }
283
- )
284
- rescue Vortex::VortexError => e
285
- logger.error "Vortex error: #{e.message}"
286
- end
322
+ # user = { email: 'user@example.com', name: 'John Doe' }
323
+ # result = client.accept_invitations(['inv-123'], user)
324
+ #
325
+ # target = { type: 'email', value: 'user@example.com' }
326
+ # result = client.accept_invitations(['inv-123'], target)
287
327
  ```
288
328
 
289
- ## Development
329
+ _Added in v0.1.0_
290
330
 
291
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt.
331
+ ---
292
332
 
293
- To install this gem onto your local machine, run `bundle exec rake install`.
333
+ ### `get_invitations_by_scope()`
294
334
 
295
- ## Contributing
335
+ Get invitations by group
296
336
 
297
- ## Webhooks
337
+ **Signature:**
338
+
339
+ ```ruby
340
+ get_invitations_by_scope(scope_type, scope)
341
+ ```
342
+
343
+ **Parameters:**
344
+
345
+ | Name | Type | Required | Description |
346
+ | ------------ | -------- | -------- | -------------- |
347
+ | `scope_type` | `String` | ✓ | The group type |
348
+ | `scope` | `String` | ✓ | The group ID |
298
349
 
299
- The SDK provides built-in support for verifying and parsing incoming webhook events from Vortex.
350
+ **Returns:** `String`
351
+ — List of invitations for the group
352
+
353
+ _Added in v0.4.0_
354
+
355
+ ---
356
+
357
+ ### `delete_invitations_by_scope()`
358
+
359
+ Delete invitations by group
360
+
361
+ **Signature:**
300
362
 
301
363
  ```ruby
302
- require 'vortex'
364
+ delete_invitations_by_scope(scope_type, scope)
365
+ ```
366
+
367
+ **Parameters:**
368
+
369
+ | Name | Type | Required | Description |
370
+ | ------------ | -------- | -------- | -------------- |
371
+ | `scope_type` | `String` | ✓ | The group type |
372
+ | `scope` | `String` | ✓ | The group ID |
373
+
374
+ **Returns:** `String`
375
+ — Success response
376
+
377
+ _Added in v0.4.0_
378
+
379
+ ---
380
+
381
+ ### `reinvite()`
382
+
383
+ Reinvite a user
303
384
 
304
- webhooks = Vortex::Webhooks.new(secret: ENV['VORTEX_WEBHOOK_SECRET'])
385
+ **Signature:**
305
386
 
306
- # In your HTTP handler (Rails example):
387
+ ```ruby
388
+ reinvite(invitation_id)
389
+ ```
390
+
391
+ **Parameters:**
392
+
393
+ | Name | Type | Required | Description |
394
+ | --------------- | -------- | -------- | ----------------------------- |
395
+ | `invitation_id` | `String` | ✓ | The invitation ID to reinvite |
396
+
397
+ **Returns:** `String`
398
+ — The reinvited invitation result
399
+
400
+ _Added in v0.2.0_
401
+
402
+ ---
403
+
404
+ ### `get_autojoin_domains()`
405
+
406
+ Get autojoin domains configured for a specific scope
407
+
408
+ **Signature:**
409
+
410
+ ```ruby
411
+ get_autojoin_domains(scope_type, scope)
412
+ ```
413
+
414
+ **Parameters:**
415
+
416
+ | Name | Type | Required | Description |
417
+ | ------------ | -------- | -------- | ----------------------------------------------------------- |
418
+ | `scope_type` | `String` | ✓ | The type of scope (e.g., "organization", "team", "project") |
419
+ | `scope` | `String` | ✓ | The scope identifier (customer's group ID) |
420
+
421
+ **Returns:** `String`
422
+ — Response with :autojoin_domains array and :invitation
423
+
424
+ **Example:**
425
+
426
+ ```ruby
427
+ # result = client.get_autojoin_domains('organization', 'acme-org')
428
+ # result['autojoinDomains'].each do |domain|
429
+ # puts "Domain: #{domain['domain']}"
430
+ # end
431
+ ```
432
+
433
+ _Added in v0.6.0_
434
+
435
+ ---
436
+
437
+ ### `configure_autojoin()`
438
+
439
+ Configure autojoin domains for a specific scope This endpoint syncs autojoin domains - it will add new domains, remove domains not in the provided list, and deactivate the autojoin invitation if all domains are removed (empty array).
440
+
441
+ **Signature:**
442
+
443
+ ```ruby
444
+ configure_autojoin(scope, scope_type, domains, component_id, scope_name = nil, metadata = nil)
445
+ ```
446
+
447
+ **Parameters:**
448
+
449
+ | Name | Type | Required | Description |
450
+ | -------------- | --------------- | -------- | ------------------------------------------------ |
451
+ | `scope` | `String` | ✓ | The scope identifier (customer's group ID) |
452
+ | `scope_type` | `String` | ✓ | The type of scope (e.g., "organization", "team") |
453
+ | `domains` | `Array<String>` | ✓ | Array of domains to configure for autojoin |
454
+ | `component_id` | `String` | ✓ | The component ID |
455
+ | `scope_name` | `String, nil` | ✓ | Optional display name for the scope |
456
+ | `metadata` | `Hash, nil` | ✓ | Optional metadata to attach to the invitation |
457
+
458
+ **Returns:** `String`
459
+ — Response with :autojoin_domains array and :invitation
460
+
461
+ **Example:**
462
+
463
+ ```ruby
464
+ # result = client.configure_autojoin(
465
+ # 'acme-org',
466
+ # 'organization',
467
+ # ['acme.com', 'acme.org'],
468
+ # 'component-123',
469
+ # 'Acme Corporation'
470
+ # )
471
+ ```
472
+
473
+ _Added in v0.6.0_
474
+
475
+ ---
476
+
477
+ </details>
478
+
479
+ ## Types
480
+
481
+ <details>
482
+ <summary>Click to expand type definitions</summary>
483
+
484
+ ### `GenerateTokenPayload`
485
+
486
+ Payload for generate_token() - used to generate secure tokens for Vortex components
487
+
488
+ | Field | Type | Required | Description |
489
+ | ----------- | ------------------ | -------- | ---------------------------------------------------------------------- |
490
+ | `user` | `Hash (TokenUser)` | | The authenticated user who will be using the Vortex component |
491
+ | `component` | `String` | | Component ID to generate token for (from your Vortex dashboard) |
492
+ | `scope` | `String` | | Scope identifier to restrict invitations (format: "scopeType:scopeId") |
493
+ | `vars` | `Hash` | | Custom variables to pass to the component for template rendering |
494
+
495
+ ### `TokenUser`
496
+
497
+ User data for token generation - represents the authenticated user sending invitations
498
+
499
+ | Field | Type | Required | Description |
500
+ | ----------------------- | --------------- | -------- | ----------------------------------------------------------------------------- |
501
+ | `id` | `String` | ✓ | Unique identifier for the user in your system. Used to attribute invitations. |
502
+ | `email` | `String` | | User's email address. Used for reply-to in invitation emails. |
503
+ | `name` | `String` | | Display name shown to invitation recipients (e.g., "John invited you") |
504
+ | `avatar_url` | `String` | | URL to user's avatar image. Displayed in invitation emails and widgets. |
505
+ | `admin_scopes` | `Array<String>` | | List of scope IDs where this user has admin privileges |
506
+ | `allowed_email_domains` | `Array<String>` | | Restrict invitations to specific email domains (e.g., ["acme.com"]) |
507
+
508
+ ### `AcceptUser`
509
+
510
+ User data for accepting invitations - identifies who accepted the invitation
511
+
512
+ | Field | Type | Required | Description |
513
+ | ------------- | --------- | -------- | ---------------------------------------------------------------------------------- |
514
+ | `email` | `String` | | Email address of the accepting user. At least one of email or phone is required. |
515
+ | `phone` | `String` | | Phone number with country code. At least one of email or phone is required. |
516
+ | `name` | `String` | | Display name of the accepting user (shown in notifications to inviter) |
517
+ | `is_existing` | `Boolean` | | Whether user was already registered. true=existing, false=new signup, nil=unknown. |
518
+
519
+ ### `CreateInvitationTarget`
520
+
521
+ Target specification when creating an invitation - where to send the invite
522
+
523
+ | Field | Type | Required | Description |
524
+ | ------- | -------- | -------- | ---------------------------------------------------------------------------------- |
525
+ | `type` | `String` | ✓ | Delivery channel: "email", "phone", "share", or "internal" |
526
+ | `value` | `String` | ✓ | Target address: email address, phone number with country code, or internal user ID |
527
+ | `name` | `String` | | Display name of the recipient (used in email greetings) |
528
+
529
+ ### `CreateInvitationScope`
530
+
531
+ Scope specification when creating an invitation - what group/team to invite into
532
+
533
+ | Field | Type | Required | Description |
534
+ | ---------- | -------- | -------- | ------------------------------------------------------- |
535
+ | `type` | `String` | ✓ | Scope type (e.g., "team", "organization", "workspace") |
536
+ | `group_id` | `String` | ✓ | Your internal identifier for this scope/group |
537
+ | `name` | `String` | ✓ | Display name for the scope (shown in invitation emails) |
538
+
539
+ ### `Identifier`
540
+
541
+ Email or phone identifier for looking up users
542
+
543
+ | Field | Type | Required | Description |
544
+ | ------- | -------- | -------- | --------------------------------------------------------------- |
545
+ | `type` | `String` | ✓ | Identifier type: "email" or "phone" |
546
+ | `value` | `String` | ✓ | The email address or phone number (with country code for phone) |
547
+
548
+ ### `ConfigureAutojoinRequest`
549
+
550
+ Request to configure autojoin domains for a scope
551
+
552
+ | Field | Type | Required | Description |
553
+ | ------------ | --------------- | -------- | ----------------------------------------------------------------- |
554
+ | `scope_type` | `String` | ✓ | Type of scope (e.g., "team", "workspace") |
555
+ | `scope_id` | `String` | ✓ | Your internal identifier for the scope |
556
+ | `domains` | `Array<String>` | ✓ | List of email domains to enable autojoin for (e.g., ["acme.com"]) |
557
+
558
+ ### `SyncInternalInvitationRequest`
559
+
560
+ Request to sync an internal invitation (for tracking invitations made outside Vortex)
561
+
562
+ | Field | Type | Required | Description |
563
+ | ------------ | ------------------------------- | -------- | ------------------------------------------------------------ |
564
+ | `inviter_id` | `String` | ✓ | Your internal user ID for the person who sent the invitation |
565
+ | `target` | `Hash (CreateInvitationTarget)` | ✓ | The invitation recipient |
566
+ | `scopes` | `Array<Hash>` | | Scopes/groups the invitation grants access to |
567
+
568
+ ### `InvitationResult`
569
+
570
+ Complete invitation details as returned by the Vortex API
571
+
572
+ | Field | Type | Required | Description |
573
+ | -------------------------- | ---------------- | -------- | ---------------------------------------------------------------------------------------------------------------------- |
574
+ | `id` | `String` | ✓ | Unique identifier for this invitation |
575
+ | `account_id` | `String` | ✓ | Your Vortex account ID |
576
+ | `click_throughs` | `Integer` | ✓ | Number of times the invitation link was clicked |
577
+ | `form_submission_data` | `Hash \| nil` | | Invitation form data submitted by the user, including invitee identifiers (such as email addresses, phone numbers, or internal IDs) and the values of any custom fields. |
578
+ | `configuration_attributes` | `Hash \| nil` | | Deprecated: Use form_submission_data instead. Contains the same data. |
579
+ | `created_at` | `String` | ✓ | ISO 8601 timestamp when the invitation was created |
580
+ | `deactivated` | `Boolean` | ✓ | Whether this invitation has been revoked or expired |
581
+ | `delivery_count` | `Integer` | ✓ | Number of times the invitation was sent (including reminders) |
582
+ | `delivery_types` | `Array<String>` | ✓ | Channels used to deliver: "email", "phone", "share", "internal" |
583
+ | `foreign_creator_id` | `String` | ✓ | Your internal user ID for the person who created this invitation |
584
+ | `invitation_type` | `String` | ✓ | Type: "single_use", "multi_use", or "autojoin" |
585
+ | `status` | `String` | ✓ | Current status: queued, sending, sent, delivered, accepted, shared |
586
+ | `target` | `Array<Hash>` | ✓ | List of invitation recipients with their contact info and status |
587
+ | `views` | `Integer` | ✓ | Number of times the invitation page was viewed |
588
+ | `groups` | `Array<Hash>` | ✓ | Scopes (teams/orgs) this invitation grants access to |
589
+ | `expired` | `Boolean` | ✓ | Whether this invitation has passed its expiration date |
590
+ | `expires` | `String` | | ISO 8601 timestamp when this invitation expires |
591
+ | `inviter` | `Hash (Inviter)` | | Information about who sent the invitation |
592
+
593
+ ### `InvitationTarget`
594
+
595
+ Target recipient of an invitation (from API response)
596
+
597
+ | Field | Type | Required | Description |
598
+ | ------------ | -------- | -------- | ----------------------------------------------------------------------- |
599
+ | `type` | `String` | ✓ | Delivery channel: "email", "phone", "share", or "internal" |
600
+ | `value` | `String` | ✓ | Target address: email, phone number with country code, or share link ID |
601
+ | `name` | `String` | | Display name of the recipient |
602
+ | `avatar_url` | `String` | | Avatar URL for the recipient |
603
+ | `status` | `String` | | Delivery status for this specific target |
604
+
605
+ ### `InvitationScope`
606
+
607
+ Scope/group that the invitation grants access to (from API response)
608
+
609
+ | Field | Type | Required | Description |
610
+ | ------------ | -------- | -------- | ------------------------------------------------------ |
611
+ | `id` | `String` | ✓ | Vortex internal UUID for this scope record |
612
+ | `account_id` | `String` | ✓ | Your Vortex account ID |
613
+ | `group_id` | `String` | ✓ | Your internal scope/group identifier |
614
+ | `type` | `String` | ✓ | Scope type (e.g., "team", "organization", "workspace") |
615
+ | `name` | `String` | ✓ | Display name for the scope |
616
+ | `created_at` | `String` | ✓ | ISO 8601 timestamp when the scope was created |
617
+
618
+ ### `InvitationAcceptance`
619
+
620
+ Details about an invitation acceptance event
621
+
622
+ | Field | Type | Required | Description |
623
+ | --------------- | --------- | -------- | ----------------------------------------------- |
624
+ | `id` | `String` | ✓ | Unique identifier for this acceptance record |
625
+ | `invitation_id` | `String` | ✓ | ID of the invitation that was accepted |
626
+ | `email` | `String` | | Email of the user who accepted |
627
+ | `phone` | `String` | | Phone of the user who accepted |
628
+ | `name` | `String` | | Name of the user who accepted |
629
+ | `is_existing` | `Boolean` | | Whether the user already had an account |
630
+ | `created_at` | `String` | ✓ | ISO 8601 timestamp when the acceptance occurred |
631
+
632
+ ### `Inviter`
633
+
634
+ Information about the user who sent an invitation
635
+
636
+ | Field | Type | Required | Description |
637
+ | ------------ | -------- | -------- | ------------------------------------- |
638
+ | `id` | `String` | ✓ | Your internal user ID for the inviter |
639
+ | `email` | `String` | | Email address of the inviter |
640
+ | `name` | `String` | | Display name of the inviter |
641
+ | `avatar_url` | `String` | | Avatar URL of the inviter |
642
+
643
+ ### `AutojoinDomain`
644
+
645
+ Autojoin domain configuration - users with matching email domains automatically join
646
+
647
+ | Field | Type | Required | Description |
648
+ | -------- | -------- | -------- | ------------------------------------------------------ |
649
+ | `id` | `String` | ✓ | Unique identifier for this autojoin configuration |
650
+ | `domain` | `String` | ✓ | Email domain that triggers autojoin (e.g., "acme.com") |
651
+
652
+ ### `AutojoinDomainsResponse`
653
+
654
+ Response from get_autojoin_domains()
655
+
656
+ | Field | Type | Required | Description |
657
+ | --------- | ----------------------- | -------- | ----------------------------------- |
658
+ | `domains` | `Array<AutojoinDomain>` | ✓ | List of configured autojoin domains |
659
+
660
+ ### `SyncInternalInvitationResponse`
661
+
662
+ Response from sync_internal_invitation()
663
+
664
+ | Field | Type | Required | Description |
665
+ | ------------ | ------------------------- | -------- | ------------------------------------------------------------------- |
666
+ | `invitation` | `Hash (InvitationResult)` | ✓ | The created or updated invitation |
667
+ | `created` | `Boolean` | ✓ | true if a new invitation was created, false if existing was updated |
668
+
669
+ ### `VortexWebhookEvent`
670
+
671
+ Webhook event payload delivered to your endpoint
672
+
673
+ | Field | Type | Required | Description |
674
+ | ----------- | -------- | -------- | ---------------------------------------------------------- |
675
+ | `id` | `String` | ✓ | Unique identifier for this webhook delivery |
676
+ | `type` | `String` | ✓ | Event type (e.g., "invitation.accepted", "member.created") |
677
+ | `timestamp` | `String` | ✓ | ISO 8601 timestamp when the event occurred |
678
+ | `data` | `Hash` | ✓ | Event-specific payload data |
679
+
680
+ </details>
681
+
682
+ ## Webhooks
683
+
684
+ Webhooks let your server receive real-time notifications when events happen in Vortex. Use them to sync invitation state with your database, trigger onboarding flows, update your CRM, or send internal notifications.
685
+
686
+ ### Setup
687
+
688
+ 1. Go to your Vortex dashboard → Integrations → Webhooks tab
689
+ 2. Click "Add Webhook"
690
+ 3. Enter your endpoint URL (must be HTTPS in production)
691
+ 4. Copy the signing secret — you'll use this to verify webhook signatures
692
+ 5. Select which events you want to receive
693
+
694
+ ### Verifying Webhooks
695
+
696
+ Always verify webhook signatures using `Vortex::Webhooks.verify_signature()` to ensure requests are from Vortex.
697
+ The signature is sent in the `X-Vortex-Signature` header.
698
+
699
+ ### Example: Rails webhook handler
700
+
701
+ ```ruby
307
702
  class WebhooksController < ApplicationController
308
703
  skip_before_action :verify_authenticity_token
309
704
 
310
- def create
311
- payload = request.body.read
705
+ def vortex
706
+ webhooks = Vortex::Webhooks.new(ENV['VORTEX_WEBHOOK_SECRET'])
707
+
708
+ payload = request.raw_post
312
709
  signature = request.headers['X-Vortex-Signature']
313
710
 
314
- begin
315
- event = webhooks.construct_event(payload, signature)
316
- rescue Vortex::WebhookSignatureError
317
- head :bad_request
318
- return
711
+ # Verify the signature
712
+ unless webhooks.verify_signature(payload, signature)
713
+ return render json: { error: 'Invalid signature' }, status: 400
319
714
  end
320
715
 
321
- case event
322
- when Vortex::WebhookEvent
323
- Rails.logger.info "Webhook event: #{event.type}"
324
- when Vortex::AnalyticsEvent
325
- Rails.logger.info "Analytics event: #{event.name}"
716
+ # Parse the event
717
+ event = webhooks.parse_event(payload)
718
+
719
+ case event['type']
720
+ when 'invitation.accepted'
721
+ # User accepted an invitation — activate their account
722
+ Rails.logger.info "Accepted: #{event['data']}"
723
+ when 'member.created'
724
+ # New member joined via invitation
725
+ Rails.logger.info "New member: #{event['data']}"
326
726
  end
327
727
 
328
- head :ok
728
+ render json: { received: true }
329
729
  end
330
730
  end
731
+
331
732
  ```
332
733
 
333
- ### Event Type Constants
734
+ ### Common Use Cases
334
735
 
335
- ```ruby
336
- if event.type == Vortex::WebhookEventTypes::INVITATION_ACCEPTED
337
- # Handle invitation accepted
338
- end
339
- ```
736
+ **Activate users on acceptance**
737
+
738
+ When invitation.accepted fires, mark the user as active in your database and trigger your onboarding flow.
739
+
740
+ **Track invitation performance**
741
+
742
+ Monitor email.delivered, email.opened, and link.clicked events to measure invitation funnel metrics.
743
+
744
+ **Sync team membership**
745
+
746
+ Use member.created and group.member.added to keep your internal membership records in sync.
747
+
748
+ **Alert on delivery issues**
749
+
750
+ Watch for email.bounced events to proactively reach out via alternative channels.
751
+
752
+ ### Supported Events
753
+
754
+ | Event | Description |
755
+ | ---------------------------- | ---------------------------------------------------- |
756
+ | `invitation.created` | A new invitation was created |
757
+ | `invitation.accepted` | An invitation was accepted by the recipient |
758
+ | `invitation.deactivated` | An invitation was deactivated (revoked or expired) |
759
+ | `invitation.email.delivered` | Invitation email was successfully delivered |
760
+ | `invitation.email.bounced` | Invitation email bounced (invalid address) |
761
+ | `invitation.email.opened` | Recipient opened the invitation email |
762
+ | `invitation.link.clicked` | Recipient clicked the invitation link |
763
+ | `invitation.reminder.sent` | A reminder email was sent for a pending invitation |
764
+ | `member.created` | A new member was created from an accepted invitation |
765
+ | `group.member.added` | A member was added to a scope/group |
766
+ | `deployment.created` | A new deployment configuration was created |
767
+ | `deployment.deactivated` | A deployment was deactivated |
768
+ | `abtest.started` | An A/B test was started |
769
+ | `abtest.winner_declared` | An A/B test winner was declared |
770
+ | `email.complained` | Recipient marked the email as spam |
771
+
772
+ ## Error Handling
773
+
774
+ All SDK errors extend `VortexError`.
340
775
 
341
- Bug reports and pull requests are welcome on GitHub at https://github.com/vortexsoftware/vortex-ruby-sdk.
776
+ | Error | Description |
777
+ | ------------- | ---------------------------------------------------------------------------------------- |
778
+ | `VortexError` | Raised for validation errors (e.g., missing API key, invalid parameters) or API failures |
342
779
 
343
- ## License
780
+ ---
344
781
 
345
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
782
+ <!-- Generated from SDK v1.18.0 manifest -->
data/lib/vortex/client.rb CHANGED
@@ -122,7 +122,7 @@ module Vortex
122
122
 
123
123
  public
124
124
 
125
- def generate_jwt(params)
125
+ def generate_jwt(params, options = {})
126
126
  user = params[:user]
127
127
  attributes = params[:attributes]
128
128
 
@@ -138,7 +138,9 @@ module Vortex
138
138
  # Convert to UUID string format (same as uuidStringify in Node.js)
139
139
  id = format_uuid(decoded_bytes)
140
140
 
141
- expires = Time.now.to_i + 3600
141
+ raw_expires = options[:expires_in] || options[:expiresIn]
142
+ expires_in_seconds = raw_expires ? parse_expires_in(raw_expires) : 2592000 # 30 days
143
+ expires = Time.now.to_i + expires_in_seconds
142
144
 
143
145
  # Step 1: Derive signing key from API key + ID (same as Node.js)
144
146
  signing_key = OpenSSL::HMAC.digest('sha256', key, id)
@@ -207,7 +209,7 @@ module Vortex
207
209
  # @param payload [Hash] Data to sign (user, component, scope, vars, etc.)
208
210
  # At minimum, include user[:id] for secure invitation attribution.
209
211
  # @param options [Hash] Optional configuration
210
- # @option options [String, Integer] :expires_in Expiration time (default: 5 minutes)
212
+ # @option options [String, Integer] :expires_in Expiration time (default: 30 days)
211
213
  # Can be a duration string ("5m", "1h", "24h", "7d") or seconds as integer.
212
214
  # @return [String] Signed JWT token
213
215
  # @raise [VortexError] If API key format is invalid or token generation fails
@@ -223,7 +225,7 @@ module Vortex
223
225
  # vars: { company_name: 'Acme' }
224
226
  # })
225
227
  #
226
- # @example Custom expiration (default is 5 minutes)
228
+ # @example Custom expiration (default is 30 days)
227
229
  # token = client.generate_token(payload, { expires_in: '1h' })
228
230
  # token = client.generate_token(payload, { expires_in: 3600 }) # seconds
229
231
  def generate_token(payload, options = nil)
@@ -241,7 +243,7 @@ module Vortex
241
243
  end
242
244
 
243
245
  # Parse expiration
244
- expires_in_seconds = 5 * 60 # Default: 5 minutes
246
+ expires_in_seconds = 30 * 24 * 60 * 60 # Default: 30 days
245
247
  if options
246
248
  options = symbolize_keys(options)
247
249
  raw_expires = options[:expires_in] || options[:expiresIn]
@@ -310,7 +312,7 @@ module Vortex
310
312
  # @return [Integer] Expiration time in seconds
311
313
  # @raise [VortexError] If format is invalid
312
314
  def parse_expires_in(expires_in)
313
- return 5 * 60 if expires_in.nil?
315
+ return 30 * 24 * 60 * 60 if expires_in.nil?
314
316
 
315
317
  if expires_in.is_a?(Integer)
316
318
  raise VortexError, "Invalid expires_in value: #{expires_in}. Numeric expires_in must be a positive integer number of seconds." if expires_in <= 0
@@ -684,7 +686,7 @@ module Vortex
684
686
  # @param scope [String] The scope identifier (customer's group ID)
685
687
  # @param scope_type [String] The type of scope (e.g., "organization", "team")
686
688
  # @param domains [Array<String>] Array of domains to configure for autojoin
687
- # @param widget_id [String] The widget configuration ID
689
+ # @param component_id [String] The component ID
688
690
  # @param scope_name [String, nil] Optional display name for the scope
689
691
  # @param metadata [Hash, nil] Optional metadata to attach to the invitation
690
692
  # @return [Hash] Response with :autojoin_domains array and :invitation
@@ -695,20 +697,20 @@ module Vortex
695
697
  # 'acme-org',
696
698
  # 'organization',
697
699
  # ['acme.com', 'acme.org'],
698
- # 'widget-123',
700
+ # 'component-123',
699
701
  # 'Acme Corporation'
700
702
  # )
701
- def configure_autojoin(scope, scope_type, domains, widget_id, scope_name = nil, metadata = nil)
703
+ def configure_autojoin(scope, scope_type, domains, component_id, scope_name = nil, metadata = nil)
702
704
  raise VortexError, 'scope is required' if scope.nil? || scope.empty?
703
705
  raise VortexError, 'scope_type is required' if scope_type.nil? || scope_type.empty?
704
- raise VortexError, 'widget_id is required' if widget_id.nil? || widget_id.empty?
706
+ raise VortexError, 'component_id is required' if component_id.nil? || component_id.empty?
705
707
  raise VortexError, 'domains must be an array' unless domains.is_a?(Array)
706
708
 
707
709
  body = {
708
710
  scope: scope,
709
711
  scopeType: scope_type,
710
712
  domains: domains,
711
- widgetId: widget_id
713
+ componentId: component_id
712
714
  }
713
715
 
714
716
  body[:scopeName] = scope_name if scope_name
data/lib/vortex/types.rb CHANGED
@@ -81,6 +81,9 @@ module Vortex
81
81
  id: String,
82
82
  accountId: String,
83
83
  clickThroughs: Integer,
84
+ # Invitation form data submitted by the user, including email addresses of invitees and the values of any custom fields.
85
+ formSubmissionData: Hash,
86
+ # @deprecated Use formSubmissionData instead. Contains the same data.
84
87
  configurationAttributes: Hash,
85
88
  attributes: Hash,
86
89
  createdAt: String,
@@ -94,9 +97,8 @@ module Vortex
94
97
  target: Array, # of INVITATION_TARGET structures
95
98
  views: Integer,
96
99
  widgetConfigurationId: String,
97
- projectId: String,
98
100
  groups: Array, # of INVITATION_GROUP structures
99
- accepts: Array, # of acceptance structures
101
+ accepts: Array, # of acceptance structures (optional)
100
102
  expired: :boolean,
101
103
  expires: String # ISO 8601 timestamp (optional)
102
104
  }.freeze
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Vortex
4
- VERSION = '1.15.0'
4
+ VERSION = '1.18.0.pre.20260427181620'.freeze
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vortex-ruby-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.15.0
4
+ version: 1.18.0.pre.20260427181620
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vortex Software
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-04-08 00:00:00.000000000 Z
11
+ date: 2026-04-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -182,9 +182,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
182
182
  version: 3.0.0
183
183
  required_rubygems_version: !ruby/object:Gem::Requirement
184
184
  requirements:
185
- - - ">="
185
+ - - ">"
186
186
  - !ruby/object:Gem::Version
187
- version: '0'
187
+ version: 1.3.1
188
188
  requirements: []
189
189
  rubygems_version: 3.4.19
190
190
  signing_key: