> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fanfare.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Event ticketing

# Event Ticketing Use Case

Learn how to use Fanfare to manage ticket sales for high-demand events with fair access and controlled distribution.

## Overview

Popular events often see massive demand that exceeds available tickets. Fanfare helps you manage ticket sales fairly, prevent scalping, and create a positive experience for fans.

**What you'll learn:**

* Setting up event ticket queues
* Managing tiered ticket releases
* Implementing purchase limits
* Preventing scalping and bots
* Handling ticket transfers

**Complexity:** Intermediate
**Time to complete:** 45 minutes

## Prerequisites

* Fanfare account with queue feature enabled
* Event and ticketing system configured
* Understanding of your venue capacity
* Marketing plan for ticket release

## When to Use Fanfare for Ticketing

| Scenario                    | Fanfare Solution            |
| --------------------------- | --------------------------- |
| High demand (>10x capacity) | Queue with rate limiting    |
| VIP/presale access          | Audience-based early access |
| Fair distribution required  | Strict FIFO queue           |
| Bot prevention needed       | Authentication + CAPTCHA    |
| Multiple ticket tiers       | Separate queues per tier    |

## Step 1: Configure Event Structure

### Event and Ticket Configuration

```typescript theme={null}
// types/event-ticketing.ts
interface EventConfig {
  eventId: string;
  eventName: string;
  venue: {
    name: string;
    city: string;
    capacity: number;
  };
  date: Date;
  doors: string;
  startTime: string;

  // Ticket tiers
  ticketTiers: TicketTier[];

  // Purchase rules
  maxTicketsPerOrder: number;
  maxOrdersPerCustomer: number;
  requireIdentification: boolean;

  // Anti-scalping
  ticketsNonTransferable: boolean;
  requireBuyerAttendance: boolean;
}

interface TicketTier {
  tierId: string;
  name: string;
  price: string;
  quantity: number;
  description: string;
  perks?: string[];
  releaseTime?: Date; // If different from general sale
}

// Example event configuration
const concertConfig: EventConfig = {
  eventId: "concert-2024-fall-tour",
  eventName: "Fall Tour 2024 - New York",
  venue: {
    name: "Madison Square Garden",
    city: "New York, NY",
    capacity: 20000,
  },
  date: new Date("2024-11-15"),
  doors: "7:00 PM",
  startTime: "8:00 PM",

  ticketTiers: [
    {
      tierId: "vip",
      name: "VIP Experience",
      price: "500.00",
      quantity: 500,
      description: "Premium seating + meet & greet",
      perks: ["Meet & Greet", "Early entry", "Exclusive merch", "Premium seating"],
    },
    {
      tierId: "floor",
      name: "Floor (General Admission)",
      price: "150.00",
      quantity: 5000,
      description: "Standing floor access",
    },
    {
      tierId: "lower-bowl",
      name: "Lower Bowl",
      price: "100.00",
      quantity: 8000,
      description: "Lower level reserved seating",
    },
    {
      tierId: "upper-bowl",
      name: "Upper Bowl",
      price: "60.00",
      quantity: 6500,
      description: "Upper level reserved seating",
    },
  ],

  maxTicketsPerOrder: 4,
  maxOrdersPerCustomer: 1,
  requireIdentification: true,
  ticketsNonTransferable: true,
  requireBuyerAttendance: true,
};
```

## Step 2: Create Ticket Sale Queues

### Admin Configuration

