AWS Builder Center

FIFA World Cup 2026 Media Reservation Platform — Built with Kiro by a Working Media Manager

As Media Manager for FIFA World Cup 2026 in Philadelphia, I built a full-stack platform to coordinate 1,500+ credentialed media across 104 matches and 39 event days — solo, using Kiro. It handles personalized token-based access, a 48-hour lockout system, real-time capacity tracking, email notifications, and printable security manifests. Kiro's Spec workflow and Autopilot mode let a non-full-time engineer ship a production system running live at the World Cup.

FIFA Fan Festival Philadelphia — Media Reservation Platform

Built with Kiro | World Cup 2026 | Philadelphia

What This Is

I'm the Media Manager for the FIFA World Cup 2026 in Philadelphia. My job is to coordinate over 1,500 credentialed media professionals — photographers, journalists, broadcasters, radio hosts, and podcasters — across 39 event days and 104 matches running from June 11 to July 19, 2026.
Managing this at scale, in a chaotic, constantly changing event logistics environment, required a custom platform. Every day brings late arrivals, outlet changes, last-minute approvals, and position conflicts. A spreadsheet wasn't going to cut it.
So I built one — with Kiro.
The Media Reservation Platform is a full-stack web application that handles every aspect of media position management: from building the match schedule to issuing personalized access links, from accepting reservation requests to printing security manifests. It replaces what would otherwise be hundreds of individual emails, calls, and spreadsheet rows.

The Scale of the Problem

  • 104 matches across the full tournament
  • 39 operating days at the Fan Festival venue
  • 4 per-match position types per match, plus daily positions and parking
  • 1,500+ credentialed individuals and outlets needing access
  • A communications team that must evaluate and action every request within a rolling 48-hour window before each match
  • Hard security constraints: approved lists printed and handed to venue staff at every position

Building the Schedule First

Everything starts with the schedule. Before any reservation can be accepted, the platform needs to know when each match happens, where, and in what time window.
The schedule is built entirely through the Admin panel:
  1. 39 Event Days are created, each covering a full ET calendar day from midnight to 23:59:59 ET, stored internally as UTC.
  2. Each day has up to 5 Time Periods (Morning, Midday, Afternoon, Evening, Late) with explicit start/end times.
  3. Each match (up to 104 matches) is placed into a time period, with home team, away team, venue, and kickoff time.
Once a match is in the system, the platform automatically creates the four reservable per-match positions for it:
CodeDisplay NameSoft Cap
platform_aCrowd View — Platform A7
platform_bCrowd View — Platform B7
platform_cPhotography Platform8
platform_dMedia Tower (Match)15
Parking passes, Radio & Broadcast Tents, and Media Tower non-match bookings are available on all 39 event days. On the 6 local Philadelphia match days, additional stadium broadcast positions also open up:
SurfaceTypeCap
Parking PassPer ET date20/day (hard cap)
Stadium Broadcast — Camera & ReporterPer ET date10/day (hard cap)
Stadium Broadcast — Roaming ReporterPer ET date15/day (hard cap)
Radio & Broadcast Tent (3 physical tents)Time-slotted3-hour max per slot
Media Tower Non-MatchTime-slottedConflict advisory
The 6 local Philadelphia match dates are fixed in the system — these are the days when stadium broadcast positions open in addition to the standard daily surfaces.

Personalized Access — No Login Required

This was a deliberate design decision. Asking 1,500 media professionals to create accounts, remember passwords, and manage credentials would add friction and create a support burden. Instead, every credentialed person gets a unique, personalized portal link — nothing else required.

How the links work

Each link looks like:
1
https://[platform]/portal/eyJhdHRlbmRlZUlkIjoiYWJj...
The token embedded in the URL is an HMAC-SHA256 signed payload containing:
  • The attendee's unique ID
  • An issued-at timestamp
  • An expiry timestamp (30-day default)
The signature is computed server-side using a secret key (PORTAL_TOKEN_SECRET) that never touches the client. When someone opens their link, the server:
  1. Splits the token at the . separator
  2. Recomputes the expected HMAC signature
  3. Compares using crypto.timingSafeEqual — constant-time comparison that prevents timing attacks, where an attacker could otherwise measure response time differences to infer parts of a valid token
  4. Decodes and validates the payload shape and expiry
  5. Returns a generic HTTP 404 for any failure — no information leakage about whether a token ever existed
Tokens are stored in the attendee record so the URL can be reconstructed and re-sent if needed. If a new token is issued, the old one is invalidated.

Outlet organizers

