Skip to main content

Deployment

This guide covers everything you need to deploy your Fanfare integration to production, including environment configuration, security considerations, and monitoring.

Pre-Deployment Checklist

Before deploying to production, verify the following:

1. Use Production Credentials

Ensure you are using production API keys, not test keys:
// Production
<FanfareProvider
  organizationId={process.env.NEXT_PUBLIC_FANFARE_ORG_ID}
  publishableKey={process.env.NEXT_PUBLIC_FANFARE_PUBLISHABLE_KEY} // pk_live_...
  environment="production"
>
Production keys:
  • Start with pk_live_
  • Connect to https://api.fanfare.io
  • Use your production organization data

2. Remove Debug Mode

Disable debug logging in production:
<FanfareProvider
  organizationId={process.env.NEXT_PUBLIC_FANFARE_ORG_ID}
  publishableKey={process.env.NEXT_PUBLIC_FANFARE_PUBLISHABLE_KEY}
  debug={false}  // Always false in production
  logging={{ level: "error" }}  // Only log errors
>

3. Verify Environment Variables

Ensure all required environment variables are set in your production environment:
# Required
NEXT_PUBLIC_FANFARE_ORG_ID=org_your_production_id
NEXT_PUBLIC_FANFARE_PUBLISHABLE_KEY=pk_live_your_production_key

# Optional
NEXT_PUBLIC_FANFARE_ENVIRONMENT=production

4. Test Production Configuration Locally

Before deploying, test with production credentials locally:
# Create a .env.production.local file (gitignored)
NEXT_PUBLIC_FANFARE_ORG_ID=org_production_123
NEXT_PUBLIC_FANFARE_PUBLISHABLE_KEY=pk_live_xyz789

# Run with production config
npm run build && npm run start

Backend Integration

Validating Admission Tokens

When customers are admitted, they receive an admission token. Your backend must validate this token before completing the transaction.
// Your backend API
import { createHmac } from "crypto";

interface AdmissionValidation {
  valid: boolean;
  experienceId?: string;
  consumerId?: string;
  admittedAt?: string;
  expiresAt?: string;
}