```typescript theme={null}
async function createEventTicketingExperience(event: EventConfig) {
  // Main ticket sale queue
  const mainQueue = await createFanfareExperience({
    name: `${event.eventName} - General Sale`,
    slug: `tickets-${event.eventId}`,

    // Sale timing
    scheduledStart: new Date("2024-09-01T10:00:00Z"),

    config: {
      // Capacity management
      maxConcurrentAdmissions: 200,
      admissionWindowSeconds: 600, // 10 minutes to complete purchase
      admissionRatePerMinute: 300,

      // Fair access
      fairnessMode: "strict",
      requireAuthentication: true,

      // Bot prevention
      enableCaptcha: true,
      captchaThreshold: "medium",

      // Purchase limits
      maxPurchasesPerConsumer: event.maxOrdersPerCustomer,
    },

    metadata: {
      eventId: event.eventId,
      eventName: event.eventName,
      eventDate: event.date.toISOString(),
      venue: event.venue.name,
      totalTickets: event.ticketTiers.reduce((sum, t) => sum + t.quantity, 0),
    },
  });

  // Create presale queues for different audiences
  await createPresaleQueues(event, mainQueue.id);

  return mainQueue;
}

async function createPresaleQueues(event: EventConfig, mainQueueId: string) {
  // Artist presale
  const artistPresale = await createFanfareExperience({
    name: `${event.eventName} - Artist Presale`,
    slug: `presale-artist-${event.eventId}`,
    scheduledStart: new Date("2024-08-28T10:00:00Z"),
    scheduledEnd: new Date("2024-08-28T22:00:00Z"),

    config: {
      maxConcurrentAdmissions: 100,
      admissionWindowSeconds: 600,
      requireAuthentication: true,
      requireAccessCode: true,
    },

    accessCodes: [
      {
        code: "FALLVIBES2024",
        maxUses: 5000,
        description: "Artist fan club presale",
      },
    ],
  });

  // Venue presale
  const venuePresale = await createFanfareExperience({
    name: `${event.eventName} - Venue Presale`,
    slug: `presale-venue-${event.eventId}`,
    scheduledStart: new Date("2024-08-29T10:00:00Z"),
    scheduledEnd: new Date("2024-08-29T22:00:00Z"),

    config: {
      maxConcurrentAdmissions: 100,
      admissionWindowSeconds: 600,
      requireAuthentication: true,
      requireAccessCode: true,
    },

    accessCodes: [
      {
        code: "MSGVIP2024",
        maxUses: 3000,
        description: "MSG member presale",
      },
    ],
  });

  // Credit card presale
  const ccPresale = await createFanfareExperience({
    name: `${event.eventName} - Card Presale`,
    slug: `presale-card-${event.eventId}`,
    scheduledStart: new Date("2024-08-30T10:00:00Z"),
    scheduledEnd: new Date("2024-08-30T22:00:00Z"),

    config: {
      maxConcurrentAdmissions: 150,
      admissionWindowSeconds: 600,
      requireAuthentication: true,
    },

    metadata: {
      presaleType: "credit_card",
      eligibleCards: ["amex", "chase"],
    },
  });

  return { artistPresale, venuePresale, ccPresale };
}
```

## Step 3: Build the Ticket Purchase Experience

### Event Ticket Page

```tsx theme={null}
// pages/events/[eventId]/tickets.tsx
import { FanfareProvider } from "@fanfare-io/fanfare-sdk-react";
import { TicketPurchaseExperience } from "@/components/TicketPurchaseExperience";
import { EventInfo } from "@/components/EventInfo";

interface TicketPageProps {
  experienceId: string;
  event: EventConfig;
  salePhase: "presale" | "general" | "ended";
  presaleCode?: string;
}

export default function TicketPage({ experienceId, event, salePhase, presaleCode }: TicketPageProps) {
  return (
    <FanfareProvider
      organizationId={process.env.NEXT_PUBLIC_FANFARE_ORG_ID!}
      publishableKey={process.env.NEXT_PUBLIC_FANFARE_PUBLISHABLE_KEY!}
    >
      <div className="ticket-page">
        <EventInfo event={event} />

        {salePhase === "ended" ? (
          <SoldOutState event={event} />
        ) : (
          <TicketPurchaseExperience experienceId={experienceId} event={event} presaleCode={presaleCode} />
        )}
      </div>
    </FanfareProvider>
  );
}

function SoldOutState({ event }: { event: EventConfig }) {
  return (
    <div className="sold-out-state">
      <h2>Sold Out</h2>
      <p>All tickets for {event.eventName} have been sold.</p>

      <div className="alternatives">
        <h3>Options</h3>
        <ul>
          <li>
            <a href={`/events/${event.eventId}/waitlist`}>Join the waitlist</a>
          </li>
          <li>
            <a href={`/events/${event.eventId}/resale`}>Check official resale</a>
          </li>
          <li>
            <a href="/events">Browse other events</a>
          </li>
        </ul>
      </div>
    </div>
  );
}
```

### Ticket Purchase Experience Component