For larger media organizations, a single outlet organizer link allows one person to make reservation requests on behalf of their entire team — selecting positions and matches across the full schedule. Smaller outlets and individual credentialed media use their personal link directly.
Rate limiting protects the link-request endpoint: 5 requests per 15 minutes per IP, preventing both link enumeration and attendee spam.

The User Portal

The portal is designed to work equally well on mobile and desktop — because media personnel are often checking it from a phone at the venue.
When a user opens their link, the platform automatically routes them to the correct day view:
  • If today falls within the June 11–July 19 range → opens to today
  • Before the event starts → opens to the first festival day
  • After the event ends → opens to the last day

What the portal shows

The day view lays out every reservable surface for that date:
  • Per-match sections: Each match on that day, with the four platform positions listed. Users see current availability, whether the window is open, and the status of any existing request (Pending / Approved / Denied / Cancelled).
  • Daily sections: Parking, Broadcast Tents, and Media Tower non-match slots are available every day. On the 6 local Philadelphia match days, stadium broadcast positions also appear.
For each position, the user can:
  • Submit a request (when the reservation window is open)
  • Cancel an existing request (when the window is still open)

Cancellation behavior

Cancelling a request removes it from the active queue, but it does not disappear from the admin view. The admin can see it was cancelled, when it was cancelled, and by whom. This is intentional — the communications team needs a full picture of outlet intent, not just the current state.

The 48-Hour Lockout Window

All reservation requests automatically lock out 48 hours before the relevant match or day. This is the window the communications team uses to evaluate and act on every pending request without the queue changing underneath them.

How it works

For matches:
Window opens at match_kickoff - 48 hours, closes at kickoff.
For daily surfaces (parking, broadcast tents, stadium positions):
Window opens at 10:00 AM ET, two days before the requested date, and closes at 23:59:59 ET on that date.
Once the window opens, attendees receive an error if they attempt to submit or cancel — with a tooltip showing exactly when the window opened and the contact email for late requests (MediaCredentials@philadelphia2026.com).
Admins are never blocked by the lockout. They can approve, deny, create, and modify reservations at any time.
After the window closes, the admin team works through every pending request — approving what fits, denying what doesn't, and entering a reason for any denial (e.g., "Not enough space", "Only one outlet reservation per match", "Photography media is not allowed in broadcast positions").

Transactional Emails via Emailit