async function validateAdmissionToken(token: string): Promise<AdmissionValidation> {
  const response = await fetch("https://api.fanfare.io/v1/admissions/validate", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${process.env.FANFARE_SECRET_KEY}`,
      "X-Organization-Id": process.env.FANFARE_ORG_ID!,
    },
    body: JSON.stringify({ token }),
  });

  if (!response.ok) {
    return { valid: false };
  }

  return response.json();
}
Security Note: Never expose your secret API key (sk_live_...) in client-side code. Only use it in your backend.

Webhook Integration

Set up webhooks to receive real-time notifications about experience events: Common webhook events:
EventDescription
queue.admittedConsumer was admitted from queue
draw.completedDraw finished, winners selected
auction.endedAuction ended
admission.expiredAdmission token expired
Example webhook handler:
// pages/api/webhooks/fanfare.ts (Next.js)
import { NextApiRequest, NextApiResponse } from "next";
import { createHmac } from "crypto";

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method !== "POST") {
    return res.status(405).end();
  }

  // Verify webhook signature
  const signature = req.headers["x-fanfare-signature"] as string;
  const payload = JSON.stringify(req.body);
  const expectedSignature = createHmac("sha256", process.env.FANFARE_WEBHOOK_SECRET!).update(payload).digest("hex");

  if (signature !== expectedSignature) {
    return res.status(401).json({ error: "Invalid signature" });
  }

  // Handle the event
  const event = req.body;

  switch (event.type) {
    case "queue.admitted":
      // Handle queue admission
      await handleQueueAdmission(event.data);
      break;
    case "draw.completed":
      // Handle draw completion
      await handleDrawCompletion(event.data);
      break;
    default:
      console.log("Unhandled event type:", event.type);
  }

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

Security Considerations

Content Security Policy (CSP)

Allow connections to Fanfare APIs in your CSP:
// next.config.js
const securityHeaders = [
  {
    key: "Content-Security-Policy",
    value: `
      default-src 'self';
      connect-src 'self' https://api.fanfare.io https://beacon.fanfare.io;
      script-src 'self' 'unsafe-inline' 'unsafe-eval';
      style-src 'self' 'unsafe-inline';
    `.replace(/\n/g, ""),
  },
];

module.exports = {
  async headers() {
    return [
      {
        source: "/(.*)",
        headers: securityHeaders,
      },
    ];
  },
};

CORS Configuration

The Fanfare API handles CORS automatically. Ensure your domain is registered in your Fanfare dashboard.

Rate Limiting

The Fanfare API has rate limits to protect against abuse:
EndpointLimit
Queue entry100 requests/minute per IP
Draw entry100 requests/minute per IP
Auction bidding60 requests/minute per consumer
Status polling120 requests/minute per consumer
The SDK handles rate limit errors gracefully with exponential backoff.

Performance Optimization

Bundle Size

Verify your bundle size includes only what you need:
# Analyze bundle
npm run build -- --analyze
The SDK supports tree-shaking. If you only use queues, draw/auction code is not included.

Lazy Loading

Load Fanfare components lazily if they are not needed on initial page load:
import { lazy, Suspense } from "react";

const QueueWidget = lazy(() => import("@fanfare/react").then((m) => ({ default: m.QueueWidget })));

function ProductPage() {
  return (
    <div>
      <h1>Product Details</h1>
      {/* QueueWidget loads only when rendered */}
      <Suspense fallback={<div>Loading queue...</div>}>
        <QueueWidget experienceId="exp_123" />
      </Suspense>
    </div>
  );
}

Preconnect to API

Add preconnect hints to reduce connection latency:
<head>
  <link rel="preconnect" href="https://api.fanfare.io" />
  <link rel="preconnect" href="https://beacon.fanfare.io" />
</head>

Monitoring and Observability

Error Tracking

Integrate with your error tracking service:
import * as Sentry from "@sentry/react";
import { FanfareProvider } from "@fanfare/react";

function App() {
  return (
    <Sentry.ErrorBoundary fallback={<ErrorFallback />}>
      <FanfareProvider
        organizationId={process.env.NEXT_PUBLIC_FANFARE_ORG_ID!}
        publishableKey={process.env.NEXT_PUBLIC_FANFARE_PUBLISHABLE_KEY!}
      >
        <YourApp />
      </FanfareProvider>
    </Sentry.ErrorBoundary>
  );
}

Analytics Events

Track Fanfare events in your analytics:
import { useFanfare } from "@fanfare/react";
import { useEffect } from "react";

function AnalyticsTracker() {
  const fanfare = useFanfare();

  useEffect(() => {
    const unsubscribeAdmitted = fanfare.on("queue:admitted", (data) => {
      // Track in your analytics
      analytics.track("Queue Admitted", {
        queueId: data.queueId,
      });
    });

    const unsubscribeDrawWon = fanfare.on("draw:won", (data) => {
      analytics.track("Draw Won", {
        drawId: data.drawId,
      });
    });

    return () => {
      unsubscribeAdmitted();
      unsubscribeDrawWon();
    };
  }, [fanfare]);

  return null;
}

Health Checks

Monitor Fanfare API availability:
// health-check.ts
async function checkFanfareHealth(): Promise<boolean> {
  try {
    const response = await fetch("https://api.fanfare.io/health", {
      timeout: 5000,
    });
    return response.ok;
  } catch {
    return false;
  }
}

Deployment Platforms

Vercel

# Set environment variables in Vercel dashboard or CLI
vercel env add NEXT_PUBLIC_FANFARE_ORG_ID
vercel env add NEXT_PUBLIC_FANFARE_PUBLISHABLE_KEY

# Deploy
vercel --prod

Netlify

# netlify.toml
[build]
  command = "npm run build"
  publish = ".next"

[build.environment]
  NEXT_PUBLIC_FANFARE_ORG_ID = "org_your_id"
  NEXT_PUBLIC_FANFARE_PUBLISHABLE_KEY = "pk_live_your_key"

AWS Amplify

# amplify.yml
version: 1
frontend:
  phases:
    preBuild:
      commands:
        - npm ci
    build:
      commands:
        - npm run build
  artifacts:
    baseDirectory: .next
    files:
      - "**/*"
  cache:
    paths:
      - node_modules/**/*
Set environment variables in the Amplify console.

Docker

# Dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
ARG NEXT_PUBLIC_FANFARE_ORG_ID
ARG NEXT_PUBLIC_FANFARE_PUBLISHABLE_KEY
ENV NEXT_PUBLIC_FANFARE_ORG_ID=$NEXT_PUBLIC_FANFARE_ORG_ID
ENV NEXT_PUBLIC_FANFARE_PUBLISHABLE_KEY=$NEXT_PUBLIC_FANFARE_PUBLISHABLE_KEY
RUN npm run build

FROM node:20-alpine AS runner
WORKDIR /app
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json

EXPOSE 3000
CMD ["npm", "start"]

Post-Deployment Verification

After deploying, verify your integration:

1. Test the Full Flow

  1. Visit your production site
  2. Join a queue/draw/auction
  3. Verify you are admitted (use a test experience if needed)
  4. Complete a test transaction
  5. Verify the admission token was validated

2. Check Logging

Monitor your application logs for any Fanfare-related errors:
# Vercel
vercel logs --follow

# Docker
docker logs -f your-container

3. Verify Webhooks

Trigger a test event and verify your webhook endpoint receives it:
  1. Go to Fanfare Dashboard > Webhooks
  2. Click “Send Test Event”
  3. Verify your endpoint returns 200 OK

4. Monitor Metrics

Check your Fanfare dashboard for:
  • Request volume
  • Error rates
  • Average queue times
  • Conversion rates

Rollback Plan

Have a rollback plan ready:
  1. Feature Flag: Use a feature flag to disable Fanfare integration quickly
  2. Fallback UI: Show a fallback message if Fanfare is unavailable
  3. Previous Version: Keep the previous deployment available for quick rollback
function ProductPage() {
  const fanfareEnabled = useFeatureFlag("fanfare_enabled");

  if (!fanfareEnabled) {
    return <FallbackProductPage />;
  }

  return (
    <FanfareProvider {...config}>
      <ProductWithQueue />
    </FanfareProvider>
  );
}

Next Steps