import json
import re
import sys
from pathlib import Path
SDK_DIR = Path(__file__).parent.parent
def get_version():
cargo = SDK_DIR / "Cargo.toml"
if cargo.exists():
content = cargo.read_text()
match = re.search(r'^version\s*=\s*"([^"]+)"', content, re.MULTILINE)
if match:
return match.group(1)
return "0.0.0"
def parse_doc_comment(doc: str) -> dict:
result = {
'description': '',
'arguments': [],
'returns': None,
'example': None,
'category': None,
'since': None,
'primary': False,
'deprecated': None,
}
if not doc:
return result
lines = []
for line in doc.split('\n'):
line = line.strip()
if line.startswith('///'):
line = line[3:].strip()
lines.append(line)
description_lines = []
example_lines = []
in_arguments = False
in_example = False
for line in lines:
if line.startswith('@vortex'):
attrs = line[7:].strip()
cat_match = re.search(r'category=(\S+)', attrs)
since_match = re.search(r'since=(\S+)', attrs)
primary_match = re.search(r'primary=true', attrs)
deprecated_match = re.search(r'deprecated=(\S+)', attrs)
if cat_match:
result['category'] = cat_match.group(1)
if since_match:
result['since'] = since_match.group(1)
if primary_match:
result['primary'] = True
if deprecated_match:
result['deprecated'] = deprecated_match.group(1)
elif line.startswith('# Arguments'):
in_arguments = True
in_example = False
elif line.startswith('# Example'):
in_example = True
in_arguments = False
elif line.startswith('# Returns'):
in_arguments = False
in_example = False
elif line.startswith('```'):
if in_example:
pass elif line.startswith('* `') and in_arguments:
match = re.match(r'\*\s*`(\w+)`\s*-\s*(.*)', line)
if match:
result['arguments'].append({
'name': match.group(1),
'description': match.group(2)
})
elif in_example and not line.startswith('#'):
example_lines.append(line)
elif not in_arguments and not in_example and not line.startswith('#'):
description_lines.append(line)
result['description'] = ' '.join(description_lines).strip()
if example_lines:
result['example'] = '\n'.join(example_lines).strip()
return result
def extract_methods(rs_file: Path) -> dict:
content = rs_file.read_text()
methods = {'primary': [], 'secondary': []}
pattern = r'((?:^\s*///.*\n)+)\s*pub\s+(async\s+)?fn\s+(\w+)\s*(<[^>]*>)?\s*\(([^)]*)\)\s*(?:->\s*([^\{]+))?'
for match in re.finditer(pattern, content, re.MULTILINE):
doc = match.group(1)
is_async = bool(match.group(2))
method_name = match.group(3)
generics = match.group(4) or ''
params_str = match.group(5)
return_type = match.group(6).strip() if match.group(6) else '()'
if method_name in ('new', 'with_base_url', 'transform_scope', 'transform_invitation',
'transform_invitations', 'transform_create_request', 'sdk_version', 'sign'):
continue
parsed = parse_doc_comment(doc)
params = []
if params_str.strip() and params_str.strip() != '&self':
params_str = re.sub(r'&(?:mut\s+)?self\s*,?\s*', '', params_str)
if params_str.strip():
for param in params_str.split(','):
param = param.strip()
if ':' in param:
parts = param.split(':', 1)
param_name = parts[0].strip()
param_type = parts[1].strip()
desc = ''
for arg in parsed['arguments']:
if arg['name'] == param_name:
desc = arg['description']
break
params.append({
'name': param_name,
'type': param_type,
'required': not param_type.startswith('Option<'),
'description': desc
})
param_sig = ', '.join(f"{p['name']}: {p['type']}" for p in params)
if is_async:
signature = f"async fn {method_name}(&self, {param_sig}) -> {return_type}"
else:
signature = f"fn {method_name}(&self, {param_sig}) -> {return_type}"
method = {
'name': method_name,
'category': parsed['category'] or 'uncategorized',
'signature': signature,
'params': params,
'returns': {'type': return_type, 'description': ''},
'description': parsed['description'],
'since': parsed['since'] or '0.1.0',
}
if parsed['example']:
method['example'] = {'code': parsed['example']}
if parsed['deprecated']:
method['deprecated'] = True
method['deprecationMessage'] = parsed['deprecated']
if parsed['primary']:
methods['primary'].append(method)
else:
methods['secondary'].append(method)
return methods
def build_types() -> list:
return [
{
"name": "GenerateTokenPayload",
"description": "Payload for generate_token() - used to generate secure tokens for Vortex components",
"fields": [
{"name": "user", "type": "Option<TokenUser>", "required": False, "description": "The authenticated user who will be using the Vortex component"},
{"name": "component", "type": "Option<String>", "required": False, "description": "Component ID to generate token for (from your Vortex dashboard)"},
{"name": "scope", "type": "Option<String>", "required": False, "description": "Scope identifier to restrict invitations (format: \"scopeType:scopeId\")"},
{"name": "vars", "type": "Option<HashMap<String, Value>>", "required": False, "description": "Custom variables to pass to the component for template rendering"},
]
},
{
"name": "TokenUser",
"description": "User data for token generation - represents the authenticated user sending invitations",
"fields": [
{"name": "id", "type": "String", "required": True, "description": "Unique identifier for the user in your system. Used to attribute invitations."},
{"name": "email", "type": "Option<String>", "required": False, "description": "User's email address. Used for reply-to in invitation emails."},
{"name": "name", "type": "Option<String>", "required": False, "description": "Display name shown to invitation recipients (e.g., \"John invited you\")"},
{"name": "avatar_url", "type": "Option<String>", "required": False, "description": "URL to user's avatar image. Displayed in invitation emails and widgets."},
{"name": "admin_scopes", "type": "Option<Vec<String>>", "required": False, "description": "List of scope IDs where this user has admin privileges"},
{"name": "allowed_email_domains", "type": "Option<Vec<String>>", "required": False, "description": "Restrict invitations to specific email domains (e.g., [\"acme.com\"])"},
]
},
{
"name": "AcceptUser",
"description": "User data for accepting invitations - identifies who accepted the invitation",
"fields": [
{"name": "email", "type": "Option<String>", "required": False, "description": "Email address of the accepting user. At least one of email or phone is required."},
{"name": "phone", "type": "Option<String>", "required": False, "description": "Phone number with country code. At least one of email or phone is required."},
{"name": "name", "type": "Option<String>", "required": False, "description": "Display name of the accepting user (shown in notifications to inviter)"},
{"name": "is_existing", "type": "Option<bool>", "required": False, "description": "Whether user was already registered. Some(true)=existing, Some(false)=new signup, None=unknown."},
]
},
{
"name": "CreateInvitationTarget",
"description": "Target specification when creating an invitation - where to send the invite",
"fields": [
{"name": "target_type", "type": "String", "required": True, "description": "Delivery channel: \"email\", \"phone\", \"share\", or \"internal\""},
{"name": "value", "type": "String", "required": True, "description": "Target address: email address, phone number with country code, or internal user ID"},
{"name": "name", "type": "Option<String>", "required": False, "description": "Display name of the recipient (used in email greetings)"},
]
},
{
"name": "CreateInvitationScope",
"description": "Scope specification when creating an invitation - what group/team to invite into",
"fields": [
{"name": "scope_type", "type": "String", "required": True, "description": "Scope type (e.g., \"team\", \"organization\", \"workspace\")"},
{"name": "group_id", "type": "String", "required": True, "description": "Your internal identifier for this scope/group"},
{"name": "name", "type": "String", "required": True, "description": "Display name for the scope (shown in invitation emails)"},
]
},
{
"name": "Identifier",
"description": "Email or phone identifier for looking up users",
"fields": [
{"name": "identifier_type", "type": "String", "required": True, "description": "Identifier type: \"email\" or \"phone\""},
{"name": "value", "type": "String", "required": True, "description": "The email address or phone number (with country code for phone)"},
]
},
{
"name": "ConfigureAutojoinRequest",
"description": "Request to configure autojoin domains for a scope",
"fields": [
{"name": "scope_type", "type": "String", "required": True, "description": "Type of scope (e.g., \"team\", \"workspace\")"},
{"name": "scope_id", "type": "String", "required": True, "description": "Your internal identifier for the scope"},
{"name": "domains", "type": "Vec<String>", "required": True, "description": "List of email domains to enable autojoin for (e.g., [\"acme.com\"])"},
]
},
{
"name": "SyncInternalInvitationRequest",
"description": "Request to sync an internal invitation (for tracking invitations made outside Vortex)",
"fields": [
{"name": "inviter_id", "type": "String", "required": True, "description": "Your internal user ID for the person who sent the invitation"},
{"name": "target", "type": "CreateInvitationTarget", "required": True, "description": "The invitation recipient"},
{"name": "scopes", "type": "Option<Vec<CreateInvitationScope>>", "required": False, "description": "Scopes/groups the invitation grants access to"},
]
},
{
"name": "InvitationResult",
"description": "Complete invitation details as returned by the Vortex API",
"fields": [
{"name": "id", "type": "String", "required": True, "description": "Unique identifier for this invitation"},
{"name": "account_id", "type": "String", "required": True, "description": "Your Vortex account ID"},
{"name": "click_throughs", "type": "i32", "required": True, "description": "Number of times the invitation link was clicked"},
{"name": "form_submission_data", "type": "Option<HashMap<String, serde_json::Value>>", "required": False, "description": "Invitation form data submitted by the user, including email addresses of invitees and the values of any custom fields."},
{"name": "configuration_attributes", "type": "Option<HashMap<String, serde_json::Value>>", "required": False, "description": "Deprecated: Use form_submission_data instead. Contains the same data."},
{"name": "created_at", "type": "String", "required": True, "description": "ISO 8601 timestamp when the invitation was created"},
{"name": "deactivated", "type": "bool", "required": True, "description": "Whether this invitation has been revoked or expired"},
{"name": "delivery_count", "type": "i32", "required": True, "description": "Number of times the invitation was sent (including reminders)"},
{"name": "delivery_types", "type": "Vec<String>", "required": True, "description": "Channels used to deliver: \"email\", \"phone\", \"share\", \"internal\""},
{"name": "foreign_creator_id", "type": "String", "required": True, "description": "Your internal user ID for the person who created this invitation"},
{"name": "invitation_type", "type": "String", "required": True, "description": "Type: \"single_use\", \"multi_use\", or \"autojoin\""},
{"name": "status", "type": "String", "required": True, "description": "Current status: queued, sending, sent, delivered, accepted, shared"},
{"name": "target", "type": "Vec<InvitationTarget>", "required": True, "description": "List of invitation recipients with their contact info and status"},
{"name": "views", "type": "i32", "required": True, "description": "Number of times the invitation page was viewed"},
{"name": "groups", "type": "Vec<InvitationScope>", "required": True, "description": "Scopes (teams/orgs) this invitation grants access to"},
{"name": "expired", "type": "bool", "required": True, "description": "Whether this invitation has passed its expiration date"},
{"name": "expires", "type": "Option<String>", "required": False, "description": "ISO 8601 timestamp when this invitation expires"},
{"name": "inviter", "type": "Option<Inviter>", "required": False, "description": "Information about who sent the invitation"},
]
},
{
"name": "InvitationTarget",
"description": "Target recipient of an invitation (from API response)",
"fields": [
{"name": "target_type", "type": "String", "required": True, "description": "Delivery channel: \"email\", \"phone\", \"share\", or \"internal\""},
{"name": "value", "type": "String", "required": True, "description": "Target address: email, phone number with country code, or share link ID"},
{"name": "name", "type": "Option<String>", "required": False, "description": "Display name of the recipient"},
{"name": "avatar_url", "type": "Option<String>", "required": False, "description": "Avatar URL for the recipient"},
{"name": "status", "type": "Option<String>", "required": False, "description": "Delivery status for this specific target"},
]
},
{
"name": "InvitationScope",
"description": "Scope/group that the invitation grants access to (from API response)",
"fields": [
{"name": "id", "type": "String", "required": True, "description": "Vortex internal UUID for this scope record"},
{"name": "account_id", "type": "String", "required": True, "description": "Your Vortex account ID"},
{"name": "group_id", "type": "String", "required": True, "description": "Your internal scope/group identifier"},
{"name": "scope_type", "type": "String", "required": True, "description": "Scope type (e.g., \"team\", \"organization\", \"workspace\")"},
{"name": "name", "type": "String", "required": True, "description": "Display name for the scope"},
{"name": "created_at", "type": "String", "required": True, "description": "ISO 8601 timestamp when the scope was created"},
]
},
{
"name": "InvitationAcceptance",
"description": "Details about an invitation acceptance event",
"fields": [
{"name": "id", "type": "String", "required": True, "description": "Unique identifier for this acceptance record"},
{"name": "invitation_id", "type": "String", "required": True, "description": "ID of the invitation that was accepted"},
{"name": "email", "type": "Option<String>", "required": False, "description": "Email of the user who accepted"},
{"name": "phone", "type": "Option<String>", "required": False, "description": "Phone of the user who accepted"},
{"name": "name", "type": "Option<String>", "required": False, "description": "Name of the user who accepted"},
{"name": "is_existing", "type": "Option<bool>", "required": False, "description": "Whether the user already had an account"},
{"name": "created_at", "type": "String", "required": True, "description": "ISO 8601 timestamp when the acceptance occurred"},
]
},
{
"name": "Inviter",
"description": "Information about the user who sent an invitation",
"fields": [
{"name": "id", "type": "String", "required": True, "description": "Your internal user ID for the inviter"},
{"name": "email", "type": "Option<String>", "required": False, "description": "Email address of the inviter"},
{"name": "name", "type": "Option<String>", "required": False, "description": "Display name of the inviter"},
{"name": "avatar_url", "type": "Option<String>", "required": False, "description": "Avatar URL of the inviter"},
]
},
{
"name": "AutojoinDomain",
"description": "Autojoin domain configuration - users with matching email domains automatically join",
"fields": [
{"name": "id", "type": "String", "required": True, "description": "Unique identifier for this autojoin configuration"},
{"name": "domain", "type": "String", "required": True, "description": "Email domain that triggers autojoin (e.g., \"acme.com\")"},
]
},
{
"name": "AutojoinDomainsResponse",
"description": "Response from get_autojoin_domains()",
"fields": [
{"name": "domains", "type": "Vec<AutojoinDomain>", "required": True, "description": "List of configured autojoin domains"},
]
},
{
"name": "SyncInternalInvitationResponse",
"description": "Response from sync_internal_invitation()",
"fields": [
{"name": "invitation", "type": "InvitationResult", "required": True, "description": "The created or updated invitation"},
{"name": "created", "type": "bool", "required": True, "description": "true if a new invitation was created, false if existing was updated"},
]
},
{
"name": "VortexWebhookEvent",
"description": "Webhook event payload delivered to your endpoint",
"fields": [
{"name": "id", "type": "String", "required": True, "description": "Unique identifier for this webhook delivery"},
{"name": "event_type", "type": "String", "required": True, "description": "Event type (e.g., \"invitation.accepted\", \"member.created\")"},
{"name": "timestamp", "type": "String", "required": True, "description": "ISO 8601 timestamp when the event occurred"},
{"name": "data", "type": "Value", "required": True, "description": "Event-specific payload data (serde_json::Value)"},
]
},
]
def build_manifest() -> dict:
version = get_version()
client_file = SDK_DIR / "src/client.rs"
methods = extract_methods(client_file) if client_file.exists() else {'primary': [], 'secondary': []}
return {
"sdk": {
"name": "vortex-rust-sdk",
"language": "rust",
"version": version,
"repository": "https://github.com/teamvortexsoftware/vortex-rust-sdk",
"package": {"name": "vortex-sdk", "registry": "crates"}
},
"overview": {
"product": {
"name": "Vortex",
"tagline": "Invitation infrastructure for modern apps",
"description": "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.",
"learnMoreUrl": "https://tryvortex.com"
},
"sdkPurpose": {
"summary": "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.",
"keyBenefits": [
"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"
]
},
"architecture": {
"summary": "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.",
"flow": [
{"step": "1. Install the backend SDK", "description": "Add this SDK to your Rust project", "location": "backend", "code": "cargo add vortex-sdk"},
{"step": "2. Initialize the client", "description": "Create a Vortex client with your API key (keep this on the server!)", "location": "backend", "code": 'use vortex_sdk::VortexClient;\n\nlet client = VortexClient::new(std::env::var("VORTEX_API_KEY")?);'},
{"step": "3. Generate a token for the current user", "description": "When a user loads a page with a Vortex component, generate a signed token on your server", "location": "backend", "code": 'let payload = GenerateTokenPayload {\n user: Some(TokenUser { id: user_id.into(), ..Default::default() }),\n ..Default::default()\n};\nlet token = client.generate_token(payload, None)?;'},
{"step": "4. Pass the token to your frontend", "description": "Include the token in your response", "location": "backend", "code": 'Json(json!({ "vortex_token": token }))'},
{"step": "5. Render a Vortex component with the token", "description": "Use the React/Angular/Web Component with the token", "location": "frontend", "code": 'import { VortexInvite } from "@teamvortexsoftware/vortex-react";\n\n<VortexInvite token={vortexToken} />'},
{"step": "6. Vortex handles the rest", "description": "The component securely communicates with Vortex servers, displays the invitation UI, sends emails/SMS, tracks conversions, and reports analytics", "location": "vortex"}
],
"diagram": """
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 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 │
│ │◄──────────────────────│
│ │ │
"""
},
"security": {
"summary": "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.",
"whyBackendSigning": "By signing tokens on your server, you:\n\n- Keep your API key secret (it never leaves your server)\n- Control exactly what user data is shared with components\n- Ensure invitations are attributed to real, authenticated users\n- Prevent abuse — users can only send invitations as themselves",
"optional": "Token signing is controlled by your component configuration in the Vortex dashboard."
}
},
"install": {
"command": "cargo add vortex-sdk"
},
"quickstart": {
"description": "Generate a secure token for Vortex components",
"code": 'use vortex_sdk::{VortexClient, GenerateTokenPayload, TokenUser};\n\nlet client = VortexClient::new(std::env::var("VORTEX_API_KEY")?);\n\nlet payload = GenerateTokenPayload {\n user: Some(TokenUser { id: "user-123".into(), email: Some("user@example.com".into()), ..Default::default() }),\n ..Default::default()\n};\n\nlet token = client.generate_token(payload, None)?;'
},
"initialization": {
"className": "VortexClient",
"constructor": {
"signature": "VortexClient::new(api_key: String) -> Self",
"params": [
{"name": "api_key", "type": "String", "required": True, "description": "Your Vortex API key"}
],
"example": 'let client = VortexClient::new(std::env::var("VORTEX_API_KEY")?);'
},
"envVars": [
{"name": "VORTEX_API_KEY", "required": True, "description": "Your Vortex API key"},
]
},
"methods": methods,
"types": build_types(),
"webhooks": {
"supported": True,
"description": "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"
],
"verifyMethod": "VortexWebhooks::verify_signature",
"signatureHeader": "X-Vortex-Signature",
"example": {
"description": "Axum webhook handler",
"code": '''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 })))
}'''
},
"useCases": [
{"title": "Activate users on acceptance", "description": "When invitation.accepted fires, mark the user as active in your database and trigger your onboarding flow."},
{"title": "Track invitation performance", "description": "Monitor email.delivered, email.opened, and link.clicked events to measure invitation funnel metrics."},
{"title": "Sync team membership", "description": "Use member.created and group.member.added to keep your internal membership records in sync."},
{"title": "Alert on delivery issues", "description": "Watch for email.bounced events to proactively reach out via alternative channels."}
],
"events": [
{"name": "invitation.created", "description": "A new invitation was created"},
{"name": "invitation.accepted", "description": "An invitation was accepted by the recipient"},
{"name": "invitation.deactivated", "description": "An invitation was deactivated (revoked or expired)"},
{"name": "invitation.email.delivered", "description": "Invitation email was successfully delivered"},
{"name": "invitation.email.bounced", "description": "Invitation email bounced (invalid address)"},
{"name": "invitation.email.opened", "description": "Recipient opened the invitation email"},
{"name": "invitation.link.clicked", "description": "Recipient clicked the invitation link"},
{"name": "invitation.reminder.sent", "description": "A reminder email was sent for a pending invitation"},
{"name": "member.created", "description": "A new member was created from an accepted invitation"},
{"name": "group.member.added", "description": "A member was added to a scope/group"},
{"name": "deployment.created", "description": "A new deployment configuration was created"},
{"name": "deployment.deactivated", "description": "A deployment was deactivated"},
{"name": "abtest.started", "description": "An A/B test was started"},
{"name": "abtest.winner_declared", "description": "An A/B test winner was declared"},
{"name": "email.complained", "description": "Recipient marked the email as spam"}
],
"methods": [
{
"name": "verify_signature",
"signature": "fn verify_signature(&self, payload: &str, signature: &str) -> bool",
"description": "Verify the HMAC-SHA256 signature of an incoming webhook payload. Returns true if valid, false otherwise.",
"params": [
{"name": "payload", "type": "&str", "description": "The raw request body"},
{"name": "signature", "type": "&str", "description": "The value of the X-Vortex-Signature header"}
],
"returns": "bool"
},
{
"name": "parse_event",
"signature": "fn parse_event(&self, payload: &str) -> Result<VortexEvent, VortexError>",
"description": "Parse a verified webhook payload into a VortexEvent. Call verify_signature first to ensure authenticity.",
"params": [
{"name": "payload", "type": "&str", "description": "The raw request body (JSON)"}
],
"returns": "Result<VortexEvent, VortexError>"
},
{
"name": "construct_event",
"signature": "fn construct_event(&self, payload: &str, signature: &str) -> Result<VortexEvent, VortexError>",
"description": "Verify and parse an incoming webhook payload in one step. Returns Err(VortexError) if signature is invalid.",
"params": [
{"name": "payload", "type": "&str", "description": "The raw request body"},
{"name": "signature", "type": "&str", "description": "The value of the X-Vortex-Signature header"}
],
"returns": "Result<VortexEvent, VortexError>"
}
]
},
"errors": {
"baseException": "VortexError",
"types": [
{
"name": "VortexError::WebhookSignatureInvalid",
"description": "Returned when webhook signature verification fails. Check that you are using the raw request body and the correct signing secret.",
"thrownBy": ["VortexWebhooks::construct_event"]
},
{"name": "VortexError", "description": "General error type for validation errors (e.g., missing API key, invalid parameters)"}
]
},
"examples": {
"install": {
"cargo": "cargo add vortex-sdk"
},
"import": '''use vortex_sdk::{VortexClient, GenerateTokenPayload, TokenUser};
use std::env;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = VortexClient::new(env::var("VORTEX_API_KEY")?);
Ok(())
}''',
"functions": {
"generateToken": '''// Generate a signed token for Vortex components
let payload = GenerateTokenPayload {
user: Some(TokenUser {
id: "user-123".to_string(), // Required: user ID for attribution
email: Some("user@example.com".to_string()), // Optional: user's email
name: Some("Jane Doe".to_string()), // Optional: user's display name
avatar_url: Some("https://example.com/avatars/jane.jpg".to_string()), // Optional: user's avatar URL
..Default::default()
}),
scope: Some("workspace-456".to_string()), // Optional: scope/workspace ID
vars: Some(serde_json::json!({"company_name": "Acme Inc"})), // Optional: template variables
..Default::default()
};
let token = client.generate_token(&payload, None)?;
// Pass token to your frontend for use with Vortex components
Ok(json!({"token": token}))''',
"generateTokenParts": {
"beforeUser": '''// Generate a signed token for Vortex components
let payload = GenerateTokenPayload {
user: Some(TokenUser {
id: "user-123".to_string(), // Required: user ID for attribution
email: Some("user@example.com".to_string()), // Optional: user's email
name: Some("Jane Doe".to_string()), // Optional: user's display name
avatar_url: Some("https://example.com/avatars/jane.jpg".to_string()), // Optional: user's avatar URL
..Default::default()
}),''',
"scopeLine": ' scope: Some("workspace-456".to_string()), // Optional: scope/workspace ID',
"varsLine": ' vars: Some(serde_json::json!({"company_name": "Acme Inc"})), // Optional: template variables',
"afterUser": ''' ..Default::default()
};
let token = client.generate_token(&payload, None)?;
// Pass token to your frontend for use with Vortex components
Ok(json!({"token": token}))'''
},
"generateJwt": '''// Generate JWT (legacy - prefer generate_token for new integrations)
let user = TokenUser {
id: "user-123".to_string(),
email: Some("user@example.com".to_string()),
name: Some("Jane Doe".to_string()), // Optional: user's display name
avatar_url: Some("https://example.com/avatars/jane.jpg".to_string()), // Optional: user's avatar URL
..Default::default()
};
let jwt = client.generate_jwt(&user, None)?;
println!("JWT: {}", jwt);''',
"generateJwtParts": {
"beforeUser": '''// Generate JWT (legacy - prefer generate_token for new integrations)
let user = TokenUser {
id: "user-123".to_string(),
email: Some("user@example.com".to_string()),
name: Some("Jane Doe".to_string()), // Optional: user's display name
avatar_url: Some("https://example.com/avatars/jane.jpg".to_string()), // Optional: user's avatar URL''',
"adminScopesLine": ' admin_scopes: Some(vec!["autojoin".to_string()]), // Optional: grants autojoin admin privileges',
"allowedEmailDomainsLine": ' allowed_email_domains: Some(vec!["example.com".to_string()]), // Optional: restrict by email domain',
"afterUser": ''' ..Default::default()
};
let jwt = client.generate_jwt(&user, None)?;
println!("JWT: {}", jwt);'''
},
"acceptInvitations": '''// Accept one or more invitations for a user
let result = client.accept_invitations(
vec!["invitation-id-1", "invitation-id-2"],
AcceptUser {
email: Some("user@example.com".to_string()),
name: Some("John Doe".to_string()), // Optional
..Default::default()
}
)?;
println!("Accepted invitations: {:?}", result);''',
"getInvitations": '''// Get a single invitation by ID
let invitation = client.get_invitation("invitation-id")?;
println!("Invitation: {:?}", invitation);''',
"getInvitationsByTarget": '''// Get invitations by target
let invitations = client.get_invitations_by_target("email", "user@example.com")?;
println!("Invitations: {:?}", invitations);'''
}
}
}
if __name__ == "__main__":
out_path = None
if "--out" in sys.argv:
idx = sys.argv.index("--out")
out_path = sys.argv[idx + 1] if idx + 1 < len(sys.argv) else None
manifest = build_manifest()
json_str = json.dumps(manifest, indent=2)
if out_path:
Path(out_path).write_text(json_str)
print(f"Manifest written to {out_path}")
else:
print(json_str)