> ## 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.

# Limited edition

# Limited Edition Drop Use Case

Learn how to use Fanfare draws and auctions to distribute limited edition products fairly and create excitement.

## Overview

Limited edition products require careful distribution to ensure fairness while maximizing engagement. Fanfare provides draws (raffles) and auctions to give everyone a fair chance while creating memorable experiences.

**What you'll learn:**

* Choosing between draws and auctions
* Setting up a draw-based release
* Running an auction for premium items
* Building the drop experience
* Managing winners and fulfillment

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

## Prerequisites

* Fanfare account with draws and/or auctions enabled
* Limited edition product ready for release
* Understanding of your customer base
* Fulfillment process for winners

## When to Use Each Distribution Method

| Method        | Best For                    | Fairness Model | Revenue     |
| ------------- | --------------------------- | -------------- | ----------- |
| Draw (Raffle) | Mass market, brand building | Equal chance   | Fixed price |
| Auction       | Collectors, price discovery | Highest bidder | Variable    |
| Queue         | First-come-first-served     | Speed-based    | Fixed price |

## Draw-Based Release

### Step 1: Configure the Draw

```typescript theme={null}
async function createLimitedEditionDraw() {
  const draw = await createFanfareExperience({
    name: "Limited Edition Sneaker Release",
    slug: "ltd-sneaker-fall-2024",

    // Entry period
    entryStart: new Date("2024-09-01T10:00:00Z"),
    entryEnd: new Date("2024-09-07T23:59:59Z"),

    // Draw timing
    drawTime: new Date("2024-09-08T12:00:00Z"),

    // Prize configuration
    config: {
      totalWinners: 500,

      // Entry limits
      maxEntriesPerConsumer: 1,
      requireAuthentication: true,

      // Selection method
      selectionMethod: "random", // or "weighted" for VIP bonus entries

      // Winner notification
      notifyWinnersVia: ["email", "sms"],
      winnerClaimWindow: 48 * 60 * 60, // 48 hours to claim

      // Waitlist for unclaimed prizes
      enableWaitlist: true,
      waitlistSize: 200,
    },

    // Product details
    prize: {
      productId: "sneaker-ltd-fall-2024",
      name: "Fall 2024 Limited Edition Sneaker",
      price: "250.00",
      description: "Only 500 pairs available worldwide",
      imageUrl: "https://your-store.com/images/ltd-sneaker.jpg",
    },

    // Branding
    branding: {
      primaryColor: "#1A1A1A",
      accentColor: "#FFD700",
      logoUrl: "https://your-brand.com/logo.png",
    },
  });

  return draw;
}
```

### Step 2: Build the Entry Experience

```tsx theme={null}
// pages/drops/[slug].tsx
import { FanfareProvider } from "@fanfare-io/fanfare-sdk-react";
import { DrawExperience } from "@/components/DrawExperience";
import { ProductShowcase } from "@/components/ProductShowcase";

interface DropPageProps {
  drawId: string;
  product: Product;
  entryStart: string;
  entryEnd: string;
  drawTime: string;
}

export default function DropPage({ drawId, product, entryStart, entryEnd, drawTime }: DropPageProps) {
  return (
    <FanfareProvider
      organizationId={process.env.NEXT_PUBLIC_FANFARE_ORG_ID!}
      publishableKey={process.env.NEXT_PUBLIC_FANFARE_PUBLISHABLE_KEY!}
    >
      <div className="drop-page">
        <ProductShowcase product={product} />

        <DrawExperience drawId={drawId} entryStart={entryStart} entryEnd={entryEnd} drawTime={drawTime} />

        <DropDetails product={product} totalWinners={500} drawTime={drawTime} />
      </div>
    </FanfareProvider>
  );
}

function DropDetails({
  product,
  totalWinners,
  drawTime,
}: {
  product: Product;
  totalWinners: number;
  drawTime: string;
}) {
  return (
    <section className="drop-details">
      <h2>How It Works</h2>
      <ol className="steps">
        <li>
          <strong>Enter the Draw</strong>
          <p>Sign up with your email during the entry period. One entry per person.</p>
        </li>
        <li>
          <strong>Wait for the Draw</strong>
          <p>Winners will be selected randomly on {new Date(drawTime).toLocaleDateString()}.</p>
        </li>
        <li>
          <strong>Get Notified</strong>
          <p>Winners receive an email and SMS with purchase instructions.</p>
        </li>
        <li>
          <strong>Complete Purchase</strong>
          <p>You have 48 hours to complete your purchase at ${product.price}.</p>
        </li>
      </ol>

      <div className="drop-stats">
        <div className="stat">
          <span className="value">{totalWinners}</span>
          <span className="label">Available</span>
        </div>
        <div className="stat">
          <span className="value">${product.price}</span>
          <span className="label">Price</span>
        </div>
      </div>
    </section>
  );
}
```

