UNPKG

@wallethero/sdk

Version:

TypeScript SDK for WalletHero API - manage pass templates and passes for Apple Wallet and Google Pay

1,450 lines (1,141 loc) 42.7 kB
# WalletHero SDK TypeScript/JavaScript SDK for WalletHero API - Manage pass templates and passes for Apple Wallet and Google Pay. ## 🆕 Version 3.7 New Features **Project-Based Template Organization & Inheritance**: Version 3.7 introduces a comprehensive project system with template inheritance capabilities. ### What's New: - **Projects**: New organizational layer between workspaces and templates - **Template Inheritance**: Main templates provide base styling, current templates override specific attributes - **Custom Fields Hierarchy**: Project → Template → Pass custom fields with replacement logic - **Template Switching**: Switch passes between templates within the same project - **Type Safety**: Full TypeScript support with `Project`, `CompletePassData`, and `TemplateMergeOptions` types ## 🔄 Version 3.3 Features **iOS Deeplink Support**: Version 3.3 added comprehensive iOS deeplink configuration for pass templates. ### Previous Updates: - **iOS Deeplinks**: Configure deeplinks to open your iOS app when users tap passes - **Flexible Configuration**: Set numeric IDs and custom URL schemes - **Type Safety**: Full TypeScript support with `IOSDeeplinkConfig` type - **Easy Management**: Simple methods to get, set, and remove deeplink configuration ## 🔄 Version 2.1 Features **Enhanced Barcode Support**: Version 2.1 added support for multiple barcode formats and header fields. ### Previous Updates: - **Barcode Types**: Support for QR, Aztec, Code 128, PDF417, and no barcode - **Header Fields**: Add top fields to passes for prominent information display - **Variable Interpolation**: Support for `${payload.key}` and `${pass.key}` variables ### New Features: ```typescript // iOS Deeplink Configuration await client.passTemplates.setIOSDeeplink(templateId, { ios_deeplink_id: 12345, ios_deeplink_url: "myapp://loyalty/member", }); // Get deeplink configuration const config = await client.passTemplates.getIOSDeeplink(templateId); // Remove deeplink configuration await client.passTemplates.removeIOSDeeplink(templateId); // Create template with deeplinks const template = await client.passTemplates.create({ // ... other fields ios_deeplink_id: 12345, ios_deeplink_url: "myapp://loyalty/dashboard", }); ``` ### Previous Barcode Features: ```typescript // Multiple barcode formats const template = await client.passTemplates.create({ // ... other fields barcode_id: "${payload.memberId}", barcode_type: "qr", // "qr", "aztec", "code128", "pdf417", or "none" barcode_label: "Member ID", // Optional label displayed below barcode // Header field (appears prominently at top of pass) top_field_label: "Member ID", top_field_value: "${payload.memberId}", }); ``` ## 🚨 Version 2.0 Breaking Changes **UUID Migration**: Version 2.0 introduces a major change where all pass and pass template IDs are now UUIDs instead of integers. ### What Changed: - **Pass IDs**: Changed from `number` to `string` (UUID format) - **Pass Template IDs**: Changed from `number` to `string` (UUID format) - **QR Code Service**: Methods now use `passId` instead of `serialNumber` ### Migration Guide: ```typescript // ❌ v1.x (deprecated) const pass = await client.passes.getBySerialNumber( "123e4567-e89b-12d3-a456-426614174000" ); const template = await client.passTemplates.get(123); // ✅ v2.x (current) const pass = await client.passes.get("123e4567-e89b-12d3-a456-426614174000"); const template = await client.passTemplates.get( "456e7890-e89b-12d3-a456-426614174000" ); // QR Code methods updated const qrCode = await client.qrCodes.getApplePassQRCode(pass.data.id); // Now uses UUID ``` ### Migration Required: - All legacy `serialNumber` methods have been removed - You must update your code to use UUID-based methods before upgrading ## Installation ```bash npm install @wallethero/sdk ``` **Requirements:** - Node.js 18+ (uses native fetch API) - TypeScript 4+ (optional, but recommended for type safety) ## Quick Start ```typescript import WalletHero from "@wallethero/sdk"; const client = new WalletHero({ apiToken: "your-api-token", // apiUrl is optional and defaults to https://api.wallethero.app // timeout: 30000, // optional, defaults to 30 seconds }); // Test connection const isConnected = await client.ping(); console.log("Connected:", isConnected); // Get current user information const userInfo = await client.me(); console.log("User:", userInfo); // Get API server information const serverInfo = await client.serverInfo(); console.log("Server info:", serverInfo); ``` ### Client Utility Methods The WalletHero client provides several utility methods for testing connectivity and getting account information: ```typescript // Test API connectivity const isConnected = await client.ping(); if (!isConnected) { console.error("Failed to connect to WalletHero API"); } // Get current user/account information const userInfo = await client.me(); console.log("Account ID:", userInfo.data.id); console.log("Account Email:", userInfo.data.email); // Get API server information and status const serverInfo = await client.serverInfo(); console.log("Server version:", serverInfo.data.version); console.log("Server status:", serverInfo.data.status); ``` **Use Cases:** - Test API connectivity during application startup - Validate API token and get account details - Monitor API server status and version - Debug connectivity issues ## Authentication The SDK requires an API token for authentication. You can obtain this token from your WalletHero dashboard. The SDK automatically connects to the WalletHero API at [https://api.wallethero.app](https://api.wallethero.app) by default. ```typescript const client = new WalletHero({ apiToken: "your-api-token-here", }); // Or specify a custom API URL if needed const customClient = new WalletHero({ apiUrl: "https://your-custom-api.com", apiToken: "your-api-token-here", }); ``` ## Usage Examples ### Pass Templates #### Create a Pass Template ```typescript const template = await client.passTemplates.create({ uuid: crypto.randomUUID(), name: "Loyalty Card Template", pass_type: "LOYALTY", apple_pass_type_identifier: "pass.WalletHero", workspace_id: 1, background_color: "#FF6B6B", label_color: "#FFFFFF", value_color: "#FFFFFF", logo_text: "My Store", // Header field (appears prominently at top) top_field_label: "Member ID", top_field_value: "${payload.memberId}", // Barcode configuration barcode_id: "${payload.memberId}", // Supports variable interpolation barcode_type: "qr", // "qr", "aztec", "code128", "pdf417", or "none" barcode_label: "Member ID", // Optional label displayed below barcode front_fields: [ { label: "Balance", value: "${payload.balance}" }, { label: "Member Since", value: "${payload.memberSince}" }, ], back_fields: [ { label: "Terms", value: "Terms and conditions apply" }, { label: "Contact", value: "support@mystore.com" }, ], locations: [{ latitude: 37.7749, longitude: -122.4194 }], location_message: "Welcome to our store!", // Custom fields for structured data custom_fields: [ { name: "membershipTier", default_value: "Bronze" }, { name: "preferences", default_value: "email" }, { name: "referralCode" }, // No default value ], }); console.log("Created template:", template.data.id); ``` #### Get Pass Templates ```typescript // Get all templates const templates = await client.passTemplates.list(); // Get templates with filtering const loyaltyTemplates = await client.passTemplates.getByType("LOYALTY"); // Get templates by workspace const workspaceTemplates = await client.passTemplates.getByWorkspace(1); // Search templates const searchResults = await client.passTemplates.search("loyalty"); ``` #### Update a Pass Template ```typescript const updatedTemplate = await client.passTemplates.update(templateId, { name: "Updated Loyalty Card Template", background_color: "#4ECDC4", front_fields: [ { label: "Points", value: "{{points}}" }, { label: "Tier", value: "{{tier}}" }, ], }); ``` **Note**: Template IDs are now UUIDs. When you create a template, the returned ID will be a UUID string. #### Duplicate a Pass Template ```typescript const duplicatedTemplate = await client.passTemplates.duplicate(templateId); console.log("Duplicated template:", duplicatedTemplate.data.name); // "Original Name (Copy)" ``` ### Barcode Types and Variable Interpolation WalletHero supports multiple barcode formats and variable interpolation for dynamic content: #### Supported Barcode Types ```typescript import type { BarcodeType } from "@wallethero/sdk"; // Available barcode types const barcodeTypes: BarcodeType[] = [ "none", // No barcode "qr", // QR Code (default) "aztec", // Aztec code "code128", // Code 128 "pdf417", // PDF417 ]; // Example with different barcode types const template = await client.passTemplates.create({ // ... other fields barcode_id: "${payload.memberCode}", barcode_type: "code128", // Will generate Code 128 barcode barcode_label: "Membership Code", // Optional label below barcode }); ``` #### Barcode Configuration Configure barcodes with data, type, and optional label: ```typescript const template = await client.passTemplates.create({ // ... other fields // Barcode configuration barcode_id: "${payload.ticketNumber}", // The data encoded in the barcode barcode_type: "qr", // Type of barcode to generate barcode_label: "Ticket #", // Optional text displayed below the barcode }); // Create a pass with barcode data const pass = await client.passes.create({ pass_template_id: template.data.id, workspace_id: 1, full_name: "Jane Smith", email: "jane@example.com", payload: { ticketNumber: "TKT-2024-001", }, }); // The generated pass will display: // - QR code containing "TKT-2024-001" // - Text "Ticket #" below the barcode ``` **Barcode Fields:** - `barcode_id` (string): The data to encode in the barcode. Supports variable interpolation. - `barcode_type` (BarcodeType): The type of barcode - "none", "qr", "aztec", "code128", "pdf417" - `barcode_label` (string, optional): Text label displayed below the barcode for user context #### Variable Interpolation Use `${payload.key}` and `${pass.key}` variables to create dynamic content: ```typescript const template = await client.passTemplates.create({ // ... other fields // Header field with pass variable top_field_label: "Card Number", top_field_value: "${pass.id}", // Uses the pass ID // Barcode with payload variable barcode_id: "${payload.memberCode}", // Front fields with mixed variables front_fields: [ { label: "Balance", value: "${payload.balance}" }, { label: "Pass ID", value: "${pass.id}" }, { label: "Member", value: "${pass.full_name}" }, ], // Location message with variables location_message: "Welcome back, ${pass.full_name}!", }); // When creating a pass, provide payload data const pass = await client.passes.create({ pass_template_id: template.data.id, workspace_id: 1, full_name: "John Doe", email: "john@example.com", payload: { balance: "$25.00", memberCode: "MB123456", }, }); // The generated pass will have: // - Header: "Card Number: [UUID of pass]" // - Barcode: "MB123456" (as Code 128) // - Balance: "$25.00" // - Pass ID: "[UUID of pass]" // - Member: "John Doe" ``` #### Custom Fields Management Custom fields allow you to define additional data fields that can be used in passes. These fields act as templates that can store default values and are useful for creating structured data inputs. ```typescript // Create a template with custom fields const template = await client.passTemplates.create({ // ... other template fields custom_fields: [ { name: "membershipTier", default_value: "Bronze" }, { name: "joinDate", default_value: "2024-01-01" }, { name: "preferences" }, // No default value ], }); // Add a custom field to an existing template await client.passTemplates.addCustomField(templateId, { name: "specialOffers", default_value: "enabled", }); // Update a custom field await client.passTemplates.updateCustomField( templateId, "membershipTier", // field name to update { name: "vipTier", // rename the field default_value: "Gold", // new default value } ); // Remove a custom field await client.passTemplates.removeCustomField(templateId, "preferences"); // Get all custom fields for a template const customFields = await client.passTemplates.getCustomFields(templateId); console.log("Custom fields:", customFields); // Output: [{ name: "vipTier", default_value: "Gold" }, ...] // Set all custom fields at once (replaces existing) await client.passTemplates.setCustomFields(templateId, [ { name: "tier", default_value: "Premium" }, { name: "region", default_value: "US" }, { name: "language", default_value: "en" }, ]); ``` **Custom Field Properties:** - `name` (string, required): The field name used as an identifier - `default_value` (string, optional): Default value for this field when creating passes **Use Cases:** - Define structured data inputs for passes - Provide default values for common fields - Create reusable field templates across multiple passes - Organize pass data with consistent field naming #### Header Fields (Top Fields) Header fields appear prominently at the top of passes and are perfect for key identifiers: ```typescript // Single header field const template = await client.passTemplates.create({ // ... other fields top_field_label: "VIP Level", top_field_value: "${payload.vipLevel}", }); // Example pass data const pass = await client.passes.create({ // ... other fields payload: { vipLevel: "Platinum", }, }); // Results in header showing: "VIP Level: Platinum" ``` #### Working with Template Images The SDK supports uploading and managing images for pass templates. Supported formats include PNG, JPEG, WebP, and other common image formats. ```typescript // Upload images to a template (templateId is now UUID) const imageFile = new File( [ /* image data */ ], "logo.png", { type: "image/png" } ); // Upload individual images await client.passTemplates.uploadIcon(templateId, imageFile); await client.passTemplates.uploadLogo(templateId, imageFile); await client.passTemplates.uploadCoverImage(templateId, imageFile); // Upload multiple images at once await client.passTemplates.updateImages(templateId, { icon: iconFile, logo: logoFile, cover_image: coverFile, }); // Get image URLs for display const template = await client.passTemplates.get(templateId); const imageUrls = client.passTemplates.getImageUrls(template.data); console.log("Icon URL:", imageUrls.icon); console.log("Logo URL:", imageUrls.logo); console.log("Cover URL:", imageUrls.cover_image); // Get optimized image URLs for different sizes const optimizedUrls = client.passTemplates.getOptimizedImageUrls(template.data); console.log("Small icon:", optimizedUrls.icon?.small); console.log("Medium logo:", optimizedUrls.logo?.medium); console.log("Large cover:", optimizedUrls.cover_image?.large); // Remove images from template await client.passTemplates.removeImages(templateId, ["icon", "logo"]); ``` ### Project-Based Template Organization Version 3.7 introduces **Projects** - a new organizational layer between Workspaces and Pass Templates that enables template inheritance and better project management. #### Key Features: - **Project Organization**: Group related templates under projects - **Template Inheritance**: Main templates provide base styling, current templates override specific attributes - **Custom Fields Hierarchy**: Project → Template → Pass custom fields with replacement logic - **Template Switching**: Switch passes between templates within the same project #### Working with Projects ```typescript // Create a project const project = await client.projects.create({ workspace_id: "workspace-uuid", name: "Loyalty Program 2024", description: "Our main loyalty program", custom_fields: { companyName: "ACME Corp", region: "North America", year: "2024", }, }); // Get projects in a workspace const projects = await client.projects.getByWorkspace("workspace-uuid"); // Get project with statistics const project = await client.projects.get(project.data.id); console.log("Project statistics:", project.data.statistics); // { // templates_count: 5, // apple_registrations_count: 120, // google_registrations_count: 85, // total_registrations_count: 205, // passes_count: 1250 // } ``` #### Template Inheritance System The template inheritance system allows you to create a **main template** that serves as the base design, and **current templates** that override specific attributes. ```typescript // Create a main template (base design) const mainTemplate = await client.passTemplates.create({ uuid: crypto.randomUUID(), name: "Loyalty Card Base", project_id: project.data.id, workspace_id: "workspace-uuid", is_main_template: true, // Mark as main template pass_type: "LOYALTY", apple_pass_type_identifier: "pass.WalletHero", // Base styling that all templates inherit background_color: "#FF6B6B", label_color: "#FFFFFF", value_color: "#FFFFFF", logo_text: "ACME Corp", // Base template custom fields template_custom_fields: { supportEmail: "support@acme.com", termsUrl: "https://acme.com/terms", }, }); // Create current templates that override specific attributes const goldTemplate = await client.passTemplates.create({ uuid: crypto.randomUUID(), name: "Gold Member Card", project_id: project.data.id, workspace_id: "workspace-uuid", is_main_template: false, // This is a current template pass_type: "LOYALTY", apple_pass_type_identifier: "pass.WalletHero", // Override specific styling background_color: "#FFD700", // Gold color tier_id: "gold", tier_label: "Gold Member", // Override template custom fields template_custom_fields: { supportEmail: "gold@acme.com", // Replaces main template value maxRewards: "500", // New field for gold members }, }); // Create passes using template inheritance const pass = await client.passes.create({ pass_template_id: mainTemplate.data.id, // Base template current_template_id: goldTemplate.data.id, // Override template project_id: project.data.id, workspace_id: "workspace-uuid", full_name: "John Doe", email: "john@example.com", custom_fields: { membershipLevel: "Gold", }, }); ``` #### Template Inheritance Management ```typescript // Get the main template for a project const mainTemplate = await client.projects.getMainTemplate(project.data.id); // Set a template as the main template await client.projects.setMainTemplate(project.data.id, templateId); // Get all templates in a project const projectTemplates = await client.projects.getTemplates(project.data.id); // Get main templates only const mainTemplates = await client.passTemplates.getMainTemplates(); // Get current templates only const currentTemplates = await client.passTemplates.getCurrentTemplates(); // Switch pass to different template within same project await client.passes.switchTemplate(pass.data.id, goldTemplate.data.id); // Reset pass to use original template await client.passes.resetToOriginalTemplate(pass.data.id); ``` #### Custom Fields Hierarchy The system supports a three-level custom fields hierarchy with **replacement logic**: 1. **Project custom fields** - Base level defaults 2. **Template custom fields** - Template-specific overrides 3. **Pass custom fields** - Pass-specific overrides Each level completely replaces the previous level (not merging). ```typescript // Project level custom fields await client.projects.updateCustomFields(project.data.id, { companyName: "ACME Corp", region: "North America", supportEmail: "support@acme.com", }); // Template level custom fields (replaces project level) await client.passTemplates.updateTemplateCustomFields(templateId, { supportEmail: "premium@acme.com", // Overrides project value tier: "Premium", // New field }); // Pass level custom fields (replaces template level) await client.passes.updateCustomFields(pass.data.id, { membershipLevel: "Gold", // Pass-specific data joinDate: "2024-01-15", // New field }); // Get complete pass data with inheritance const completeData = await client.passes.getCompleteData(pass.data.id); console.log("Effective custom fields:", completeData.effectiveCustomFields); console.log("Main template:", completeData.mainTemplate); console.log("Current template:", completeData.currentTemplate); ``` #### Template Tier Management Organize templates by tiers for better categorization: ```typescript // Set template tier await client.passTemplates.setTier(templateId, "premium", "Premium Member"); // Get templates by tier const premiumTemplates = await client.passTemplates.getByTier("premium"); // Remove template tier await client.passTemplates.removeTier(templateId); ``` #### Advanced Template Operations ```typescript // Get passes using template inheritance const inheritancePasses = await client.passes.getPassesWithTemplateInheritance(); // Get passes without template inheritance const standardPasses = await client.passes.getPassesWithoutTemplateInheritance(); // Bulk switch multiple passes to new template await client.passes.bulkSwitchTemplate( ["pass-uuid-1", "pass-uuid-2"], "new-template-uuid" ); // Get complete pass data with all inheritance information const completeData = await client.projects.getCompletePassData(pass.data.id); ``` #### Statistics Support Version 3.7 introduces comprehensive statistics for projects and pass templates, automatically calculated and included in API responses: ```typescript // Project statistics (automatically included) const project = await client.projects.get(projectId); console.log("Project statistics:", project.data.statistics); // { // templates_count: 5, // Number of templates in project // apple_registrations_count: 120, // Apple Wallet registrations // google_registrations_count: 85, // Google Pay registrations // total_registrations_count: 205, // Combined registrations // passes_count: 1250 // Total passes created // } // Pass template statistics (automatically included) const template = await client.passTemplates.get(templateId); console.log("Template statistics:", template.data.statistics); // { // apple_registrations_count: 25, // Apple Wallet registrations for this template // google_registrations_count: 18, // Google Pay registrations for this template // total_registrations_count: 43, // Combined registrations for this template // passes_count: 312 // Total passes created from this template // } // Statistics are included in list operations too const projects = await client.projects.getByWorkspace(workspaceId); projects.data.forEach((project) => { console.log( `${project.name}: ${project.statistics?.passes_count || 0} passes` ); }); const templates = await client.passTemplates.getByProject(projectId); templates.data.forEach((template) => { console.log( `${template.name}: ${ template.statistics?.total_registrations_count || 0 } registrations` ); }); ``` **Statistics Features:** - **Automatic Calculation**: Statistics are calculated server-side and included in API responses - **Real-time Data**: Always up-to-date with the latest counts - **Project Level**: Track overall project performance with template and pass counts - **Template Level**: Monitor individual template performance and adoption - **Platform Breakdown**: Separate counts for Apple Wallet and Google Pay registrations - **Type Safety**: Full TypeScript support with `ProjectStatistics` and `PassTemplateStatistics` types #### iOS Deeplink Configuration Configure iOS deeplinks for pass templates to enable deep linking into your iOS app when users tap on the pass: ```typescript // Set iOS deeplink configuration for a template await client.passTemplates.setIOSDeeplink(templateId, { ios_deeplink_id: 12345, // Numeric identifier for your app ios_deeplink_url: "myapp://loyalty/member", // Custom URL scheme }); // Get current iOS deeplink configuration const deeplinkConfig = await client.passTemplates.getIOSDeeplink(templateId); console.log("Current deeplink config:", deeplinkConfig); // Output: { ios_deeplink_id: 12345, ios_deeplink_url: "myapp://loyalty/member" } // Set only the deeplink ID await client.passTemplates.setIOSDeeplink(templateId, { ios_deeplink_id: 67890, }); // Set only the deeplink URL await client.passTemplates.setIOSDeeplink(templateId, { ios_deeplink_url: "myapp://store/promotions", }); // Remove iOS deeplink configuration await client.passTemplates.removeIOSDeeplink(templateId); // Verify removal const removedConfig = await client.passTemplates.getIOSDeeplink(templateId); console.log("After removal:", removedConfig); // Output: { ios_deeplink_id: undefined, ios_deeplink_url: undefined } // Example: Create template with iOS deeplink from the start const template = await client.passTemplates.create({ uuid: crypto.randomUUID(), name: "Loyalty Card with Deeplink", pass_type: "LOYALTY", apple_pass_type_identifier: "pass.WalletHero", workspace_id: 1, ios_deeplink_id: 12345, ios_deeplink_url: "myapp://loyalty/dashboard", // ... other template fields }); ``` **iOS Deeplink Fields:** - `ios_deeplink_id` (number, optional): Numeric identifier for your iOS app - `ios_deeplink_url` (string, optional): Custom URL scheme that opens your app **Use Cases:** - Direct users to specific screens in your iOS app when they tap the pass - Pass member ID or other data through URL parameters - Create personalized deep links based on pass data - Integrate passes with your existing iOS app navigation **Type Safety:** The SDK provides the `IOSDeeplinkConfig` type for type-safe deeplink configuration: ```typescript import type { IOSDeeplinkConfig } from "@wallethero/sdk"; const config: IOSDeeplinkConfig = { ios_deeplink_id: 12345, ios_deeplink_url: "myapp://member/profile", }; await client.passTemplates.setIOSDeeplink(templateId, config); ``` ### QR Code Service The SDK includes a dedicated QR code service for generating QR codes that link to your passes: ```typescript // Generate QR code for Apple Wallet pass const appleQR = await client.qrCodes.getApplePassQRCode(passId); console.log("Apple QR Code URL:", appleQR); // Direct URL for <img> src // Generate QR code for Google Pay pass const googleQR = await client.qrCodes.getGooglePassQRCode(passId); console.log("Google QR Code URL:", googleQR); // Generate QR code with custom styling options const customQR = await client.qrCodes.getApplePassQRCode(passId, { width: 300, margin: 2, dark: "#000000", light: "#FFFFFF", }); // Get QR code data as JSON response const qrData = await client.qrCodes.getApplePassQRCode(passId, { format: "json", }); console.log("Pass URL:", qrData.url); console.log("QR Code Image URL:", qrData.qrCode); // Get pass URLs directly (for custom QR code generation) const applePassUrl = await client.qrCodes.getApplePassURL(passId); const googlePassUrl = await client.qrCodes.getGooglePassURL(passId); ``` **QR Code Options:** - `width` (number): QR code image width in pixels - `margin` (number): Margin around QR code - `dark` (string): Color for dark modules (hex format) - `light` (string): Color for light modules (hex format) - `format` ("png" | "json"): Return format - PNG URL or JSON with metadata **Use Cases:** - Display QR codes on web pages for pass downloads - Generate custom-styled QR codes matching your brand - Create printable QR codes for physical distribution - Integrate with custom QR code libraries ### File Management The SDK provides direct file management capabilities for advanced use cases: ```typescript // Upload a file directly const fileUpload = await client.files.upload(imageFile, { title: "My Image", description: "Logo for pass template", tags: ["logo", "template"], }); // Get file information const fileInfo = await client.files.get(fileUpload.data.id); // Generate optimized URLs for images const thumbnailUrl = client.files.getFileUrl(fileInfo.data.id, { width: 150, height: 150, fit: "cover", quality: 80, format: "webp", }); // List all files const allFiles = await client.files.list({ filter: { type: { _starts_with: "image/" } }, sort: ["-uploaded_on"], limit: 20, }); // Update file metadata await client.files.update(fileId, { title: "Updated Title", description: "Updated description", }); // Delete a file await client.files.delete(fileId); ``` ### Passes #### Create a Pass ```typescript // Create pass (ID is automatically generated as UUID) const pass = await client.passes.create({ pass_template_id: "template-uuid-here", // Template ID is now UUID workspace_id: 1, full_name: "John Doe", email: "john@example.com", custom_fields: { balance: "$25.00", points: 1250, tier: "Gold", member_since: "2024-01-15", }, }); console.log("Created pass with ID:", pass.data.id); ``` **Note**: The `id` field is read-only and automatically generated as a UUID when creating passes. You cannot provide your own ID. #### Pass Custom Fields Management The SDK provides comprehensive methods for managing custom fields in passes: ```typescript // Update all custom fields at once await client.passes.updateCustomFields(passId, { points: 1500, tier: "Platinum", lastVisit: "2024-12-20", }); // Set a single custom field await client.passes.setCustomField(passId, "bonusPoints", 250); // Get a single custom field value const currentPoints = await client.passes.getCustomField(passId, "points"); console.log("Current points:", currentPoints); // Get all custom fields const allFields = await client.passes.getCustomFields(passId); console.log("All custom fields:", allFields); // Merge new fields without removing existing ones await client.passes.mergeCustomFields(passId, { preferredStore: "Downtown", notifications: true, }); // Check if a field exists const hasEmail = await client.passes.hasCustomField(passId, "email"); // Get all field names const fieldNames = await client.passes.getCustomFieldNames(passId); // Remove a specific field await client.passes.removeCustomField(passId, "bonusPoints"); // Clear all custom fields await client.passes.clearCustomFields(passId); ``` #### Search and Filter Passes by Custom Fields ```typescript // Search passes by a specific custom field value const goldMembers = await client.passes.searchByCustomField( "membershipLevel", "Gold" ); // Filter by multiple custom field criteria const activePlatinumMembers = await client.passes.filterByCustomFields({ membershipLevel: "Platinum", notifications: true, status: "active", }); // Combine with other query options const recentGoldMembers = await client.passes.searchByCustomField( "membershipLevel", "Gold", { filter: { date_created: { _gte: "2024-01-01" }, }, sort: ["-date_created"], limit: 50, } ); ``` #### Bulk Operations ```typescript // Bulk update custom fields for multiple passes await client.passes.bulkUpdateCustomFields([ { passId: "pass-uuid-1", customFields: { lastPromotion: "2024-12-20", tier: "Gold" }, }, { passId: "pass-uuid-2", customFields: { lastPromotion: "2024-12-20", tier: "Silver" }, }, ]); // Bulk create passes from template const newPasses = await client.passes.bulkCreateFromTemplate(templateId, [ { workspace_id: 1, full_name: "Alice Johnson", email: "alice@example.com", custom_fields: { tier: "Gold", points: 1000 }, }, { workspace_id: 1, full_name: "Bob Smith", email: "bob@example.com", custom_fields: { tier: "Silver", points: 500 }, }, ]); ``` #### Get Passes ```typescript // Get all passes const allPasses = await client.passes.list(); // Get specific pass by ID const pass = await client.passes.get(passId); // Get passes with filtering and pagination const filteredPasses = await client.passes.list({ filter: { workspace_id: { _eq: 1 } }, sort: ["-date_created"], limit: 20, offset: 0, }); // Get passes by template const templatePasses = await client.passes.getByTemplate(templateId); // Get passes by workspace const workspacePasses = await client.passes.getByWorkspace(workspaceId); ``` #### Update and Delete Passes ```typescript // Update pass information const updatedPass = await client.passes.update(passId, { full_name: "Updated Name", email: "updated@example.com", notification: "Your pass has been updated!", }); // Delete a pass await client.passes.delete(passId); ``` ### Workspaces Workspaces provide organization and isolation for your pass templates, passes, and other resources. The workspace service allows you to manage workspaces and their members. #### Create a Workspace ```typescript // Create a new workspace const workspace = await client.workspaces.create({ name: "My Organization Workspace", }); console.log("Created workspace:", workspace.data.id); console.log("Workspace name:", workspace.data.name); ``` #### Get Workspaces ```typescript // Get all workspaces the current user has access to const workspaces = await client.workspaces.list(); console.log(`Found ${workspaces.data.length} workspaces`); // Get a specific workspace by ID const workspace = await client.workspaces.get(workspaceId); console.log("Workspace details:", workspace.data); // Get current user's workspaces (same as list) const myWorkspaces = await client.workspaces.getMy(); // Get workspace with related data (passes, registrations, etc.) const workspaceWithRelations = await client.workspaces.getWithRelations( workspaceId ); console.log("Related passes:", workspaceWithRelations.data); // Get workspace with custom fields const workspaceWithCustomFields = await client.workspaces.getWithRelations( workspaceId, [ "*", // All workspace fields "passes.id", "passes.full_name", "passes.custom_fields", "apple_registrations.device_library_identifier", "google_registrations.class_id", ] ); ``` #### Search and Filter Workspaces ```typescript // Search workspaces by name const searchResults = await client.workspaces.search("Production"); console.log( `Found ${searchResults.data.length} workspaces matching "Production"` ); // Get workspaces by exact name match const specificWorkspaces = await client.workspaces.getByName( "Production Environment" ); if (specificWorkspaces.data.length > 0) { console.log("Found production workspace:", specificWorkspaces.data[0]); } // Advanced filtering and pagination const filteredWorkspaces = await client.workspaces.list({ fields: ["id", "name", "date_created"], filter: { name: { _contains: "Project" }, }, sort: ["-date_created"], limit: 10, offset: 0, }); console.log("Filtered workspaces:", filteredWorkspaces.data); ``` #### Update and Delete Workspaces ```typescript // Update workspace information const updatedWorkspace = await client.workspaces.update(workspaceId, { name: "Updated Workspace Name", }); console.log("Updated name:", updatedWorkspace.data.name); // Delete a workspace await client.workspaces.delete(workspaceId); console.log("Workspace deleted"); ``` #### Workspace Statistics ```typescript // Get comprehensive workspace statistics const stats = await client.workspaces.getStats(workspaceId); console.log("Workspace Statistics:"); console.log(`- Passes: ${stats.passCount}`); console.log(`- Templates: ${stats.templateCount}`); console.log(`- Apple Registrations: ${stats.appleRegistrationCount}`); console.log(`- Google Registrations: ${stats.googleRegistrationCount}`); // Example output: // Workspace Statistics: // - Passes: 1,250 // - Templates: 15 // - Apple Registrations: 892 // - Google Registrations: 358 ``` #### Workspace Member Management ```typescript // Get workspace members const members = await client.workspaces.getMembers(workspaceId); console.log(`Workspace has ${members.data.length} members`); // List all members with details members.data.forEach((member) => { const user = member.directus_users_id; console.log(`Member: ${user.first_name} ${user.last_name} (${user.email})`); }); // Add a member to workspace await client.workspaces.addMember(workspaceId, userId); console.log("Member added successfully"); // Remove a member from workspace await client.workspaces.removeMember(workspaceId, userId); console.log("Member removed successfully"); ``` #### Workspace Utilities ```typescript // Check if a workspace name is available const isAvailable = await client.workspaces.isNameAvailable( "New Workspace Name" ); if (isAvailable) { console.log("Name is available!"); // Create workspace with this name const newWorkspace = await client.workspaces.create({ name: "New Workspace Name", }); } else { console.log("Name is already taken"); } // Workspace-related resource management // Get all templates in a workspace const templates = await client.passTemplates.getByWorkspace(workspaceId); console.log(`Workspace has ${templates.data.length} templates`); // Get all passes in a workspace const passes = await client.passes.getByWorkspace(workspaceId); console.log(`Workspace has ${passes.data.length} passes`); ``` #### Complete Workspace Management Example ```typescript async function manageWorkspace() { const client = new WalletHero({ apiToken: "your-api-token", }); try { // Create a new workspace const workspace = await client.workspaces.create({ name: "Event Management Workspace", }); const workspaceId = workspace.data.id; console.log(`Created workspace: ${workspaceId}`); // Create a pass template in this workspace const template = await client.passTemplates.create({ uuid: crypto.randomUUID(), name: "Event Ticket Template", pass_type: "EVENT_TICKET", apple_pass_type_identifier: "pass.WalletHero", workspace_id: workspaceId, background_color: "#FF6B6B", front_fields: [ { label: "Event", value: "${payload.eventName}" }, { label: "Date", value: "${payload.eventDate}" }, ], }); // Create passes from the template const passes = await Promise.all([ client.passes.create({ pass_template_id: template.data.id, workspace_id: workspaceId, full_name: "Alice Johnson", email: "alice@example.com", custom_fields: { eventName: "Tech Conference 2024", eventDate: "March 15, 2024", }, }), client.passes.create({ pass_template_id: template.data.id, workspace_id: workspaceId, full_name: "Bob Smith", email: "bob@example.com", custom_fields: { eventName: "Tech Conference 2024", eventDate: "March 15, 2024", }, }), ]); // Get workspace statistics const stats = await client.workspaces.getStats(workspaceId); console.log("Final stats:", stats); // Output: { passCount: 2, templateCount: 1, appleRegistrationCount: 0, googleRegistrationCount: 0 } // Get workspace with all related data const fullWorkspace = await client.workspaces.getWithRelations(workspaceId); console.log("Workspace with relations:", fullWorkspace.data); } catch (error) { console.error("Error managing workspace:", error); } } ``` **Workspace Fields:** - `id` (string, UUID): Unique workspace identifier - `name` (string): Human-readable workspace name - `user_created` (string, UUID): ID of user who created the workspace - `date_created` (string, ISO timestamp): Creation timestamp - `user_updated` (string, UUID): ID of user who last updated the workspace - `date_updated` (string, ISO timestamp): Last update timestamp **Use Cases:** - Organize resources by customer, project, or environment - Implement multi-tenant applications with workspace isolation - Manage team access and permissions through workspace membership - Generate analytics and reporting per workspace - Separate development, staging, and production environments ## API Reference ### Client Services The WalletHero SDK provides the following services: - **client.passTemplates**: Pass template management - **client.passes**: Pass management and custom fields - **client.workspaces**: Workspace management and organization - **client.files**: File upload and management - **client.qrCodes**: QR code generation - **client.ping()**: Test API connectivity - **client.me()**: Get current user information - **client.serverInfo()**: Get server status and information ### Type Definitions The SDK exports comprehensive TypeScript types: ```typescript import type { // Core entities PassTemplate, Pass, Workspace, // Utility types PassType, BarcodeType, PassField, CustomField, Location, IOSDeeplinkConfig, // Request types CreatePassTemplateRequest, UpdatePassTemplateRequest, CreatePassRequest, UpdatePassRequest, CreateWorkspaceRequest, UpdateWorkspaceRequest, // Response types ApiResponse, ApiListResponse, ApiError, // File handling DirectusFile, FileUploadOptions, TemplateImageUpdate, // QR Code QRCodeOptions, QRCodeResponse, // Configuration WalletHeroConfig, QueryOptions, } from "@wallethero/sdk"; ``` ### Supported Pass Types - `LOYALTY`: Loyalty cards and membership programs - `GIFT_CARD`: Gift cards and store credit - `EVENT_TICKET`: Event tickets and admissions - `OFFER`: Coupons and promotional offers - `GENERIC`: General purpose passes ### Supported Barcode Types - `qr`: QR Code (default, widely supported) - `aztec`: Aztec code (compact format) - `code128`: Code 128 (numeric and alphanumeric) - `pdf417`: PDF417 (high data capacity) - `none`: No barcode ### Error Handling The SDK throws `WalletHeroError` for API-related errors: ```typescript import { WalletHeroError } from "@wallethero/sdk"; try { const pass = await client.passes.get("invalid-id"); } catch (error) { if (error instanceof WalletHeroError) { console.error("API Error:", error.message); console.error("Status:", error.status); console.error("Details:", error.details); } } ``` ## Contributing This SDK is part of the WalletHero platform. For questions, support, or feature requests, please contact the WalletHero team. ## License This SDK is proprietary software. See your WalletHero license agreement for usage terms.