Skip to main content
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. You focus on your product; Vortex handles the growth mechanics.

Installation

npm install @teamvortexsoftware/vortex-node-22-sdk

Quick Start

Generate a secure token for Vortex components
import { Vortex } from '@teamvortexsoftware/vortex-node-22-sdk';

const vortex = new Vortex(process.env.VORTEX_API_KEY!);

// Generate a token for the current user
const token = vortex.generateToken({
  user: { id: 'user-123', email: 'user@example.com' }
});

// Pass the token to your frontend component
// <VortexInvite token={token} />

Integration Flow

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.
1

Install the backend SDK

Add this SDK to your Node.js server
npm install @teamvortexsoftware/vortex-node-22-sdk
2

Initialize the client

Create a Vortex client with your API key (keep this on the server!)
import { Vortex } from '@teamvortexsoftware/vortex-node-22-sdk';

const vortex = new Vortex(process.env.VORTEX_API_KEY);
3

Generate a token for the current user

When a user loads a page with a Vortex component, generate a signed token on your server
const token = vortex.generateToken({ user: { id: currentUser.id } });
4

Pass the token to your frontend

Include the token in your page response or API response
res.json({ vortexToken: token, ...otherData });
5

Render a Vortex component with the token

Use the React/Angular/Web Component with the token
import { VortexInvite } from '@teamvortexsoftware/vortex-react';

<VortexInvite token={vortexToken} />
6

Vortex handles the rest

The component securely communicates with Vortex servers, displays the invitation UI, sends emails/SMS, tracks conversions, and reports analytics

Methods

Core Methods

generateToken()

Sign a payload 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.
generateToken(payload: GenerateTokenData, options?: GenerateTokenOptions | undefined): string
Parameters:
NameTypeRequiredDescription
payloadGenerateTokenDataData to sign (user, component, scope, vars, etc.)
options`GenerateTokenOptionsundefined`Optional configuration (expiresIn)
Returns: string — Signed JWT token string
// --- Backend (Node.js/Express) ---
import { Vortex } from '@teamvortexsoftware/vortex-node-22-sdk';

const vortex = new Vortex(process.env.VORTEX_API_KEY);

app.get('/api/invite-token', (req, res) => {
  const token = vortex.generateToken({
    user: { id: req.user.id, email: req.user.email }
  });
  res.json({ token });
});

// --- Frontend (React) ---
import { VortexInvite } from '@teamvortexsoftware/vortex-react';

function InvitePage() {
  const { data } = useFetch('/api/invite-token');
  return <VortexInvite token={data.token} />;
}

getInvitation()

Get a single invitation by ID
getInvitation(invitationId: string): Promise<InvitationResult>
Parameters:
NameTypeRequiredDescription
invitationIdstringThe invitation ID to retrieve
Returns: Promise&lt;InvitationResult&gt; — The invitation details
const invitation = await vortex.getInvitation('inv-123');
console.log(invitation.status);

acceptInvitation()

Accept a single invitation This is the recommended method for accepting invitations.
acceptInvitation(invitationId: string, user: AcceptUser): Promise<InvitationResult>
Parameters:
NameTypeRequiredDescription
invitationIdstringSingle invitation ID to accept
userAcceptUserUser object with email or phone (and optional name)
Returns: Promise&lt;InvitationResult&gt; — Invitation result
await vortex.acceptInvitation('inv-123', { email: 'user@example.com', name: 'John' });

generateJwt()

Generate a JWT token for a user
generateJwt(params: { [key: string]: any; user: User; }, options?: GenerateJwtOptions | undefined): string
Parameters:
NameTypeRequiredDescription
params{ [key: string]: any; user: User; }Object containing user and optional additional properties
options`GenerateJwtOptionsundefined`Optional configuration (expiresIn)
Returns: string — JWT token string
const token = vortex.generateJwt({
  user: {
    id: "user-123",
    email: "user@example.com",
    adminScopes: ['autojoin']
  }
});

// With custom expiration
const tokenWithCustomExpiry = vortex.generateJwt(
  { user: { id: "user-123", email: "user@example.com" } },
  { expiresIn: '24h' }  // Custom expiration (default: 30 days)
);