### Step 3: Draw Experience Component

```tsx theme={null}
// components/DrawExperience.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 DrawExperienceProps {
  drawId: string;
  entryStart: string;
  entryEnd: string;
  drawTime: string;
}

export function DrawExperience({ drawId, entryStart, entryEnd, drawTime }: DrawExperienceProps) {
  const { view, start } = useExperienceJourney(drawId, { autoStart: true });
  const stage = view?.journeyStage === "routed" ? view.sequence.phase : view?.journeyStage;
  const sequence = view?.journeyStage === "routed" ? view.sequence : null;

  const now = Date.now();
  const entryStartTime = new Date(entryStart).getTime();
  const entryEndTime = new Date(entryEnd).getTime();
  const drawTimeMs = new Date(drawTime).getTime();

  // Determine phase
  const phase =
    now < entryStartTime
      ? "pre_entry"
      : now < entryEndTime
        ? "entry_open"
        : now < drawTimeMs
          ? "entry_closed"
          : "drawn";

  return (
    <div className="draw-experience">
      {phase === "pre_entry" && <PreEntryState entryStart={entryStart} />}

      {phase === "entry_open" && <EntryOpenState sequence={sequence} onEnter={start} entryEnd={entryEnd} />}

      {phase === "entry_closed" && <EntryClosedState sequence={sequence} drawTime={drawTime} />}

      {phase === "drawn" && <DrawnState sequence={sequence} />}
    </div>
  );
}

function PreEntryState({ entryStart }: { entryStart: string }) {
  return (
    <div className="pre-entry-state">
      <h2>Entry Opens Soon</h2>
      <CountdownTimer targetTime={entryStart} />

      <div className="notify-me">
        <p>Get notified when entry opens</p>
        <NotifyMeForm />
      </div>
    </div>
  );
}

function EntryOpenState({
  sequence,
  onEnter,
  entryEnd,
}: {
  sequence: SequenceView | null;
  onEnter: () => void;
  entryEnd: string;
}) {
  const isEntered = sequence?.stage === "participating" || sequence?.stage === "admitted";

  if (isEntered) {
    return (
      <div className="entered-state">
        <div className="success-badge">
          <span className="icon">✓</span>
          <span>You're In!</span>
        </div>

        <p>Your entry has been recorded. Good luck!</p>

        <div className="draw-countdown">
          <p>Draw happens in:</p>
          <CountdownTimer targetTime={entryEnd} />
        </div>

        <p className="reminder">We'll notify you by email if you win.</p>
      </div>
    );
  }

  return (
    <div className="entry-open-state">
      <h2>Enter the Draw</h2>

      <div className="entry-closing">
        <p>Entry closes in:</p>
        <CountdownTimer targetTime={entryEnd} />
      </div>

      <EntryForm onSubmit={onEnter} />

      <p className="terms">By entering, you agree to the draw rules and terms.</p>
    </div>
  );
}

function EntryForm({ onSubmit }: { onSubmit: () => void }) {
  const [email, setEmail] = useState("");
  const [phone, setPhone] = useState("");
  const [agreed, setAgreed] = useState(false);
  const [isSubmitting, setIsSubmitting] = useState(false);

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setIsSubmitting(true);

    try {
      // SDK handles entry
      onSubmit();
    } finally {
      setIsSubmitting(false);
    }
  };

  return (
    <form onSubmit={handleSubmit} className="entry-form">
      <div className="form-group">
        <label htmlFor="email">Email Address</label>
        <input
          id="email"
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          required
          placeholder="your@email.com"
        />
      </div>

      <div className="form-group">
        <label htmlFor="phone">Phone Number (for SMS notification)</label>
        <input
          id="phone"
          type="tel"
          value={phone}
          onChange={(e) => setPhone(e.target.value)}
          placeholder="+1 (555) 123-4567"
        />
      </div>

      <div className="form-group checkbox">
        <label>
          <input type="checkbox" checked={agreed} onChange={(e) => setAgreed(e.target.checked)} required />I agree to
          the draw rules and terms of service
        </label>
      </div>

      <button type="submit" disabled={isSubmitting || !agreed}>
        {isSubmitting ? "Entering..." : "Enter Draw"}
      </button>
    </form>
  );
}

function EntryClosedState({ sequence, drawTime }: { sequence: SequenceView | null; drawTime: string }) {
  const isEntered = sequence?.stage === "participating" || sequence?.stage === "admitted";

  return (
    <div className="entry-closed-state">
      <h2>Entry Period Closed</h2>

      {isEntered ? (
        <div className="entered-confirmation">
          <p className="success">You're entered! Good luck!</p>
        </div>
      ) : (
        <p className="missed">Entry is now closed. Follow us for future drops.</p>
      )}

      <div className="draw-info">
        <p>Draw happens:</p>
        <CountdownTimer targetTime={drawTime} />
      </div>
    </div>
  );
}

function DrawnState({ sequence }: { sequence: SequenceView | null }) {
  if (sequence?.phase === "granted") {
    return (
      <div className="winner-state">
        <div className="winner-celebration">
          <h2>Congratulations!</h2>
          <p>You've been selected!</p>
        </div>

        <div className="claim-section">
          <p>Complete your purchase before the access window closes.</p>

          <button
            onClick={() => {
              sessionStorage.setItem("fanfare_admission", JSON.stringify({ admissionGrant: sequence.grant.token }));
              window.location.href = "/checkout";
            }}
          >
            Claim Your Prize
          </button>
        </div>

        <p className="warning">Don't miss out - unclaimed prizes go to the waitlist.</p>
      </div>
    );
  }

  if (sequence?.stage === "denied") {
    return (
    <div className="not-selected-state">
      <h2>Draw Complete</h2>
      <p>Unfortunately, you weren't selected this time.</p>

      <div className="follow-up">
        <p>Stay tuned for future drops!</p>
        <button onClick={() => (window.location.href = "/subscribe")}>Get Notified of Future Drops</button>
      </div>
    </div>
  );
  }

  return <WaitingForDrawState />;
}

function WaitingForDrawState() {
  return (
    <div className="waiting-for-draw-state">
      <h2>Draw In Progress</h2>
      <p>Keep an eye on your account and email for the result.</p>
    </div>
  );
}
```

