Skip to main content

Vortex Python SDK for invitation management and JWT generation

Project description

Vortex Python SDK

A Python SDK for Vortex invitation management and JWT generation.

Features

Invitation Delivery Types

Vortex supports multiple delivery methods for invitations:

  • email - Email invitations sent by Vortex (includes reminders and nudges)
  • phone - Phone invitations sent by the user/customer
  • share - Shareable invitation links for social sharing
  • internal - Internal invitations managed entirely by your application
    • No email/SMS communication triggered by Vortex
    • Target value can be any customer-defined identifier
    • Useful for in-app invitation flows where you handle the delivery
    • Example use case: In-app notifications, dashboard invites, etc.

Installation

pip install vortex-python-sdk

If your project uses requirements.txt, pyproject.toml, or a platform-specific build config, make sure vortex-python-sdk is added there as well so it gets installed in your deployment environment.

Note: The package will be available on PyPI once published. See PUBLISHING.md for publishing instructions.

Usage

Basic Setup

from vortex_sdk import Vortex

# Initialize the client with your Vortex API key
vortex = Vortex(api_key="your-vortex-api-key")

# Or with custom base URL
vortex = Vortex(api_key="your-vortex-api-key", base_url="https://custom-api.example.com")

Sign user data for use with the Vortex Widget (Alternative to JWT)

Instead of generating a full JWT, you can use the simpler sign() method. This produces an HMAC signature that you pass alongside the user prop on your Vortex widget.

import os
from vortex_sdk import Vortex

vortex = Vortex(api_key=os.environ["VORTEX_API_KEY"])

# Sign the user data
signature = vortex.sign({
    "id": "user-123",
    "email": "user@example.com",
    "name": "Jane Doe",  # Optional
})

# Pass both user and signature to your frontend
# Frontend usage:
# <VortexInvite
#   user={{ userId: "user-123", userEmail: "user@example.com", name: "Jane Doe" }}
#   signature={signature}
# />

Token Generation (Recommended)

The generate_token() method creates a signed JWT for use with Vortex widgets. This is the recommended approach for production environments.

import os
from vortex_sdk import Vortex

vortex = Vortex(api_key=os.environ["VORTEX_API_KEY"])

# Sign just the user (minimum for secure attribution)
token = vortex.generate_token({"user": {"id": "user-123"}})

# Sign full payload with additional data
token = vortex.generate_token({
    "component": "widget-abc",
    "user": {
        "id": "user-123",
        "name": "Peter",
        "email": "peter@example.com"
    },
    "scope": "workspace_456",
    "vars": {"company_name": "Acme"}
})

# Custom expiration (default is 5 minutes)
token = vortex.generate_token(
    {"user": {"id": "user-123"}},
    {"expires_in": "1h"}  # or expires_in=3600 (seconds)
)

# Frontend usage:
# window.vortex = {
#   token: SERVER_GENERATED_TOKEN,
#   component: "widget-abc",
#   user: { id: "user-123", name: "Peter" }  // user.id must match token
# }

Expiration formats:

  • "5m" - 5 minutes
  • "1h" - 1 hour
  • "24h" - 24 hours
  • "7d" - 7 days
  • 3600 - integer seconds

Security notes:

  • Always sign at least user.id for secure invitation attribution
  • Tokens include iat (issued at) and exp (expiration) claims
  • Generate tokens server-side; never expose your API key to the client

JWT Generation (Legacy)

# Generate JWT for a user
user = {
    "id": "user-123",
    "email": "user@example.com",
    "user_name": "Jane Doe",                                    # Optional: user's display name
    "user_avatar_url": "https://example.com/avatars/jane.jpg",  # Optional: user's avatar URL
    "admin_scopes": ["autojoin"]                                # Optional: grants autojoin admin privileges
}

jwt = vortex.generate_jwt(user=user)
print(f"JWT: {jwt}")

# Or using type-safe models
from vortex_sdk import User

user = User(
    id="user-123",
    email="user@example.com",
    user_name="Jane Doe",                                       # Optional
    user_avatar_url="https://example.com/avatars/jane.jpg",     # Optional
    admin_scopes=["autojoin"]                              # Optional
)

jwt = vortex.generate_jwt(user=user)

Invitation Management

Get Invitations by Target

import asyncio

async def get_user_invitations():
    # Async version
    invitations = await vortex.get_invitations_by_target("email", "user@example.com")
    for invitation in invitations:
        print(f"Invitation ID: {invitation.id}, Status: {invitation.status}")

# Sync version
invitations = vortex.get_invitations_by_target_sync("email", "user@example.com")

Accept an Invitation

async def accept_user_invitation():
    # Async version
    result = await vortex.accept_invitation(
        invitation_id="inv-123",
        user={"email": "user@example.com"}
    )
    print(f"Result: {result}")

# Sync version
result = vortex.accept_invitation_sync(
    invitation_id="inv-123",
    user={"email": "user@example.com"}
)
Tracking new vs existing users

The is_existing field helps you track whether invitation accepts come from new signups or existing users:

# New user signup
result = vortex.accept_invitation_sync(
    invitation_id="inv-123",
    user={"email": "newuser@example.com", "is_existing": False}
)

# Existing user accepting an invitation
result = vortex.accept_invitation_sync(
    invitation_id="inv-123",
    user={"email": "existinguser@example.com", "is_existing": True}
)

# Using AcceptUser class
from vortex_sdk.types import AcceptUser
user = AcceptUser(email="user@example.com", is_existing=True)
result = vortex.accept_invitation_sync("inv-123", user)

Get Specific Invitation

async def get_invitation():
    # Async version
    invitation = await vortex.get_invitation("invitation-id")
    print(f"Invitation: {invitation.id}")