Every status change sends an automatic email to the attendee. The platform uses Emailit (https://api.emailit.com/v2/emails) for all transactional delivery.

Email flow

TriggerEmail sent
Request submittedPending — your request has been received
Admin approvesApproved — with position details
Admin deniesDenied — with the admin's reason
Request cancelledCancellation confirmation
Portal link (re)issuedMagic link email
The transport layer implements exponential backoff retry: up to 3 attempts at 500ms → 1s → 2s intervals, retrying on server errors (5xx) and network failures only — not on client errors (4xx). Emails are dispatched non-blocking after every database commit, so they never slow down the HTTP response. On terminal failure after all retries, an email.failed entry is written to the audit log.
The sender displays as Fan Festival Media Credentials <[configured address]> — the same identity the communications team uses for all media correspondence.

The Admin Dashboard

The admin interface is a two-column shell — a fixed white sidebar on the left (inspired by Calendly's layout), and a scrollable main content area on the right. It requires authentication via Appwrite's Users service; unauthenticated requests to any /admin/* route are immediately redirected to the login page at the edge (before any server component renders).

Sidebar navigation

1
2
3
4
5
6
7
8
9
10
11
├── Schedule ← Build and edit event days, time periods, matches
├── Assignments ← Direct platform assignments (admin-created)
├── Requests ← Review queue for all pending spot requests
│ ├── Platforms ← Per-match surface requests
│ ├── Media Tower ← Non-match tower bookings
│ ├── Tents ← Radio & Broadcast Tent slots
│ ├── Stadium Positions ← Camera & reporter positions (match days only)
│ └── Parking Passes ← Daily parking requests
├── Attendees ← Full credentialed media roster + portal link management
├── Manifests ← PDF generation per day
└── Reports ← Usage analytics

The Day Picker (Schedule view)

The schedule view shows all 39 event days. For each day, the admin can see at a glance:
  • How many requests are Pending
  • How many are Approved
  • How many are Denied
Clicking into a day shows every match and every day-based surface. Each position displays a live tally — how many spots have been filled versus the total capacity, so the admin always knows exactly how much room is left before approving the next request.

Approving and denying

The admin can approve or deny individual requests from the day view or the surface-specific review queues. Denials include a free-text reason field that flows directly into the email sent to the attendee.

Post-lockout manual additions

Even after the 48-hour window closes, the admin retains full ability to:
  • Create new reservations manually (for late requests submitted via email)
  • Approve or deny requests that came in before lockout
  • Modify existing assignments
This accommodates the reality of live event logistics — sometimes a late accreditation arrives or a position opens up unexpectedly.

The Manifest — Printing for Security

At the end of the review period for each day, the admin can generate a PDF manifest — a clean, black-and-white printed list of every approved reservation for that day.
The manifest is designed specifically for printing and handing to security staff at each venue and position. Hard copies are essential when venue staff don't have device access or connectivity.

What the manifest contains

  • Header: ET date, generating admin's name, and a UTC-to-ET formatted generation timestamp
  • Per-match sections: For each match on that day, a block showing all four platform positions, each listing the approved attendee's full name, organization, and any notes
  • Daily sections (below per-match): Stadium broadcast positions (match days only), Parking Passes, Radio & Broadcast Tents (with time window), Media Tower non-match slots
  • Capacity counts: Each position shows N of CAP so staff know the total expected headcount
  • Footer on every page: "FIFA Fan Festival Philadelphia — Media Reservations — Manifest for [date] — CONFIDENTIAL"
Manifests are streamed as application/pdf with Cache-Control: no-store — always freshly generated, never cached, so they reflect the latest approval state at the moment of printing.

The Tech Stack

Core framework

  • Next.js 16 (App Router) — server components, route handlers, edge middleware
  • React 19 — UI rendering
  • TypeScript 6 — end-to-end type safety

Styling

  • Tailwind CSS 4 — utility-first styling
  • DaisyUI 5 — component layer with a fully custom mrp theme built on FIFA official colors (#193C9C dark blue, #304FFE country blue, #EEFF42 yellow)
  • Phosphor Icons — iconography throughout the admin and portal UIs

Backend / Database

  • Appwrite (node-appwrite v26) — backend as a service, handling:
    • Admin authentication (Users service)
    • All application data (TablesDB service — the current non-deprecated API)
    • Atomic transactions (withTransaction / batchWrite) for consistency on every mutation
    • 10 tables: event_days, time_periods, games, platforms, attendees, assignments, spot_requests, reservations, tier_definitions, audit_log

Email

  • Emailit — transactional email service, used for all status notifications. REST API v2 with exponential backoff retry and non-blocking dispatch.

PDF generation

  • PDFKit (Node.js) — streamed PDF generation for daily manifests. Runs server-only (excluded from the client bundle via serverExternalPackages).

Dates and time zones

  • date-fns 4 + @date-fns/tz — all times stored as UTC, displayed in Eastern Time. Every lockout calculation, manifest timestamp, and tooltip message runs through a centralized TimeZoneService.

Security

  • HMAC-SHA256 portal tokens (crypto module, timingSafeEqual comparison)
  • CSRF double-submit cookie pattern (bootstrapped at the edge in proxy.ts)
  • Rate limiting — sliding window, in-memory: 10/15min for admin login, 5/15min for portal link requests, 60/min for portal lookups, 20/min for reservation submissions
  • Zod — input validation on all API routes

Testing and tooling

  • Vitest 4 — unit tests + property-based tests with fast-check
  • Playwright 1.59 — end-to-end tests (portal flows, admin dashboard, accessibility, PDF verification, viewport)
  • axe-core — automated accessibility scanning in E2E suite
  • Prettier + ESLint 10 (flat config) — code formatting and linting
  • Netlify — deployment (@netlify/plugin-nextjs)

Why Kiro Made This Possible

This platform was built by one person — me — as a working media manager, not a full-time engineer. Kiro let me move fast and build right:
  • The Spec workflow helped me think through reservation rules, lockout logic, and cap enforcement before writing a single line — catching edge cases early rather than mid-tournament.
  • Autopilot mode handled the repetitive scaffolding (route handlers, repo files, email templates) so I could focus on the business logic that actually matters.
  • The steering files I built for this project (nextjs-architecture.md, dependency-management.md) gave Kiro the context it needed to consistently use the right APIs (TablesDB, not Databases; proxy.ts, not middleware.ts) throughout a large, complex codebase.
  • When I needed to trace through the lockout logic or understand the interaction between per-match and daily surfaces, Kiro helped me reason through it in plain language before implementing it.
The result is a production platform that is running live — handling real requests from real media organizations for the World Cup right now.

Media Reservation Platform — FIFA Fan Festival Philadelphia 2026
World Cup Matches: June 11 – July 19, 2026 | 104 Matches | 39 Days | 1,500+ Credentialed Media
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article