```tsx theme={null}
// components/TicketPurchaseExperience.tsx
import { useExperienceJourney } from "@fanfare-io/fanfare-sdk-react";
import type { SequenceView } from "@fanfare-io/fanfare-sdk-core/experiences";
import { useState, useEffect } from "react";

interface TicketPurchaseExperienceProps {
  experienceId: string;
  event: EventConfig;
  presaleCode?: string;
}

export function TicketPurchaseExperience({ experienceId, event, presaleCode }: TicketPurchaseExperienceProps) {
  const { view, start } = useExperienceJourney(experienceId, {
    autoStart: !presaleCode, // Auto-start if no presale code needed
  });

  // Handle presale code entry
  const [codeEntered, setCodeEntered] = useState(false);

  const handleCodeSubmit = (code: string) => {
    void start({ accessCode: code });
    setCodeEntered(true);
  };

  // Show presale code form if required
  if (presaleCode && !codeEntered && view?.journeyStage === "ready") {
    return <PresaleCodeForm onSubmit={handleCodeSubmit} />;
  }

  if (!view) return <LoadingState message="Joining the queue..." />;
  if (view.journeyStage === "ready") return <SaleNotStartedState event={event} onStart={start} />;
  if (view.journeyStage === "routing") return <LoadingState message="Joining the queue..." />;
  if (view.journeyStage === "gated") return <GateState view={view} />;
  if (view.sequence.phase === "participating") return <QueueState sequence={view.sequence} event={event} />;
  if (view.sequence.phase === "granted") return <TicketSelectionState sequence={view.sequence} event={event} />;
  if (view.sequence.phase === "ended") return <SessionExpiredState onRetry={start} />;

  return null;
}

function PresaleCodeForm({ onSubmit }: { onSubmit: (code: string) => void }) {
  const [code, setCode] = useState("");
  const [error, setError] = useState("");

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    if (!code.trim()) {
      setError("Please enter your presale code");
      return;
    }
    onSubmit(code.toUpperCase());
  };

  return (
    <div className="presale-code-form">
      <h2>Enter Presale Code</h2>
      <p>You need a valid presale code to access this sale.</p>

      <form onSubmit={handleSubmit}>
        <input
          type="text"
          value={code}
          onChange={(e) => setCode(e.target.value)}
          placeholder="Enter code"
          maxLength={20}
        />
        {error && <span className="error">{error}</span>}
        <button type="submit">Access Presale</button>
      </form>
    </div>
  );
}

function SaleNotStartedState({ event, onStart }: { event: EventConfig; onStart: () => Promise<unknown> }) {
  return (
    <div className="sale-not-started">
      <h2>Tickets On Sale Soon</h2>

      <button onClick={() => void onStart()}>Check access</button>

      <div className="event-preview">
        <h3>{event.eventName}</h3>
        <p>
          {event.venue.name} - {event.venue.city}
        </p>
        <p>{event.date.toLocaleDateString()}</p>
      </div>

      <div className="ticket-tiers-preview">
        <h3>Ticket Options</h3>
        <ul>
          {event.ticketTiers.map((tier) => (
            <li key={tier.tierId}>
              <strong>{tier.name}</strong> - ${tier.price}
              <p>{tier.description}</p>
            </li>
          ))}
        </ul>
      </div>

      <div className="preparation-tips">
        <h3>Get Ready</h3>
        <ul>
          <li>Create an account now to save time</li>
          <li>Have your payment information ready</li>
          <li>Know which ticket type you want</li>
          <li>You'll have 10 minutes to complete your purchase</li>
        </ul>
      </div>
    </div>
  );
}

function QueueState({ sequence, event }: { sequence: SequenceView; event: EventConfig }) {
  return (
    <div className="queue-state">
      <div className="queue-header">
        <h2>You're in the Queue</h2>
        <p>Please keep this page open</p>
      </div>

      <div className="queue-position">
        <div className="position-circle">
          <span className="position">{sequence.phase}</span>
          <span className="label">Current status</span>
        </div>
      </div>

      <div className="queue-stats">
        <div className="stat">
          <span className="value">Watch for admission</span>
          <span className="label">Next step</span>
        </div>
      </div>

      <div className="event-reminder">
        <p>
          <strong>{event.eventName}</strong>
        </p>
        <p>
          {event.venue.name} - {event.date.toLocaleDateString()}
        </p>
      </div>

      <div className="queue-tips">
        <h4>While you wait:</h4>
        <ul>
          <li>Don't refresh this page</li>
          <li>Decide on your ticket preference</li>
          <li>Have payment info ready</li>
        </ul>
      </div>
    </div>
  );
}

function TicketSelectionState({
  sequence,
  event,
}: {
  sequence: Extract<SequenceView, { phase: "granted" }>;
  event: EventConfig;
}) {
  const expiresAt = sequence.grant.expiresAt ? new Date(sequence.grant.expiresAt).getTime() : undefined;
  const [selectedTier, setSelectedTier] = useState<TicketTier | null>(null);
  const [quantity, setQuantity] = useState(1);
  const [ticketAvailability, setTicketAvailability] = useState<Map<string, number>>(new Map());

  // Fetch live availability
  useEffect(() => {
    const fetchAvailability = async () => {
      const availability = await getTicketAvailability(event.eventId);
      setTicketAvailability(availability);
    };

    fetchAvailability();
    const interval = setInterval(fetchAvailability, 10000);
    return () => clearInterval(interval);
  }, [event.eventId]);

  const handleProceedToCheckout = () => {
    if (!selectedTier) return;

    const orderData = {
      eventId: event.eventId,
      tierId: selectedTier.tierId,
      quantity,
      admissionGrant: sequence.grant.token,
    };

    sessionStorage.setItem("ticket_order", JSON.stringify(orderData));
    window.location.href = `/events/${event.eventId}/checkout`;
  };

  return (
    <div className="ticket-selection">
      <div className="selection-header">
        <h2>Select Your Tickets</h2>
        <div className="session-timer urgent">
          <span className="label">Time remaining:</span>
          <CountdownTimer targetTime={expiresAt} />
        </div>
      </div>

      <div className="ticket-tiers">
        {event.ticketTiers.map((tier) => {
          const available = ticketAvailability.get(tier.tierId) ?? tier.quantity;
          const isAvailable = available > 0;
          const isSelected = selectedTier?.tierId === tier.tierId;

          return (
            <div
              key={tier.tierId}
              className={`ticket-tier ${isSelected ? "selected" : ""} ${!isAvailable ? "sold-out" : ""}`}
              onClick={() => isAvailable && setSelectedTier(tier)}
            >
              <div className="tier-info">
                <h3>{tier.name}</h3>
                <p className="description">{tier.description}</p>
                {tier.perks && (
                  <ul className="perks">
                    {tier.perks.map((perk) => (
                      <li key={perk}>{perk}</li>
                    ))}
                  </ul>
                )}
              </div>

              <div className="tier-price">
                <span className="price">${tier.price}</span>
                {isAvailable ? (
                  <span className="availability">{available.toLocaleString()} available</span>
                ) : (
                  <span className="sold-out-badge">Sold Out</span>
                )}
              </div>

              {isSelected && <span className="selected-badge">Selected</span>}
            </div>
          );
        })}
      </div>

      {selectedTier && (
        <div className="quantity-selection">
          <h3>How many tickets?</h3>
          <div className="quantity-controls">
            <button onClick={() => setQuantity(Math.max(1, quantity - 1))} disabled={quantity <= 1}>
              -
            </button>
            <span className="quantity">{quantity}</span>
            <button
              onClick={() => setQuantity(Math.min(event.maxTicketsPerOrder, quantity + 1))}
              disabled={quantity >= event.maxTicketsPerOrder}
            >
              +
            </button>
          </div>
          <p className="limit-notice">Maximum {event.maxTicketsPerOrder} tickets per order</p>
        </div>
      )}

      {selectedTier && (
        <div className="order-summary">
          <h3>Order Summary</h3>
          <div className="summary-line">
            <span>
              {quantity}x {selectedTier.name}
            </span>
            <span>${(selectedTier.price * quantity).toFixed(2)}</span>
          </div>
          <div className="summary-line fees">
            <span>Service fees</span>
            <span>${(selectedTier.price * quantity * 0.1).toFixed(2)}</span>
          </div>
          <div className="summary-line total">
            <span>Total</span>
            <span>${(selectedTier.price * quantity * 1.1).toFixed(2)}</span>
          </div>

          <button onClick={handleProceedToCheckout} className="checkout-btn">
            Proceed to Checkout
          </button>
        </div>
      )}
    </div>
  );
}

```