## Auction-Based Release

### Step 1: Configure the Auction

```typescript theme={null}
async function createLimitedEditionAuction() {
  const auction = await createFanfareExperience({
    name: "Rare Collector's Edition Artwork",
    slug: "rare-artwork-auction-001",

    // Timing
    startTime: new Date("2024-10-01T18:00:00Z"),
    endTime: new Date("2024-10-03T18:00:00Z"),

    // Auction configuration
    config: {
      auctionType: "english", // ascending price auction

      // Pricing
      startingBid: 500,
      reservePrice: 1000, // Minimum price to sell
      bidIncrement: 50,
      buyNowPrice: 5000, // Optional instant purchase

      // Anti-sniping
      antiSnipingEnabled: true,
      antiSnipingExtension: 120, // Extend 2 minutes if bid in last 2 minutes

      // Bidder requirements
      requireAuthentication: true,
      requirePaymentMethod: true, // Must have card on file
      bidderDeposit: 100, // Refundable deposit to bid

      // Limits
      maxBidsPerConsumer: 50,
    },

    // Item details
    item: {
      productId: "artwork-rare-001",
      name: "Original Digital Artwork #001",
      description: "One-of-a-kind digital artwork with physical print",
      imageUrl: "https://your-store.com/images/artwork-001.jpg",
      attributes: {
        artist: "Famous Artist",
        medium: "Digital + Physical Print",
        size: "24x36 inches",
        certificate: "Includes Certificate of Authenticity",
      },
    },

    // Branding
    branding: {
      primaryColor: "#2C2C2C",
      accentColor: "#C9A227",
    },
  });

  return auction;
}
```

### Step 2: Build the Auction Experience

