TypeScript/JavaScript SDK for WalletHero API - Manage pass templates and passes for Apple Wallet and Google Pay.
Project-Based Template Organization & Inheritance: Version 3.7 introduces a comprehensive project system with template inheritance capabilities.
- 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
, andTemplateMergeOptions
types
iOS Deeplink Support: Version 3.3 added comprehensive iOS deeplink configuration for pass templates.
- 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
Enhanced Barcode Support: Version 2.1 added support for multiple barcode formats and header fields.
- 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
// 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",
});
// 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}",
});
UUID Migration: Version 2.0 introduces a major change where all pass and pass template IDs are now UUIDs instead of integers.
-
Pass IDs: Changed from
number
tostring
(UUID format) -
Pass Template IDs: Changed from
number
tostring
(UUID format) -
QR Code Service: Methods now use
passId
instead ofserialNumber
// ❌ 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
- All legacy
serialNumber
methods have been removed - You must update your code to use UUID-based methods before upgrading
npm install @wallethero/sdk
Requirements:
- Node.js 18+ (uses native fetch API)
- TypeScript 4+ (optional, but recommended for type safety)
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);
The WalletHero client provides several utility methods for testing connectivity and getting account information:
// 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
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 by default.
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",
});
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 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");
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.
const duplicatedTemplate = await client.passTemplates.duplicate(templateId);
console.log("Duplicated template:", duplicatedTemplate.data.name); // "Original Name (Copy)"
WalletHero supports multiple barcode formats and variable interpolation for dynamic content:
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
});
Configure barcodes with data, type, and optional label:
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
Use ${payload.key}
and ${pass.key}
variables to create dynamic content:
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 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.
// 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 appear prominently at the top of passes and are perfect for key identifiers:
// 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"
The SDK supports uploading and managing images for pass templates. Supported formats include PNG, JPEG, WebP, and other common image formats.
// 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"]);
Version 3.7 introduces Projects - a new organizational layer between Workspaces and Pass Templates that enables template inheritance and better project management.
- 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
// 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
// }
The template inheritance system allows you to create a main template that serves as the base design, and current templates that override specific attributes.
// 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",
},
});
// 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);
The system supports a three-level custom fields hierarchy with replacement logic:
- Project custom fields - Base level defaults
- Template custom fields - Template-specific overrides
- Pass custom fields - Pass-specific overrides
Each level completely replaces the previous level (not merging).
// 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);
Organize templates by tiers for better categorization:
// 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);
// 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);
Version 3.7 introduces comprehensive statistics for projects and pass templates, automatically calculated and included in API responses:
// 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
andPassTemplateStatistics
types
Configure iOS deeplinks for pass templates to enable deep linking into your iOS app when users tap on the pass:
// 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:
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);
The SDK includes a dedicated QR code service for generating QR codes that link to your passes:
// 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
The SDK provides direct file management capabilities for advanced use cases:
// 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);
// 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.
The SDK provides comprehensive methods for managing custom fields in passes:
// 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 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 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 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 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 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 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 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 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 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");
// 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
// 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");
// 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`);
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
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
The SDK exports comprehensive TypeScript types:
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";
-
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
-
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
The SDK throws WalletHeroError
for API-related errors:
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);
}
}
This SDK is part of the WalletHero platform. For questions, support, or feature requests, please contact the WalletHero team.
This SDK is proprietary software. See your WalletHero license agreement for usage terms.