vortex-sdk 1.19.0

Vortex Rust SDK for authentication and invitation management
Documentation

vortex-rust-sdk

Version Language

Invitation infrastructure for modern apps

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. Learn more about Vortex →

Why This SDK?

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.

  • Keep your API key secure — it never touches the browser
  • Sign user identity for attribution — know who sent each invitation
  • Control what data components can access via scoped tokens
  • Verify webhook signatures for secure event handling

How It Works

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.

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│   Your Server   │     │  User Browser   │     │  Vortex Cloud   │
│    (this SDK)   │     │   (component)   │     │                 │
└────────┬────────┘     └────────┬────────┘     └────────┬────────┘
         │                       │                       │
         │  1. generate_token()  │                       │
         │◄──────────────────────│                       │
         │                       │                       │
         │  2. Return token      │                       │
         │──────────────────────►│                       │
         │                       │                       │
         │                       │  3. Component calls   │
         │                       │     API with token    │
         │                       │──────────────────────►│
         │                       │                       │
         │                       │  4. Render UI,        │
         │                       │     send invitations  │
         │                       │◄──────────────────────│
         │                       │                       │

Integration Flow

1. Install the backend SDK [backend]

Add this SDK to your Rust project

cargo add vortex-sdk

2. Initialize the client [backend]

Create a Vortex client with your API key (keep this on the server!)

use vortex_sdk::VortexClient;

let client = VortexClient::new(std::env::var("VORTEX_API_KEY")?);

3. Generate a token for the current user [backend]

When a user loads a page with a Vortex component, generate a signed token on your server

let payload = GenerateTokenPayload {
    user: Some(TokenUser { id: user_id.into(), ..Default::default() }),
    ..Default::default()
};
let token = client.generate_token(payload, None)?;

4. Pass the token to your frontend [backend]

Include the token in your response

Json(json!({ "vortex_token": token }))

5. Render a Vortex component with the token [frontend]

Use the React/Angular/Web Component with the token

import { VortexInvite } from "@teamvortexsoftware/vortex-react";

<VortexInvite token={vortexToken} />

6. Vortex handles the rest [vortex]

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

Security Model

⚠️ 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.

By signing tokens on your server, you:

  • Keep your API key secret (it never leaves your server)
  • Control exactly what user data is shared with components
  • Ensure invitations are attributed to real, authenticated users
  • Prevent abuse — users can only send invitations as themselves

When Signing is Optional

Token signing is controlled by your component configuration in the Vortex dashboard.


Quick Start

Generate a secure token for Vortex components

use vortex_sdk::{VortexClient, GenerateTokenPayload, TokenUser};

let client = VortexClient::new(std::env::var("VORTEX_API_KEY")?);

let payload = GenerateTokenPayload {
    user: Some(TokenUser { id: "user-123".into(), email: Some("user@example.com".into()), ..Default::default() }),
    ..Default::default()
};

let token = client.generate_token(payload, None)?;

Installation

cargo add vortex-sdk

Initialization

let client = VortexClient::new(std::env::var("VORTEX_API_KEY")?);

Environment Variables

Variable Required Description
VORTEX_API_KEY Your Vortex API key

Core Methods

These are the methods you'll use most often.

generate_token()

Generate a signed token for use with Vortex widgets

Signature:

fn generate_token(&self, payload: &GenerateTokenPayload, options: Option<&GenerateTokenOptions>) -> Result<String, VortexError>

Parameters:

Name Type Required Description
payload &GenerateTokenPayload Data to sign (user, component, scope, vars, etc.)
options Option<&GenerateTokenOptions> Optional configuration (expires_in)

Returns: Result<String, VortexError>

Added in v0.8.0


get_invitation()

Get a specific invitation by ID

Signature:

async fn get_invitation(&self, invitation_id: &str) -> Result<Invitation, VortexError>

Parameters:

Name Type Required Description
invitation_id &str The invitation ID

Returns: Result<Invitation, VortexError>

Added in v0.1.0


accept_invitation()

Accept a single invitation (recommended method)

Signature:

async fn accept_invitation(&self, invitation_id: &str, user: crate::types::AcceptUser) -> Result<Invitation, VortexError>

Parameters:

Name Type Required Description
invitation_id &str Single invitation ID to accept
user crate::types::AcceptUser User object with email and/or phone

Returns: Result<Invitation, VortexError>

Example:

use vortex_sdk::{VortexClient, AcceptUser};

let client = VortexClient::new("VRTX.key.secret".to_string());
let user = AcceptUser::new().with_email("user@example.com");
let result = client.accept_invitation("inv-123", user).await;