## Step 4: Anti-Scalping Measures

### Purchase Verification

```typescript theme={null}
// services/ticket-verification.ts
interface PurchaseVerification {
  consumerId: string;
  eventId: string;
  orderDetails: OrderDetails;
}

export async function verifyTicketPurchase(verification: PurchaseVerification) {
  const { consumerId, eventId, orderDetails } = verification;

  // Check purchase limits
  const existingOrders = await db.query.ticketOrders.findMany({
    where: and(eq(ticketOrders.consumerId, consumerId), eq(ticketOrders.eventId, eventId)),
  });

  if (existingOrders.length >= event.maxOrdersPerCustomer) {
    throw new Error("Maximum orders per customer exceeded");
  }

  const totalTickets = existingOrders.reduce((sum, o) => sum + o.quantity, 0);
  if (totalTickets + orderDetails.quantity > event.maxTicketsPerOrder * event.maxOrdersPerCustomer) {
    throw new Error("Maximum tickets per customer exceeded");
  }

  // Verify consumer identity
  const consumer = await db.query.consumers.findFirst({
    where: eq(consumers.id, consumerId),
  });

  if (!consumer?.emailVerified) {
    throw new Error("Email verification required");
  }

  if (event.requireIdentification && !consumer?.identityVerified) {
    throw new Error("Identity verification required");
  }

  return true;
}
```