# Sync version
invitation = vortex.get_invitation_sync("invitation-id")

Revoke Invitation

async def revoke_invitation():
    # Async version
    result = await vortex.revoke_invitation("invitation-id")
    print(f"Revoked: {result}")

# Sync version
result = vortex.revoke_invitation_sync("invitation-id")

Scope Operations

Get Invitations by Scope

async def get_scope_invitations():
    # Async version
    invitations = await vortex.get_invitations_by_scope("organization", "org123")
    print(f"Found {len(invitations)} invitations")

# Sync version
invitations = vortex.get_invitations_by_scope_sync("organization", "org123")

Delete Invitations by Scope

async def delete_scope_invitations():
    # Async version
    result = await vortex.delete_invitations_by_scope("organization", "org123")
    print(f"Deleted: {result}")

# Sync version
result = vortex.delete_invitations_by_scope_sync("organization", "org123")

Reinvite

async def reinvite_user():
    # Async version
    invitation = await vortex.reinvite("invitation-id")
    print(f"Reinvited: {invitation.id}")

# Sync version
invitation = vortex.reinvite_sync("invitation-id")

Sync Internal Invitation

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.

async def sync_internal_invitation_action():
    # Async version
    result = await vortex.sync_internal_invitation(
        creator_id="user-123",      # The inviter's user ID in your system
        target_value="user-456",    # The invitee's user ID in your system
        action="accepted",          # "accepted" or "declined"
        component_id="component-uuid"  # The widget component UUID
    )
    print(f"Processed: {result['processed']}")
    print(f"Invitation IDs: {result['invitationIds']}")

# Sync version
result = vortex.sync_internal_invitation_sync(
    creator_id="user-123",
    target_value="user-456",
    action="accepted",
    component_id="component-uuid"
)

Parameters:

  • creator_id (str) — The inviter's user ID in your system
  • target_value (str) — The invitee's user ID in your system
  • action ("accepted" | "declined") — The invitation decision
  • component_id (str) — The widget component UUID

Response:

  • processed (int) — Count of invitations processed
  • invitationIds (list[str]) — IDs of processed invitations

Use cases:

  • You handle invitation delivery through your own in-app notifications or UI
  • Users accept/decline invitations within your application
  • You need to keep Vortex updated with the invitation status

Context Manager Usage

# Async context manager
async with Vortex(api_key="your-api-key") as vortex:
    invitations = await vortex.get_invitations_by_target("email", "user@example.com")

# Sync context manager
with Vortex(api_key="your-api-key") as vortex:
    invitations = vortex.get_invitations_by_target_sync("email", "user@example.com")

Error Handling

from vortex_sdk import VortexApiError

try:
    invitation = vortex.get_invitation_sync("invalid-id")
except VortexApiError as e:
    print(f"API Error: {e.message} (Status: {e.status_code})")
except Exception as e:
    print(f"Unexpected error: {e}")

Development

Installation

# Install development dependencies
pip install -e ".[dev]"

Running Tests

pytest

Code Formatting

# Format code
black src/ tests/
isort src/ tests/

# Lint code
ruff check src/ tests/
mypy src/

Webhooks

The SDK provides built-in support for verifying and parsing incoming webhook events from Vortex.

Setup

import os
from vortex_sdk import VortexWebhooks, is_webhook_event, is_analytics_event

webhooks = VortexWebhooks(secret=os.environ["VORTEX_WEBHOOK_SECRET"])

Verifying and Parsing Events

# In your HTTP handler (Flask example):
@app.route("/webhooks/vortex", methods=["POST"])
def handle_webhook():
    payload = request.get_data(as_text=True)
    signature = request.headers.get("X-Vortex-Signature", "")

    try:
        event = webhooks.construct_event(payload, signature)
    except VortexWebhookSignatureError:
        return "Invalid signature", 400

    if is_webhook_event(event.__dict__):
        print(f"Webhook event: {event.type}")
    elif is_analytics_event(event.__dict__):
        print(f"Analytics event: {event.name}")

    return "OK", 200

Event Types

Webhook event types are available as the WebhookEventType enum:

from vortex_sdk import WebhookEventType

if event.type == WebhookEventType.INVITATION_ACCEPTED:
    # Handle invitation accepted
    pass

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

vortex_python_sdk-0.16.0.tar.gz (25.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

vortex_python_sdk-0.16.0-py3-none-any.whl (22.6 kB view details)

Uploaded Python 3

File details

Details for the file vortex_python_sdk-0.16.0.tar.gz.

File metadata

  • Download URL: vortex_python_sdk-0.16.0.tar.gz
  • Upload date:
  • Size: 25.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.15

File hashes

Hashes for vortex_python_sdk-0.16.0.tar.gz
Algorithm Hash digest
SHA256 42dead42fb7f5d208d50f91b709ada1d014f0f7e49e7cdf7a3905d2005b9c15c
MD5 b0cbb56475c6150dfd06a67aa27a6b10
BLAKE2b-256 7168db2d19aae6a151a170b03d0c95f2ac360786d296552f182100c868953492

See more details on using hashes here.

File details

Details for the file vortex_python_sdk-0.16.0-py3-none-any.whl.

File metadata

File hashes

Hashes for vortex_python_sdk-0.16.0-py3-none-any.whl
Algorithm Hash digest
SHA256 fd028565e29bb3c038b902525775904d54a2c5c0fd56d2ea85aece2a828a6a12
MD5 142800de46dad4087f16f0cba5b19b86
BLAKE2b-256 d48ac5bc7e2bd122308350eb1b70a3fa6dd08977b76a08d7b954b212febb8e85

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page