```tsx theme={null}
// components/AuctionExperience.tsx
import { useExperienceJourney } from "@fanfare-io/fanfare-sdk-react";
import { useState, useEffect, useCallback } from "react";

interface AuctionExperienceProps {
  auctionId: string;
  item: AuctionItem;
}

export function AuctionExperience({ auctionId, item }: AuctionExperienceProps) {
  const { view } = useExperienceJourney(auctionId, { autoStart: true });

  const auctionSequence =
    view?.journeyStage === "routed" &&
    view.sequence.phase === "participating" &&
    view.sequence.mechanism === "auction"
      ? view.sequence
      : null;

  const auctionState = auctionSequence?.state$.get();
  const currentBid = auctionState?.currentBid || auctionState?.startingBid || "0.00";
  const isHighBidder = Boolean(auctionState?.isHighBidder);
  const endTime = auctionState?.endsAt;
  const bidIncrement = auctionState?.bidIncrement || "50.00";

  return (
    <div className="auction-experience">
      <AuctionHeader item={item} endTime={endTime} />

      <div className="auction-main">
        <div className="current-bid-section">
          <span className="label">Current Bid</span>
          <span className="amount">{formatMoney(currentBid, "USD")}</span>
          {auctionState?.totalBids && <span className="bid-count">{auctionState.totalBids} bids</span>}
        </div>

        {isHighBidder && (
          <div className="high-bidder-notice">
            <span className="icon">✓</span>
            You're the highest bidder!
          </div>
        )}

        <BidForm
          currentBid={currentBid}
          minBid={addMoney(currentBid, bidIncrement)}
          bidIncrement={bidIncrement}
          buyNowPrice={auctionState?.buyNowPrice}
          isHighBidder={isHighBidder}
          onBid={(amount) => auctionSequence?.bid(amount)}
        />

        <BidHistory bids={auctionState?.recentBids || []} />
      </div>
    </div>
  );
}

function AuctionHeader({ item, endTime }: { item: AuctionItem; endTime?: number }) {
  const [timeLeft, setTimeLeft] = useState(0);
  const [isEnding, setIsEnding] = useState(false);

  useEffect(() => {
    if (!endTime) return;

    const interval = setInterval(() => {
      const remaining = endTime - Date.now();
      setTimeLeft(Math.max(0, remaining));
      setIsEnding(remaining < 5 * 60 * 1000); // Less than 5 minutes
    }, 1000);

    return () => clearInterval(interval);
  }, [endTime]);

  return (
    <header className="auction-header">
      <h1>{item.name}</h1>

      <div className={`auction-timer ${isEnding ? "ending-soon" : ""}`}>
        <span className="label">{isEnding ? "Ending Soon!" : "Time Remaining"}</span>
        <span className="time">{formatAuctionTime(timeLeft)}</span>
      </div>
    </header>
  );
}

function BidForm({
  currentBid,
  minBid,
  bidIncrement,
  buyNowPrice,
  isHighBidder,
  onBid,
}: {
  currentBid: string;
  minBid: string;
  bidIncrement: string;
  buyNowPrice?: string;
  isHighBidder: boolean;
  onBid: (amount: string) => void;
}) {
  const [bidAmount, setBidAmount] = useState(minBid);
  const [isSubmitting, setIsSubmitting] = useState(false);

  // Update minimum when current bid changes
  useEffect(() => {
    if (compareMoney(bidAmount, minBid) < 0) {
      setBidAmount(minBid);
    }
  }, [minBid, bidAmount]);

  const handleBid = async () => {
    if (compareMoney(bidAmount, minBid) < 0) return;

    setIsSubmitting(true);
    try {
      await onBid(bidAmount);
    } finally {
      setIsSubmitting(false);
    }
  };

  const quickBidAmounts = [
    minBid,
    addMoney(minBid, bidIncrement),
    addMoney(minBid, multiplyMoney(bidIncrement, 2)),
    addMoney(minBid, multiplyMoney(bidIncrement, 5)),
  ];

  return (
    <div className="bid-form">
      <div className="quick-bids">
        {quickBidAmounts.map((amount) => (
          <button key={amount} onClick={() => setBidAmount(amount)} className={bidAmount === amount ? "selected" : ""}>
            {formatMoney(amount, "USD")}
          </button>
        ))}
      </div>

      <div className="custom-bid">
        <label htmlFor="bid-amount">Your Bid</label>
        <div className="bid-input-group">
          <span className="currency">$</span>
          <input
            id="bid-amount"
            type="number"
            value={bidAmount}
            onChange={(e) => setBidAmount(normalizeMoneyInput(e.target.value, minBid))}
            min={minBid}
            step={bidIncrement}
          />
        </div>
        <span className="min-bid">Minimum: {formatMoney(minBid, "USD")}</span>
      </div>

      <button onClick={handleBid} disabled={isSubmitting || compareMoney(bidAmount, minBid) < 0} className="place-bid-btn">
        {isSubmitting ? "Placing Bid..." : isHighBidder ? "Increase Bid" : "Place Bid"}
      </button>

      {buyNowPrice && (
        <div className="buy-now-section">
          <span className="divider">or</span>
          <button onClick={() => onBid(buyNowPrice)} className="buy-now-btn">
            Buy Now for {formatMoney(buyNowPrice, "USD")}
          </button>
        </div>
      )}
    </div>
  );
}

function BidHistory({ bids }: { bids: Bid[] }) {
  if (bids.length === 0) {
    return (
      <div className="bid-history empty">
        <p>No bids yet. Be the first!</p>
      </div>
    );
  }

  return (
    <div className="bid-history">
      <h3>Recent Bids</h3>
      <ul>
        {bids.map((bid, index) => (
          <li key={bid.id}>
            <span className="bidder">{bid.displayName}</span>
            <span className="amount">{formatMoney(bid.amount, "USD")}</span>
            <span className="time">{formatTimeAgo(bid.timestamp)}</span>
            {index === 0 && <span className="leader-badge">Leading</span>}
          </li>
        ))}
      </ul>
    </div>
  );
}

function formatAuctionTime(ms: number): string {
  const hours = Math.floor(ms / (1000 * 60 * 60));
  const minutes = Math.floor((ms % (1000 * 60 * 60)) / (1000 * 60));
  const seconds = Math.floor((ms % (1000 * 60)) / 1000);

  if (hours > 0) {
    return `${hours}h ${minutes}m ${seconds}s`;
  }
  if (minutes > 0) {
    return `${minutes}m ${seconds}s`;
  }
  return `${seconds}s`;
}

function formatTimeAgo(timestamp: number): string {
  const seconds = Math.floor((Date.now() - timestamp) / 1000);

  if (seconds < 60) return "Just now";
  if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`;
  if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ago`;
  return new Date(timestamp).toLocaleDateString();
}
```