### Ticket Transfer Controls

```typescript theme={null}
// services/ticket-transfer.ts
export async function requestTicketTransfer(params: {
  ticketId: string;
  currentOwnerId: string;
  recipientEmail: string;
}) {
  const { ticketId, currentOwnerId, recipientEmail } = params;

  const ticket = await db.query.tickets.findFirst({
    where: eq(tickets.id, ticketId),
  });

  if (!ticket) {
    throw new Error("Ticket not found");
  }

  if (ticket.ownerId !== currentOwnerId) {
    throw new Error("Not authorized");
  }

  const event = await getEvent(ticket.eventId);

  // Check if transfers allowed
  if (event.ticketsNonTransferable) {
    throw new Error("Tickets for this event are non-transferable");
  }

  // Check transfer window
  const eventDate = new Date(event.date);
  const cutoffDate = new Date(eventDate.getTime() - 24 * 60 * 60 * 1000); // 24h before

  if (Date.now() > cutoffDate.getTime()) {
    throw new Error("Transfer window has closed");
  }

  // Create transfer request
  const transfer = await db
    .insert(ticketTransfers)
    .values({
      ticketId,
      fromConsumerId: currentOwnerId,
      recipientEmail,
      status: "pending",
      expiresAt: new Date(Date.now() + 48 * 60 * 60 * 1000), // 48h to accept
    })
    .returning();

  // Notify recipient
  await sendTransferInvitation(recipientEmail, ticket, transfer[0]);

  return transfer[0];
}
```

## Step 5: Inventory Management

### Real-Time Availability

```typescript theme={null}
// services/ticket-inventory.ts
import { redis } from "../cache";

export async function getTicketAvailability(eventId: string): Promise<Map<string, number>> {
  const cacheKey = `event:${eventId}:availability`;

  // Try cache first
  const cached = await redis.get(cacheKey);
  if (cached) {
    return new Map(Object.entries(JSON.parse(cached)));
  }

  // Fetch from database
  const tiers = await db.query.ticketTiers.findMany({
    where: eq(ticketTiers.eventId, eventId),
    columns: {
      tierId: true,
      quantity: true,
      soldCount: true,
      reservedCount: true,
    },
  });

  const availability = new Map<string, number>();
  for (const tier of tiers) {
    const available = tier.quantity - tier.soldCount - tier.reservedCount;
    availability.set(tier.tierId, Math.max(0, available));
  }

  // Cache for 10 seconds
  await redis.setex(cacheKey, 10, JSON.stringify(Object.fromEntries(availability)));

  return availability;
}

export async function reserveTickets(eventId: string, tierId: string, quantity: number) {
  return await db.transaction(async (tx) => {
    const tier = await tx
      .select()
      .from(ticketTiers)
      .where(and(eq(ticketTiers.eventId, eventId), eq(ticketTiers.tierId, tierId)))
      .for("update");

    if (!tier[0]) {
      throw new Error("Ticket tier not found");
    }

    const available = tier[0].quantity - tier[0].soldCount - tier[0].reservedCount;
    if (available < quantity) {
      throw new Error("Not enough tickets available");
    }

    await tx
      .update(ticketTiers)
      .set({ reservedCount: tier[0].reservedCount + quantity })
      .where(eq(ticketTiers.id, tier[0].id));

    // Invalidate cache
    await redis.del(`event:${eventId}:availability`);

    return { reserved: quantity, remaining: available - quantity };
  });
}
```

