Skip to content

Event Tickets Example

Issue and manage event tickets with check-in tracking.

Setup

typescript
import { WalletHero } from '@wallethero/sdk';

const client = new WalletHero({
  apiToken: process.env.WALLETHERO_API_TOKEN!
});

const WORKSPACE_ID = 'your-workspace-id';
const PROJECT_ID = 'your-project-id';

1. Create Event Template

typescript
const concertTemplate = await client.passTemplates.create({
  name: 'Summer Concert 2024',
  workspace_id: WORKSPACE_ID,
  project_id: PROJECT_ID,
  apple_pass_type_identifier: 'pass.com.example.events',
  background_color: '#1a1a2e',
  label_color: '#e94560',
  value_color: '#ffffff',
  logo_text: 'SUMMER FEST',
  barcode_type: 'qr',
  barcode_id: 'id',  // Use pass ID as barcode
  top_field_label: 'SECTION',
  top_field_value: '{{section}}',
  front_fields: [
    { label: 'Date', value: '{{event_date}}' },
    { label: 'Time', value: '{{event_time}}' },
    { label: 'Seat', value: '{{seat}}' }
  ],
  secondary_fields: [
    { label: 'Guest', value: '{{full_name}}' }
  ],
  back_fields: [
    { label: 'Venue', value: 'Central Park Arena' },
    { label: 'Address', value: '123 Park Ave, New York, NY' },
    { label: 'Terms', value: 'No refunds. Must present valid ID.' }
  ],
  locations: [
    {
      latitude: 40.7829,
      longitude: -73.9654,
      name: 'Central Park Arena'
    }
  ],
  location_message: 'Welcome to Summer Fest!'
});

// Upload event artwork
await client.passTemplates.uploadCoverImage(
  concertTemplate.id,
  eventBannerFile
);

2. Issue Tickets

typescript
interface TicketOrder {
  name: string;
  email: string;
  section: string;
  row: string;
  seat: string;
  ticketType: 'GA' | 'VIP' | 'Premium';
}

async function issueTicket(order: TicketOrder) {
  const ticket = await client.passes.create({
    pass_template_id: concertTemplate.id,
    project_id: PROJECT_ID,
    workspace_id: WORKSPACE_ID,
    full_name: order.name,
    email: order.email,
    custom_fields: {
      section: order.section,
      row: order.row,
      seat: order.seat,
      ticket_type: order.ticketType,
      event_date: 'July 15, 2024',
      event_time: '7:00 PM',
      checked_in: false,
      order_id: `ORD-${Date.now()}`
    }
  });

  // Record ticket purchase event
  await client.events.create({
    pass_id: ticket.id,
    event_category: 'activity',
    event_type: 'custom',
    metadata: {
      action: 'ticket_purchased',
      ticket_type: order.ticketType,
      section: order.section
    }
  });

  return ticket;
}

// Issue tickets for an order
const tickets = await Promise.all([
  issueTicket({
    name: 'John Doe',
    email: '[email protected]',
    section: 'A',
    row: '5',
    seat: '12',
    ticketType: 'VIP'
  }),
  issueTicket({
    name: 'Jane Doe',
    email: '[email protected]',
    section: 'A',
    row: '5',
    seat: '13',
    ticketType: 'VIP'
  })
]);

console.log('Tickets issued:');
tickets.forEach(t => {
  console.log(`- ${t.full_name}: ${t.apple_pass_url}`);
});

3. Check-In System

typescript
async function checkIn(passId: string, gate: string) {
  const pass = await client.passes.get(passId);

  // Check if already checked in
  if (pass.custom_fields?.checked_in) {
    return {
      success: false,
      error: 'Already checked in',
      checkedInAt: pass.custom_fields.checked_in_at
    };
  }

  // Record check-in
  await client.events.recordCheckIn(passId, {
    source: gate,
    metadata: {
      gate,
      section: pass.custom_fields?.section
    }
  });

  // Update pass status
  await client.passes.update(passId, {
    notification: 'Welcome! Enjoy the show!',
    custom_fields: {
      checked_in: true,
      checked_in_at: new Date().toISOString(),
      checked_in_gate: gate
    }
  });

  return {
    success: true,
    guest: pass.full_name,
    section: pass.custom_fields?.section,
    seat: pass.custom_fields?.seat
  };
}

// Simulate scanning a ticket
const result = await checkIn('ticket-pass-id', 'Gate A');
if (result.success) {
  console.log(`✓ Checked in ${result.guest} - Section ${result.section}, Seat ${result.seat}`);
} else {
  console.log(`✗ ${result.error}`);
}

4. Real-Time Updates

typescript
// Send reminder before event
async function sendEventReminder() {
  // Get all tickets for tomorrow's event
  const tomorrow = new Date();
  tomorrow.setDate(tomorrow.getDate() + 1);

  const tickets = await client.passes.list({
    filter: {
      pass_template_id: { _eq: concertTemplate.id },
      custom_fields: {
        checked_in: { _eq: false }
      }
    }
  });

  // Send reminder to each
  for (const ticket of tickets) {
    await client.passes.update(ticket.id, {
      notification: '🎵 See you tomorrow! Doors open at 6 PM.'
    });
  }

  console.log(`Sent reminders to ${tickets.length} ticket holders`);
}

// Send last-minute gate change
async function announceGateChange(section: string, newGate: string) {
  const affected = await client.passes.list({
    filter: {
      pass_template_id: { _eq: concertTemplate.id },
      custom_fields: {
        section: { _eq: section }
      }
    }
  });

  for (const ticket of affected) {
    await client.passes.update(ticket.id, {
      notification: `Gate change! Section ${section} now enters via ${newGate}.`,
      custom_fields: {
        entry_gate: newGate
      }
    });
  }

  console.log(`Notified ${affected.length} guests in Section ${section}`);
}

5. Analytics

typescript
// Get check-in statistics
async function getCheckInStats() {
  const total = await client.passes.list({
    filter: { pass_template_id: { _eq: concertTemplate.id } }
  });

  const checkedIn = total.filter(p => p.custom_fields?.checked_in);

  // Check-ins by gate
  const checkIns = await client.events.list({
    workspace_id: WORKSPACE_ID,
    event_category: 'activity'
  });

  const byGate = checkIns.reduce((acc, e) => {
    const gate = e.metadata?.gate || 'Unknown';
    acc[gate] = (acc[gate] || 0) + 1;
    return acc;
  }, {} as Record<string, number>);

  return {
    totalTickets: total.length,
    checkedIn: checkedIn.length,
    remaining: total.length - checkedIn.length,
    byGate
  };
}

const stats = await getCheckInStats();
console.log(`Check-in Status:`);
console.log(`  Total: ${stats.totalTickets}`);
console.log(`  Checked In: ${stats.checkedIn} (${(stats.checkedIn/stats.totalTickets*100).toFixed(1)}%)`);
console.log(`  Remaining: ${stats.remaining}`);
console.log(`  By Gate:`, stats.byGate);

WalletHero Documentation