getInvitationsByTarget()

Get invitations by target (email, username, or phone number)
getInvitationsByTarget(targetType: "email" | "username" | "phoneNumber", targetValue: string): Promise<InvitationResultBase[]>
Parameters:
NameTypeRequiredDescription
targetType`“email""username""phoneNumber”`The type of target identifier
targetValuestringThe target value to search for
Returns: Promise&lt;InvitationResultBase[]&gt; — Array of invitation results matching the target
const invitations = await vortex.getInvitationsByTarget('email', 'user@example.com');

revokeInvitation()

Revoke (delete) an invitation
revokeInvitation(invitationId: string): Promise<{}>
Parameters:
NameTypeRequiredDescription
invitationIdstringThe invitation ID to revoke
Returns: Promise&lt;{}&gt; — Empty object on success
await vortex.revokeInvitation('inv-123');

acceptInvitations()

Accept one or more invitations for a user
acceptInvitations(invitationIds: string[], userOrTarget: AcceptUser | InvitationTarget | InvitationTarget[]): Promise<InvitationResult>
Parameters:
NameTypeRequiredDescription
invitationIdsstring[]Array of invitation IDs to accept
userOrTarget`AcceptUserInvitationTargetInvitationTarget[]`User object with email or phone, or legacy target format
Returns: Promise&lt;InvitationResult&gt; — The accepted invitation result
await vortex.acceptInvitations(['inv-123'], { email: 'user@example.com' });

deleteInvitationsByGroup()

Delete all invitations for a specific group
deleteInvitationsByGroup(groupType: string, groupId: string): Promise<{}>
Parameters:
NameTypeRequiredDescription
groupTypestringThe type of group (e.g., “team”, “organization”)
groupIdstringThe group identifier
Returns: Promise&lt;{}&gt; — Empty object

getInvitationsByGroup()

Get all invitations for a specific group
getInvitationsByGroup(groupType: string, groupId: string): Promise<InvitationResult[]>
Parameters:
NameTypeRequiredDescription
groupTypestringThe type of group (e.g., “team”, “organization”)
groupIdstringThe group identifier
Returns: Promise&lt;InvitationResult[]&gt; — Array of invitation results

deleteInvitationsByScope()

Delete all invitations for a specific scope
deleteInvitationsByScope(scopeType: string, scope: string): Promise<{}>
Parameters:
NameTypeRequiredDescription
scopeTypestringThe type of scope (e.g., “team”, “organization”)
scopestringThe scope identifier (customer’s scope ID)
Returns: Promise&lt;{}&gt; — Empty object
await vortex.deleteInvitationsByScope('team', 'team-123');

getInvitationsByScope()

Get all invitations for a specific scope
getInvitationsByScope(scopeType: string, scope: string): Promise<InvitationResult[]>
Parameters:
NameTypeRequiredDescription
scopeTypestringThe type of scope (e.g., “team”, “organization”)
scopestringThe scope identifier (customer’s scope ID)
Returns: Promise&lt;InvitationResult[]&gt; — Array of invitation results
const invitations = await vortex.getInvitationsByScope('team', 'team-123');

reinvite()

Resend an invitation (reinvite)
reinvite(invitationId: string): Promise<InvitationResult>
Parameters:
NameTypeRequiredDescription
invitationIdstringThe invitation ID to resend
Returns: Promise&lt;InvitationResult&gt; — The updated invitation
const invitation = await vortex.reinvite('inv-123');

getAutojoinDomains()

Get autojoin domains configured for a specific scope
getAutojoinDomains(scopeType: string, scope: string): Promise<AutojoinDomainsResponse>
Parameters:
NameTypeRequiredDescription
scopeTypestringThe type of scope (e.g., “organization”, “team”, “project”)
scopestringThe scope identifier (customer’s group ID)
Returns: Promise&lt;AutojoinDomainsResponse&gt; — Autojoin domains and associated invitation
const result = await vortex.getAutojoinDomains('organization', 'acme-org');
console.log(result.autojoinDomains); // [{ id: '...', domain: 'acme.com' }]