Added in v0.6.0


All Methods

get_invitations_by_target()

Get invitations by target (email or sms)

Signature:

async fn get_invitations_by_target(&self, target_type: &str, target_value: &str) -> Result<Vec<Invitation>, VortexError>

Parameters:

Name Type Required Description
target_type &str Type of target (email, phone)
target_value &str The target value

Returns: Result<Vec<Invitation>, VortexError>

Added in v0.1.0


revoke_invitation()

Revoke (delete) an invitation

Signature:

async fn revoke_invitation(&self, invitation_id: &str) -> Result<(), VortexError>

Parameters:

Name Type Required Description
invitation_id &str The invitation ID to revoke

Returns: Result<(), VortexError>

Added in v0.1.0


accept_invitations()

Accept multiple invitations

Signature:

async fn accept_invitations(&self, invitation_ids: Vec<String>, param: impl Into<crate::types::AcceptInvitationParam>) -> Result<Invitation, VortexError>

Parameters:

Name Type Required Description
invitation_ids Vec<String> Vector of invitation IDs to accept
param impl Into<crate::types::AcceptInvitationParam> User data (preferred) or legacy target format

Returns: Result<Invitation, VortexError>

Added in v0.1.0


delete_invitations_by_scope()

Delete all invitations for a specific scope

Signature:

async fn delete_invitations_by_scope(&self, scope_type: &str, scope: &str) -> Result<(), VortexError>

Parameters:

Name Type Required Description
scope_type &str The scope type (organization, team, etc.)
scope &str The scope identifier

Returns: Result<(), VortexError>

Added in v0.4.0


get_invitations_by_scope()

Get all invitations for a specific scope

Signature:

async fn get_invitations_by_scope(&self, scope_type: &str, scope: &str) -> Result<Vec<Invitation>, VortexError>

Parameters:

Name Type Required Description
scope_type &str The scope type (organization, team, etc.)
scope &str The scope identifier

Returns: Result<Vec<Invitation>, VortexError>

Added in v0.4.0


reinvite()

Reinvite a user (send invitation again)

Signature:

async fn reinvite(&self, invitation_id: &str) -> Result<Invitation, VortexError>

Parameters:

Name Type Required Description
invitation_id &str The invitation ID to reinvite

Returns: Result<Invitation, VortexError>

Added in v0.2.0


create_invitation()

Create an invitation from your backend This method allows you to create invitations programmatically using your API key, without requiring a user JWT token. Useful for server-side invitation creation, such as "People You May Know" flows or admin-initiated invitations. - email: Send an email invitation - sms: Create an SMS invitation (short link returned for you to send) - internal: Create an internal invitation for PYMK flows (no email sent)

Signature:

async fn create_invitation(&self, request: CreateInvitationRequest) -> Result<CreateInvitationResponse, VortexError>

Parameters:

Name Type Required Description
request CreateInvitationRequest

Returns: Result<CreateInvitationResponse, VortexError>

Example:

use vortex_sdk::{VortexClient, CreateInvitationRequest, CreateInvitationTarget, Inviter, CreateInvitationScope};

async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = VortexClient::new("VRTX.xxx.yyy".to_string());

// Create an email invitation
let request = CreateInvitationRequest::new(
"widget-config-123",
CreateInvitationTarget::email("invitee@example.com"),
Inviter::new("user-456")
.with_email("inviter@example.com")
.with_user_name("John Doe"),
)
.with_groups(vec![
CreateInvitationScope::new("team", "team-789", "Engineering"),
]);

let result = client.create_invitation(request).await?;

// Create an internal invitation (PYMK flow - no email sent)
let request = CreateInvitationRequest::new(
"widget-config-123",
CreateInvitationTarget::internal("internal-user-abc"),
Inviter::new("user-456"),
)
.with_source("pymk");

let result = client.create_invitation(request).await?;
Ok(())
}

Added in v0.1.0


get_autojoin_domains()

Get autojoin domains configured for a specific scope

Signature:

async fn get_autojoin_domains(&self, scope_type: &str, scope: &str) -> Result<AutojoinDomainsResponse, VortexError>

Parameters:

Name Type Required Description
scope_type &str The type of scope (e.g., "organization", "team", "project")
scope &str The scope identifier (customer's group ID)

Returns: Result<AutojoinDomainsResponse, VortexError>

Added in v0.6.0


sync_internal_invitation()

Configure autojoin domains for a specific scope

Signature:

async fn sync_internal_invitation(&self, request: &SyncInternalInvitationRequest) -> Result<SyncInternalInvitationResponse, VortexError>

Parameters:

Name Type Required Description
request &SyncInternalInvitationRequest The configure autojoin request

Returns: Result<SyncInternalInvitationResponse, VortexError>

Example:

use vortex_sdk::{VortexClient, SyncInternalInvitationRequest};

async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = VortexClient::new("VRTX.xxx.yyy".to_string());
let request = SyncInternalInvitationRequest::new(
"user-123", "user-456", "accepted", "component-uuid",
);
let result = client.sync_internal_invitation(&request).await?;
println!("Processed {} invitations", result.processed);
Ok(())
}

Added in v0.6.0


Types

GenerateTokenPayload

Payload for generate_token() - used to generate secure tokens for Vortex components

Field Type Required Description
user Option<TokenUser> The authenticated user who will be using the Vortex component
component Option<String> Component ID to generate token for (from your Vortex dashboard)
scope Option<String> Scope identifier to restrict invitations (format: "scopeType:scopeId")
vars Option<HashMap<String, Value>> Custom variables to pass to the component for template rendering

TokenUser

User data for token generation - represents the authenticated user sending invitations

Field Type Required Description
id String Unique identifier for the user in your system. Used to attribute invitations.
email Option<String> User's email address. Used for reply-to in invitation emails.
name Option<String> Display name shown to invitation recipients (e.g., "John invited you")
avatar_url Option<String> URL to user's avatar image. Displayed in invitation emails and widgets.
admin_scopes Option<Vec<String>> List of scope IDs where this user has admin privileges
allowed_email_domains Option<Vec<String>> Restrict invitations to specific email domains (e.g., ["acme.com"])

AcceptUser

User data for accepting invitations - identifies who accepted the invitation

Field Type Required Description
email Option<String> Email address of the accepting user. At least one of email or phone is required.
phone Option<String> Phone number with country code. At least one of email or phone is required.
name Option<String> Display name of the accepting user (shown in notifications to inviter)
is_existing Option<bool> Whether user was already registered. Some(true)=existing, Some(false)=new signup, None=unknown.

CreateInvitationTarget

Target specification when creating an invitation - where to send the invite

Field Type Required Description
target_type String Delivery channel: "email", "phone", "share", or "internal"
value String Target address: email address, phone number with country code, or internal user ID
name Option<String> Display name of the recipient (used in email greetings)

CreateInvitationScope

Scope specification when creating an invitation - what group/team to invite into

Field Type Required Description
scope_type String Scope type (e.g., "team", "organization", "workspace")
group_id String Your internal identifier for this scope/group
name String Display name for the scope (shown in invitation emails)

Identifier

Email or phone identifier for looking up users

Field Type Required Description
identifier_type String Identifier type: "email" or "phone"
value String The email address or phone number (with country code for phone)

ConfigureAutojoinRequest

Request to configure autojoin domains for a scope

Field Type Required Description
scope_type String Type of scope (e.g., "team", "workspace")
scope_id String Your internal identifier for the scope
domains Vec<String> List of email domains to enable autojoin for (e.g., ["acme.com"])

SyncInternalInvitationRequest

Request to sync an internal invitation (for tracking invitations made outside Vortex)

Field Type Required Description
inviter_id String Your internal user ID for the person who sent the invitation
target CreateInvitationTarget The invitation recipient
scopes Option<Vec<CreateInvitationScope>> Scopes/groups the invitation grants access to

InvitationResult

Complete invitation details as returned by the Vortex API

Field Type Required Description
id String Unique identifier for this invitation
account_id String Your Vortex account ID
click_throughs i32 Number of times the invitation link was clicked
form_submission_data Option<HashMap<String, serde_json::Value>> 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.
configuration_attributes Option<HashMap<String, serde_json::Value>> Deprecated: Use form_submission_data instead. Contains the same data.
created_at String ISO 8601 timestamp when the invitation was created
deactivated bool Whether this invitation has been revoked or expired
delivery_count i32 Number of times the invitation was sent (including reminders)
delivery_types Vec<String> Channels used to deliver: "email", "phone", "share", "internal"
foreign_creator_id String Your internal user ID for the person who created this invitation
invitation_type String Type: "single_use", "multi_use", or "autojoin"
status String Current status: queued, sending, sent, delivered, accepted, shared
target Vec<InvitationTarget> List of invitation recipients with their contact info and status
views i32 Number of times the invitation page was viewed
groups Vec<InvitationScope> Scopes (teams/orgs) this invitation grants access to
expired bool Whether this invitation has passed its expiration date
expires Option<String> ISO 8601 timestamp when this invitation expires
inviter Option<Inviter> Information about who sent the invitation