## Step 3: Winner Management

### Process Draw Winners

```typescript theme={null}
// services/draw-winner-management.ts
async function processDrawWinners(drawId: string) {
  // Get winners list
  const winners = await getDrawWinners(drawId);

  for (const winner of winners) {
    const checkoutLink = await createSecureCheckoutLink({
      consumerId: winner.consumerId,
      drawId,
      expiresInHours: 48,
    });

    await db.insert(drawClaims).values({
      drawId,
      consumerId: winner.consumerId,
      expiresAt: new Date(Date.now() + 48 * 60 * 60 * 1000), // 48 hours
      status: "pending",
    });

    await sendWinnerNotification(winner, checkoutLink);
  }

  // Process waitlist
  const waitlist = await getDrawWaitlist(drawId);
  for (const entry of waitlist) {
    await sendWaitlistNotification(entry);
  }
}

async function sendWinnerNotification(winner: DrawWinner, checkoutLink: string) {
  await Promise.all([
    sendEmail({
      to: winner.email,
      subject: "You Won! Complete Your Purchase",
      template: "draw-winner",
      data: {
        name: winner.name,
        productName: winner.prize.name,
        checkoutLink,
        deadline: new Date(Date.now() + 48 * 60 * 60 * 1000).toLocaleString(),
      },
    }),

    sendSMS({
      to: winner.phone,
      message: `Congratulations! You won the ${winner.prize.name}. Check your email to complete checkout within 48h.`,
    }),
  ]);
}
```

### Process Auction Winner

```typescript theme={null}
async function processAuctionWinner(auctionId: string) {
  const auction = await getAuctionResult(auctionId);

  if (auction.status !== "ended") {
    throw new Error("Auction not yet ended");
  }

  const winner = auction.highestBidder;
  const finalPrice = auction.highestBid;

  // Check reserve price met
  if (compareMoney(finalPrice, auction.config.reservePrice) < 0) {
    await handleReserveNotMet(auction);
    return;
  }

  // Generate payment intent for final amount
  const paymentIntent = await stripe.paymentIntents.create({
    amount: toMinorUnits(finalPrice),
    currency: "usd",
    customer: winner.stripeCustomerId,
    metadata: {
      auctionId,
      type: "auction_win",
    },
  });

  // Send winner notification
  await sendEmail({
    to: winner.email,
    subject: `Congratulations! You won the auction for ${auction.item.name}`,
    template: "auction-winner",
    data: {
      itemName: auction.item.name,
      finalPrice,
      paymentUrl: `https://your-store.com/auction/pay/${auctionId}?pi=${paymentIntent.id}`,
      deadline: new Date(Date.now() + 72 * 60 * 60 * 1000).toLocaleString(),
    },
  });
}