configureAutojoin()

Configure autojoin domains for a specific scopeThis 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).
configureAutojoin(params: ConfigureAutojoinRequest): Promise<AutojoinDomainsResponse>
Parameters:
NameTypeRequiredDescription
paramsConfigureAutojoinRequestConfiguration parameters
Returns: Promise&lt;AutojoinDomainsResponse&gt; — Updated autojoin domains and associated invitation
const result = await vortex.configureAutojoin({
  scope: 'acme-org',
  scopeType: 'organization',
  scopeName: 'Acme Corporation',
  domains: ['acme.com', 'acme.org'],
  componentId: 'component-123',
});

syncInternalInvitation()

Sync an internal invitation action (accept or decline)This method notifies Vortex that an internal invitation was accepted or declined within your application, so Vortex can update the invitation status accordingly.
syncInternalInvitation(params: SyncInternalInvitationRequest): Promise<SyncInternalInvitationResponse>
Parameters:
NameTypeRequiredDescription
paramsSyncInternalInvitationRequestSync parameters
Returns: Promise&lt;SyncInternalInvitationResponse&gt; — Object with processed count and invitation IDs
const result = await vortex.syncInternalInvitation({
  creatorId: 'user-123',
  targetValue: 'user-456',
  action: 'accepted',
  componentId: 'component-uuid-789',
});
console.log(`Processed ${result.processed} invitations`);

Types

InvitationTarget

Target recipient of an invitation
FieldTypeRequiredDescription
type`“email""phone""share""internal”`Delivery channel type
valuestringTarget address (email, phone number, or share link ID)
name`stringnullundefined`Display name of the person being invited
avatarUrl`stringnullundefined`Avatar URL for the person being invited

ScopeInput

ScopeInput is used when creating JWTs - represents customer’s scope data Supports both ‘id’ (legacy) and ‘groupId’ (preferred) for backward compatibility
FieldTypeRequiredDescription
typestringScope type (e.g., ‘team’, ‘organization’, ‘workspace’)
id`stringundefined`⚠️ Deprecated: Use scopeId instead
scopeId`stringundefined`The scope identifier (preferred)
groupId`stringundefined`⚠️ Deprecated: Use scopeId instead
namestringDisplay name for the scope

GroupInput

FieldTypeRequiredDescription
typestringScope type (e.g., ‘team’, ‘organization’, ‘workspace’)
id`stringundefined`⚠️ Deprecated: Use scopeId instead
scopeId`stringundefined`The scope identifier (preferred)
groupId`stringundefined`⚠️ Deprecated: Use scopeId instead
namestringDisplay name for the scope

InvitationScope

InvitationScope represents a scope in API responses This matches the MemberGroups table structure from the API
FieldTypeRequiredDescription
idstringVortex internal UUID
accountIdstringVortex account ID
scopeIdstringThe customer’s scope ID (preferred)
groupIdstring⚠️ Deprecated: Use scopeId instead
typestringScope type (e.g., ‘workspace’, ‘team’)
namestringDisplay name for the scope
createdAtstringISO timestamp when the scope was created

InvitationGroup

FieldTypeRequiredDescription
idstringVortex internal UUID
accountIdstringVortex account ID
scopeIdstringThe customer’s scope ID (preferred)
groupIdstring⚠️ Deprecated: Use scopeId instead
typestringScope type (e.g., ‘workspace’, ‘team’)
namestringDisplay name for the scope
createdAtstringISO timestamp when the scope was created

InvitationAcceptance

Record of a user accepting an invitation
FieldTypeRequiredDescription
idstringUnique acceptance record ID
accountIdstringVortex account ID
acceptedAtstringISO timestamp when the invitation was accepted
targetInvitationTargetThe user who accepted the invitation

InvitationResultBase