InvitationTarget

Target recipient of an invitation (from API response)

Field Type Required Description
target_type String Delivery channel: "email", "phone", "share", or "internal"
value String Target address: email, phone number with country code, or share link ID
name Option<String> Display name of the recipient
avatar_url Option<String> Avatar URL for the recipient
status Option<String> Delivery status for this specific target

InvitationScope

Scope/group that the invitation grants access to (from API response)

Field Type Required Description
id String Vortex internal UUID for this scope record
account_id String Your Vortex account ID
group_id String Your internal scope/group identifier
scope_type String Scope type (e.g., "team", "organization", "workspace")
name String Display name for the scope
created_at String ISO 8601 timestamp when the scope was created

InvitationAcceptance

Details about an invitation acceptance event

Field Type Required Description
id String Unique identifier for this acceptance record
invitation_id String ID of the invitation that was accepted
email Option<String> Email of the user who accepted
phone Option<String> Phone of the user who accepted
name Option<String> Name of the user who accepted
is_existing Option<bool> Whether the user already had an account
created_at String ISO 8601 timestamp when the acceptance occurred

Inviter

Information about the user who sent an invitation

Field Type Required Description
id String Your internal user ID for the inviter
email Option<String> Email address of the inviter
name Option<String> Display name of the inviter
avatar_url Option<String> Avatar URL of the inviter

AutojoinDomain

Autojoin domain configuration - users with matching email domains automatically join

Field Type Required Description
id String Unique identifier for this autojoin configuration
domain String Email domain that triggers autojoin (e.g., "acme.com")

AutojoinDomainsResponse

Response from get_autojoin_domains()

Field Type Required Description
domains Vec<AutojoinDomain> List of configured autojoin domains

SyncInternalInvitationResponse

Response from sync_internal_invitation()

Field Type Required Description
invitation InvitationResult The created or updated invitation
created bool true if a new invitation was created, false if existing was updated

VortexWebhookEvent

Webhook event payload delivered to your endpoint

Field Type Required Description
id String Unique identifier for this webhook delivery
event_type String Event type (e.g., "invitation.accepted", "member.created")
timestamp String ISO 8601 timestamp when the event occurred
data Value Event-specific payload data (serde_json::Value)

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

Verifying Webhooks

Always verify webhook signatures using VortexWebhooks::verify_signature() to ensure requests are from Vortex. The signature is sent in the X-Vortex-Signature header.

Example: Axum webhook handler

use axum::{extract::State, http::HeaderMap, Json};
use vortex_sdk::VortexWebhooks;

async fn handle_webhook(
    State(webhooks): State<VortexWebhooks>,
    headers: HeaderMap,
    body: String,
) -> Result<Json<serde_json::Value>, (StatusCode, String)> {
    let signature = headers
        .get("X-Vortex-Signature")
        .and_then(|v| v.to_str().ok())
        .ok_or((StatusCode::BAD_REQUEST, "Missing signature".into()))?;

    // Verify the signature
    if !webhooks.verify_signature(&body, signature) {
        return Err((StatusCode::BAD_REQUEST, "Invalid signature".into()));
    }

    // Parse the event
    let event = webhooks.parse_event(&body)?;

    match event.event_type.as_str() {
        "invitation.accepted" => {
            // User accepted an invitation — activate their account
            println!("Accepted: {:?}", event.data);
        }
        "member.created" => {
            // New member joined via invitation
            println!("New member: {:?}", event.data);
        }
        _ => {}
    }

    Ok(Json(serde_json::json!({ "received": true })))
}

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

Supported Events

Event Description
invitation.created A new invitation was created
invitation.accepted An invitation was accepted by the recipient
invitation.deactivated An invitation was deactivated (revoked or expired)
invitation.email.delivered Invitation email was successfully delivered
invitation.email.bounced Invitation email bounced (invalid address)
invitation.email.opened Recipient opened the invitation email
invitation.link.clicked Recipient clicked the invitation link
invitation.reminder.sent A reminder email was sent for a pending invitation
member.created A new member was created from an accepted invitation
group.member.added A member was added to a scope/group
deployment.created A new deployment configuration was created
deployment.deactivated A deployment was deactivated
abtest.started An A/B test was started
abtest.winner_declared An A/B test winner was declared
email.complained Recipient marked the email as spam

Error Handling

All SDK errors extend VortexError.

Error Description
VortexError::WebhookSignatureInvalid Returned when webhook signature verification fails. Check that you are using the raw request body and the correct signing secret.
VortexError General error type for validation errors (e.g., missing API key, invalid parameters)