async function handleReserveNotMet(auction: Auction) {
  // Notify seller
  await sendEmail({
    to: auction.sellerEmail,
    subject: `Reserve Not Met - ${auction.item.name}`,
    template: "auction-reserve-not-met",
    data: {
      itemName: auction.item.name,
      highestBid: auction.highestBid,
      reservePrice: auction.config.reservePrice,
    },
  });

  // Notify high bidder
  if (auction.highestBidder) {
    await sendEmail({
      to: auction.highestBidder.email,
      subject: `Auction Ended - Reserve Not Met`,
      template: "auction-reserve-not-met-bidder",
      data: {
        itemName: auction.item.name,
        yourBid: auction.highestBid,
      },
    });
  }
}
```

## Best Practices

### 1. Prevent Gaming and Fraud

```typescript theme={null}
// Draw fraud prevention
const drawFraudPrevention = {
  // Require verified email
  requireEmailVerification: true,

  // Block disposable emails
  blockDisposableEmails: true,

  // Limit entries by IP
  maxEntriesPerIP: 3,

  // Require account age
  minAccountAgeDays: 7,

  // Geographic restrictions
  allowedCountries: ["US", "CA", "UK"],
};

// Auction fraud prevention
const auctionFraudPrevention = {
  // Require verified payment method
  requirePaymentMethod: true,

  // Bidder deposit
  bidderDeposit: 100,

  // Account verification
  requireIdentityVerification: true,

  // Shill bidding detection
  detectShillBidding: true,
};
```

### 2. Clear Communication

```typescript theme={null}
// Communication timeline for draws
const drawCommunicationSchedule = [
  { trigger: "entry_open", template: "draw-entry-open", channels: ["email"] },
  { trigger: "24h_before_close", template: "draw-reminder", channels: ["email", "push"] },
  { trigger: "entry_closed", template: "draw-closed", channels: ["email"] },
  { trigger: "draw_complete_winner", template: "draw-winner", channels: ["email", "sms", "push"] },
  { trigger: "draw_complete_non_winner", template: "draw-non-winner", channels: ["email"] },
  { trigger: "24h_before_claim_deadline", template: "claim-reminder", channels: ["email", "sms"] },
  { trigger: "claim_expired", template: "claim-expired-waitlist", channels: ["email", "sms"] },
];
```

### 3. Transparent Rules

```tsx theme={null}
function DrawRules({ draw }: { draw: Draw }) {
  return (
    <section className="draw-rules">
      <h2>Official Rules</h2>

      <dl>
        <dt>Eligibility</dt>
        <dd>Must be 18+ and resident of eligible countries</dd>

        <dt>Entry Period</dt>
        <dd>
          {new Date(draw.entryStart).toLocaleString()} - {new Date(draw.entryEnd).toLocaleString()}
        </dd>

        <dt>Entry Limit</dt>
        <dd>One entry per person</dd>

        <dt>Selection</dt>
        <dd>Winners selected randomly using certified random number generator</dd>

        <dt>Notification</dt>
        <dd>Winners notified via email and SMS within 1 hour of draw</dd>

        <dt>Claim Period</dt>
        <dd>Winners must complete purchase within 48 hours</dd>

        <dt>Price</dt>
        <dd>${draw.prize.price} (no additional fees)</dd>
      </dl>
    </section>
  );
}
```

## Troubleshooting

### Draw Issues

| Problem               | Cause                 | Solution                              |
| --------------------- | --------------------- | ------------------------------------- |
| Duplicate entries     | Multiple accounts     | Implement email/phone verification    |
| Low participation     | Poor marketing timing | Announce earlier, extend entry period |
| Many unclaimed prizes | Short claim window    | Extend to 72h, improve notifications  |

### Auction Issues

| Problem              | Cause               | Solution                      |
| -------------------- | ------------------- | ----------------------------- |
| Sniping complaints   | Last-second bids    | Enable anti-sniping extension |
| Low bidding activity | High starting price | Lower start, keep reserve     |
| Winner doesn't pay   | No deposit required | Implement bidder deposits     |

## What's Next

* [Appointment Booking](/guides/use-cases/appointment-booking) - Service scheduling
* [Event Ticketing](/guides/use-cases/event-ticketing) - Event access management
* [Webhooks Guide](/guides/advanced/webhooks-guide) - Real-time notifications