Base invitation result without target information. Used by endpoints like getInvitationsByTarget where target is already known.
FieldTypeRequiredDescription
idstringUnique invitation identifier
accountIdstringVortex account ID that owns this invitation
clickThroughsnumberNumber of times the invitation link was clicked
formSubmissionData`Record<string, any>null`Data submitted through the invitation form when the invitation was created.
Contains standard fields (like email) as well as any custom fields configured
on your invitation widget. Useful for capturing additional context about
invitations, such as personal messages, referral codes, or other user-provided
information.
configurationAttributes`Record<string, any>null`⚠️ Deprecated: Use formSubmissionData instead. This field contains the same data.
attributes`Record<string, any>null`Custom attributes attached to this invitation
createdAtstringISO timestamp when the invitation was created
deactivatedbooleanWhether the invitation has been deactivated
deliveryCountnumberNumber of delivery attempts made
deliveryTypes`(“email""phone""share""internal”)[]`Delivery channels used for this invitation
foreignCreatorIdstringYour user ID who created this invitation
invitationType`“single_use""multi_use""autojoin”`Type of invitation: single_use (one accept), multi_use (unlimited), or autojoin (domain-based)
modifiedAt`stringnull`ISO timestamp when the invitation was last modified
status`“queued""sending""sent""delivered""accepted""shared""unfurled""accepted_elsewhere”`Current status of the invitation
viewsnumberNumber of times the invitation was viewed
widgetConfigurationIdstringID of the component configuration used
scopesInvitationScope[]Scopes associated with this invitation (preferred)
groupsInvitationScope[]⚠️ Deprecated: Use scopes instead
accepts`InvitationAcceptance[]undefined`List of users who accepted this invitation
expiredbooleanWhether the invitation has expired
expires`stringundefined`ISO timestamp when the invitation expires
source`stringundefined`Source identifier (e.g., campaign name)
subtype`stringnullundefined`Customer-defined subtype for categorizing this invitation (e.g., pymk, find-friends, profile-button)
creatorName`stringnullundefined`Display name of the invitation creator
creatorAvatarUrl`stringnullundefined`Avatar URL of the invitation creator

InvitationResult

Full invitation result including target information. Used by getInvitation, getInvitationsByScope, and other endpoints that return targets.
FieldTypeRequiredDescription
idstringUnique invitation identifier
accountIdstringVortex account ID that owns this invitation
clickThroughsnumberNumber of times the invitation link was clicked
formSubmissionData`Record<string, any>null`Data submitted through the invitation form when the invitation was created.
Contains standard fields (like email) as well as any custom fields configured
on your invitation widget. Useful for capturing additional context about
invitations, such as personal messages, referral codes, or other user-provided
information.
configurationAttributes`Record<string, any>null`⚠️ Deprecated: Use formSubmissionData instead. This field contains the same data.
attributes`Record<string, any>null`Custom attributes attached to this invitation
createdAtstringISO timestamp when the invitation was created
deactivatedbooleanWhether the invitation has been deactivated
deliveryCountnumberNumber of delivery attempts made
deliveryTypes`(“email""phone""share""internal”)[]`Delivery channels used for this invitation
foreignCreatorIdstringYour user ID who created this invitation
invitationType`“single_use""multi_use""autojoin”`Type of invitation: single_use (one accept), multi_use (unlimited), or autojoin (domain-based)
modifiedAt`stringnull`ISO timestamp when the invitation was last modified
status`“queued""sending""sent""delivered""accepted""shared""unfurled""accepted_elsewhere”`Current status of the invitation
viewsnumberNumber of times the invitation was viewed
widgetConfigurationIdstringID of the component configuration used
scopesInvitationScope[]Scopes associated with this invitation (preferred)
groupsInvitationScope[]⚠️ Deprecated: Use scopes instead
accepts`InvitationAcceptance[]undefined`List of users who accepted this invitation
expiredbooleanWhether the invitation has expired
expires`stringundefined`ISO timestamp when the invitation expires
source`stringundefined`Source identifier (e.g., campaign name)
subtype`stringnullundefined`Customer-defined subtype for categorizing this invitation (e.g., pymk, find-friends, profile-button)
creatorName`stringnullundefined`Display name of the invitation creator
creatorAvatarUrl`stringnullundefined`Avatar URL of the invitation creator
targetInvitationTarget[]List of invitation targets (recipients)