## Step 6: Webhook Integration

```typescript theme={null}
// routes/webhooks/fanfare-tickets.ts
import express from "express";
import { verifyFanfareWebhook } from "../middleware/webhook-verification";

const router = express.Router();

router.post("/", verifyFanfareWebhook, async (req, res) => {
  const event = req.body;

  switch (event.type) {
    case "admission.created":
      // Consumer entered queue
      await trackQueueEntry(event.data);
      break;

    case "admission.admitted":
      // Consumer reached front of queue
      await prepareTicketSession(event.data);
      break;

    case "admission.expired":
      // Consumer didn't complete purchase
      await releaseReservedTickets(event.data);
      break;

    case "admission.completed":
      // Purchase completed
      await finalizeTicketSale(event.data);
      break;

    case "queue.sold_out":
      // All tickets sold
      await handleSoldOut(event.data);
      break;
  }

  res.json({ received: true });
});

async function handleSoldOut(data: QueueSoldOutEvent) {
  const { experienceId, eventId } = data;

  // Update event status
  await db.update(events).set({ saleStatus: "sold_out" }).where(eq(events.id, eventId));

  // Notify remaining queue members
  await notifyWaitingConsumers(experienceId, {
    type: "sold_out",
    message: "All tickets have been sold. You can join the waitlist for cancelled tickets.",
  });

  // Open waitlist
  await openEventWaitlist(eventId);
}
```

## Best Practices

### 1. Clear Communication

```tsx theme={null}
function TicketPurchaseRules({ event }: { event: EventConfig }) {
  return (
    <section className="purchase-rules">
      <h3>Purchase Information</h3>
      <ul>
        <li>Maximum {event.maxTicketsPerOrder} tickets per order</li>
        <li>Maximum {event.maxOrdersPerCustomer} order per customer</li>
        {event.requireIdentification && <li>Valid photo ID required at venue</li>}
        {event.ticketsNonTransferable && <li>Tickets are non-transferable</li>}
        {event.requireBuyerAttendance && <li>Purchaser must be present at entry</li>}
      </ul>
    </section>
  );
}
```

### 2. Mobile-Optimized Queue

```css theme={null}
/* Mobile queue experience */
.queue-state {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 20px;
}

.position-circle {
  width: 200px;
  height: 200px;
  border-radius: 50%;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  margin: 0 auto;
}

.position-circle .position {
  font-size: 48px;
  font-weight: bold;
  color: white;
}

.session-timer.urgent {
  background: #ff5722;
  color: white;
  padding: 8px 16px;
  border-radius: 20px;
  animation: pulse 1s infinite;
}

@keyframes pulse {
  0%,
  100% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.05);
  }
}
```

### 3. Handle High Concurrency

```typescript theme={null}
// Use optimistic locking for ticket reservations
async function reserveWithOptimisticLock(tierId: string, quantity: number, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      return await reserveTickets(tierId, quantity);
    } catch (error) {
      if (i === retries - 1) throw error;
      await sleep(100 * (i + 1)); // Exponential backoff
    }
  }
}
```

## Troubleshooting

### Queue Moving Slowly

1. Increase admission rate
2. Check for failed completions blocking slots
3. Verify infrastructure can handle load

### Tickets Showing Sold Out Incorrectly

1. Check reserved vs. sold counts
2. Verify cache invalidation working
3. Review transaction rollbacks

### Access Code Not Working

1. Verify code is active and not expired
2. Check remaining uses
3. Confirm case sensitivity

## What's Next

* [Webhooks Guide](/guides/advanced/webhooks-guide) - Advanced event handling
* [Error Handling](/guides/advanced/error-handling) - Handle purchase failures
* [Real-time Updates](/guides/advanced/real-time-updates) - Live availability updates