AcceptUser

User type for accepting invitations Requires either email or phone (or both)
FieldTypeRequiredDescription
email`stringundefined`Email address of the accepting user
phone`stringundefined`Phone number of the accepting user
name`stringundefined`Display name of the accepting user
isExisting`booleanundefined`Whether the accepting user is an existing user in your system.
Set to true if the user was already registered before accepting the invitation.
Set to false if this is a new user signup.
Leave undefined if unknown.
Used for analytics to track new vs existing user conversions.

AcceptInvitationRequest

Request body for accepting invitations
FieldTypeRequiredDescription
invitationIdsstring[]Array of invitation IDs to accept
userAcceptUserInformation about the user accepting the invitations

AcceptInvitationRequestLegacy

Legacy request body for accepting invitations
FieldTypeRequiredDescription
invitationIdsstring[]Array of invitation IDs to accept
targetInvitationTargetTarget information (legacy format)

SyncInternalInvitationRequest

Request body for syncing an internal invitation action
FieldTypeRequiredDescription
creatorIdstringThe inviter’s user ID
targetValuestringThe invitee’s user ID
action`“accepted""declined”`The action taken: “accepted” or “declined”
componentIdstringThe widget component UUID

SyncInternalInvitationResponse

Response from syncing an internal invitation action
FieldTypeRequiredDescription
processednumberNumber of invitations processed
invitationIdsstring[]IDs of the invitations that were processed

User

User type for JWT generation Only id is required. Email is optional but recommended for invitation attribution.
FieldTypeRequiredDescription
idstringUnique user identifier in your system
email`stringundefined`User’s email address (optional, used for reply-to in invitation emails)
name`stringundefined`User’s display name (preferred)
avatarUrl`stringundefined`User’s avatar URL (preferred)
userName`stringundefined`⚠️ Deprecated: Use name instead
userAvatarUrl`stringundefined`⚠️ Deprecated: Use avatarUrl instead
adminScopes`string[]undefined`Admin scope permissions (e.g., [‘autojoin’])
allowedEmailDomains`string[]undefined`Optional list of allowed email domains for invitation restrictions.
When present, email invitations will only be allowed to addresses
matching one of these domains (e.g., [‘acme.com’, ‘acme.org’]).
Domain matching is case-insensitive.

AutojoinDomain

Autojoin domain configuration Allows users with matching email domains to automatically join a scope
FieldTypeRequiredDescription
idstringUnique domain configuration ID
domainstringEmail domain (e.g., ‘acme.com’)

AutojoinDomainsResponse

Response from autojoin API endpoints
FieldTypeRequiredDescription
autojoinDomainsAutojoinDomain[]List of configured autojoin domains
invitation`InvitationResultnull`The autojoin invitation if one exists, null otherwise

ConfigureAutojoinRequest

Request body for configuring autojoin domains
FieldTypeRequiredDescription
scopestringScope ID in your system
scopeTypestringType of scope (e.g., ‘team’, ‘organization’)
scopeName`stringundefined`Display name for the scope
domainsstring[]List of email domains that can autojoin (e.g., [‘acme.com’, ‘acme.org’])
componentIdstringComponent ID to use for autojoin invitations
metadata`Record<string, any>undefined`Custom metadata to attach to autojoin invitations

Inviter

Information about the user creating the invitation (the inviter)
FieldTypeRequiredDescription
userIdstringThe internal user ID of the person creating the invitation (from your system)
userEmail`stringundefined`The email address of the person creating the invitation
name`stringundefined`The display name of the person creating the invitation (preferred)
avatarUrl`stringundefined`Avatar URL for the person creating the invitation (preferred)
userName`stringundefined`⚠️ Deprecated: Use name instead
userAvatarUrl`stringundefined`⚠️ Deprecated: Use avatarUrl instead

GenerateTokenUser

User object for generateToken - flexible structure Only id is required for secure attribution
FieldTypeRequiredDescription
id`stringnumber`User ID - required for secure attribution
email`stringundefined`User’s email address
name`stringundefined`User’s display name
phone`stringundefined`User’s phone number
avatarUrl`stringundefined`User’s avatar URL

GenerateTokenData

Payload structure for generateToken method All fields are optional - sign only what you need
FieldTypeRequiredDescription
component`stringundefined`Widget component ID
trigger`stringundefined`DOM selector for trigger button
embed`stringundefined`DOM selector for embed container
user`GenerateTokenUserundefined`User information - include id for secure attribution
scope`stringundefined`Scope/workspace identifier
vars`Record<string, any>undefined`Template variables for customization

GenerateJwtOptions

Options for generateJwt method
FieldTypeRequiredDescription
expiresIn`stringnumberundefined`JWT expiration time
  • String format: ‘5m’, ‘1h’, ‘24h’, ‘7d’ (minutes, hours, days)
  • Number format: seconds
  • Default: 30 days (2592000 seconds) |

GenerateTokenOptions

Options for generateToken method
FieldTypeRequiredDescription
expiresIn`stringnumberundefined`Token expiration time
  • String format: ‘5m’, ‘1h’, ‘24h’, ‘7d’ (minutes, hours, days)
  • Number format: seconds
  • Default: ‘30d’ (30 days) |

Webhooks

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.

Setup

  1. Go to your Vortex dashboard → Integrations → Webhooks tab
  2. Click “Add Webhook”
  3. Enter your endpoint URL (must be HTTPS in production)
  4. Copy the signing secret — you’ll use this to verify webhook signatures
  5. Select which events you want to receive

Example

Express.js webhook handler
import express from 'express';
import { VortexWebhooks, isWebhookEvent } from '@teamvortexsoftware/vortex-node-22-sdk';

const app = express();
const webhooks = new VortexWebhooks({
  secret: process.env.VORTEX_WEBHOOK_SECRET!,
});

// Important: Use raw body for signature verification
app.post('/webhooks/vortex', express.raw({ type: 'application/json' }), (req, res) => {
  try {
    const event = webhooks.constructEvent(
      req.body,
      req.headers['x-vortex-signature'] as string
    );

    if (isWebhookEvent(event)) {
      switch (event.type) {
        case 'invitation.accepted':
          // User accepted an invitation — activate their account
          console.log('Invitation accepted:', event.data.targetEmail);
          break;
        case 'member.created':
          // New member joined via invitation
          console.log('New member:', event.data);
          break;
      }
    }

    res.json({ received: true });
  } catch (err) {
    console.error('Webhook error:', err);
    res.status(400).send('Webhook Error');
  }
});

Events

EventDescription
invitation.createdA new invitation was created
invitation.acceptedAn invitation was accepted by the recipient
invitation.deactivatedAn invitation was deactivated (revoked or expired)
invitation.email.deliveredInvitation email was successfully delivered
invitation.email.bouncedInvitation email bounced (invalid address)
invitation.email.openedRecipient opened the invitation email
invitation.link.clickedRecipient clicked the invitation link
invitation.reminder.sentA reminder email was sent for a pending invitation
member.createdA new member was created from an accepted invitation
group.member.addedA member was added to a scope/group
deployment.createdA new deployment configuration was created
deployment.deactivatedA deployment was deactivated
abtest.startedAn A/B test was started
abtest.winner_declaredAn A/B test winner was declared
email.complainedRecipient marked the email as spam

Use Cases

  • Activate users on acceptance — When invitation.accepted fires, mark the user as active in your database and trigger your onboarding flow.
  • Track invitation performance — Monitor email.delivered, email.opened, and link.clicked events to measure invitation funnel metrics.
  • Sync team membership — Use member.created and group.member.added to keep your internal membership records in sync.
  • Alert on delivery issues — Watch for email.bounced events to proactively reach out via alternative channels.

Error Handling

ErrorDescription
VortexWebhookSignatureErrorThrown when webhook signature verification fails. Check that you are using the raw request body (not parsed JSON) and the correct signing secret from your Vortex dashboard.
ErrorThrown for validation errors (e.g., missing API key, invalid user ID in generateToken/generateJwt)

Resources