Skip to content

Engineering · 7 min read

Event Ticketing Platform Architecture: Holds, Idempotent Checkout, On-Sale Spikes and Migration

How to build a ticketing platform that never oversells: inventory holds, idempotent checkout, on-sale queues, payouts, tax, reindexing and migration.

Zain Khalid MalikZain Khalid MalikCTO & Co-founder, Innovation InsightPublished
Laptop showing code on a clean white desk

Short answer

A ticketing platform has to do one thing perfectly: sell each ticket once. That takes short inventory holds with atomic decrements, an idempotent checkout that treats payment, order and side effects as separate steps, a waiting room for on-sale spikes, payouts through connected accounts, tax by event location, search indexes you can rebuild without downtime, and a migration plan that runs the old and new systems side by side. This is how we approach each.

The shape of the system

Ticketing looks like e-commerce with dates attached, but the failure modes are different. Stock is small and fixed, demand arrives all at once when tickets go on sale, and a mistake is public: two people at the door holding the same seat. We split the platform into services that fail independently: catalogue and search (events, venues, discovery), inventory (capacity, holds, seats), checkout and orders, payments and payouts, ticket issue and check-in, and notifications. Event pages can be cached hard; inventory and checkout cannot.

ComponentMain jobWhat breaks if it is wrong
Catalogue and searchEvent pages, discovery, filtersSlow pages and stale availability, but no lost money
InventoryCapacity per ticket type or seat, holds, releasesOverselling, or tickets stuck in holds that never release
CheckoutQuote, tax, payment, orderDouble charges, orders without payment, payment without orders
PayoutsOrganiser balances, fees, transfersPaying organisers money that is later refunded
Tickets and check-inQR codes, scanning, offline door modeQueues at the door, duplicate entry

Inventory holds and oversell prevention

When a buyer picks tickets, create a hold: a row that reserves the quantity for a short time (typically 8 to 15 minutes, shown as a timer). The hold is created with an atomic conditional update, for example decrementing available capacity only where available is at least the requested quantity, inside one database transaction. If the update affects no row, the tickets are gone and the buyer is told immediately, before they type a card number.

  • General admission: one counter per ticket type, decremented atomically in PostgreSQL. A Redis counter in front is fine for speed, but the database stays the source of truth.
  • Reserved seating: one row per seat with a status (available, held, sold) and the hold ID; claim seats with a single update that checks every seat in the request is still available.
  • Expiry: a scheduled job releases expired holds, and checkout re-checks the hold is still valid before charging. Never rely on the browser timer.
  • Limits: cap tickets per order and per account for high-demand events, and keep a small allocation back for comps and fixes.

Idempotent checkout

Checkout is where retries happen: a slow network, a double tap, a payment provider timeout. Every checkout request carries an idempotency key generated by the client, and every step can be repeated safely. On Paint Nite, a live events business running ticketed experiences across the US and Canada, we rebuilt checkout as a six-stage pipeline: quote, tax, tender, persist, emit and effects. The order is written in one atomic transaction, failures have explicit compensation (a refund or a released hold, not a hope), and side effects such as emails and confirmations go onto a queue where they can be retried. The result is checkout with idempotent payments, atomic order persistence and queued, retryable side effects.

StageWhat it doesSafe to retry because
QuotePrices the hold: ticket price, fees, discountsPure calculation from the hold and the price rules
TaxApplies tax for the event's jurisdictionDeterministic for the same quote and location
TenderCharges the card or walletThe payment provider deduplicates on the idempotency key
PersistWrites order, tickets and ledger entries in one transactionA unique constraint on the checkout key stops a second order
EmitPublishes an order-created eventConsumers ignore events they have already processed
EffectsEmails, tickets, analytics, organiser balancesEach consumer is idempotent and retried from the queue

Webhooks from the payment provider are the other half: payment succeeded, failed, disputed, refunded. Store each event ID and ignore repeats. Our guide to Stripe webhooks and idempotency has the patterns in code.

On-sale traffic spikes and queues

A popular on-sale can bring most of an event's traffic into the first few minutes. Three measures keep it up. First, serve event pages and availability summaries from a CDN cache with short lifetimes, so reads never touch the inventory database. Second, put a virtual waiting room in front of checkout for flagged events: visitors get a place in line and are let into checkout at a rate the inventory service can handle. Cloudflare and Queue-it offer this as a service; a simple token-bucket queue in Redis works for smaller platforms. Third, scale checkout separately from the rest, and load test it at the expected peak before every large on-sale, not once at launch.

Bots arrive with demand. Rate limit by account and device, require sign-in for high-demand events, and add a challenge at the waiting room rather than at the payment step, where it would cost real buyers their hold.

Payouts

Organisers are paid through connected accounts (Stripe Connect or Adyen for Platforms), which also run identity checks. Keep an internal ledger per organiser: gross sales, platform fees, refunds, chargebacks and transfers, so every payout can be explained line by line. Release funds after the event by default; for trusted organisers, allow scheduled payouts before the event with a reserve held back for refunds. When an event is cancelled, the ledger tells you exactly what to claw back. See our Stripe Connect marketplace guide for the account setup.

Tax by jurisdiction

Admission tax usually follows the place of the event, and the rules vary by US state, Canadian province and country, including whether the platform or the organiser is responsible for collecting it. Put tax in its own stage of checkout with its own tests, use a tax engine or a rules table maintained by someone who reads the changes, and store the tax breakdown on each order for refunds and reports. Paint Nite runs separate Stripe accounts, keys and webhook secrets for the US and Canada, with a Canadian tax path and per-state nexus rules, because a cross-border platform is two payment set-ups, not one with a currency switch.

Search and reindexing without downtime

Discovery runs on a search engine (OpenSearch or Elasticsearch) fed from the catalogue. The problem comes when the index schema changes: a new filter, a different analyser, a new ranking signal. Rebuilding in place means downtime or broken search. The pattern we used on Paint Nite: versioned indexes behind an alias, dual-write to the old and new index during a rebuild, and resumable reindex jobs, then switch the alias when the new index has caught up. Search schema changes that previously required production downtime now ship with none.

Check-in at the door

Each ticket carries a signed QR code (a ticket ID plus a signature the scanner can verify offline). The door app downloads the attendee list before doors open, validates codes locally, records scans and syncs them as soon as it can, so a venue with no signal still works. When two devices scan the same ticket while offline, the first sync wins and the second shows a warning to staff. Rotate the signing key per event, and invalidate tickets on refund or transfer.

Migrating from a legacy ticketing platform

Ticketing projects are often replacements, and the business cannot stop selling while the new system is built. Do not plan a big-bang cut-over. On Paint Nite we built dedicated sync services that keep the new platform and the legacy one aligned, so features migrate in slices: events, venues and customers stay consistent in both while booking, checkout and operations move across one area at a time. The new platform is eight NestJS services behind a single GraphQL gateway, communicating through a RabbitMQ message queue, built by five of our engineers alongside the client's technical lead, with 754 automated test files run on every pull request.

  1. Map the data first: events, ticket types, orders, customers, organisers, balances. Decide which system owns each during the transition.
  2. Build sync in both directions where needed, with conflict rules and a reconciliation report you check daily.
  3. Move read-only surfaces first (event pages, search), then new events, then checkout, then payouts last.
  4. Keep old ticket QR codes valid at the door until every event sold on the old platform has passed.
  5. Turn off the legacy system only after a full payout cycle reconciles.

If your current platform is old enough that nobody wants to touch it, our legacy modernization service starts with an assessment of what to keep, wrap or replace.

Cost and next steps

An Eventbrite-style MVP with general admission, checkout, QR tickets and payouts costs $34k to $63k with a senior offshore team; the breakdown and a fee comparison are in cost to build an app like Eventbrite. Platform rebuilds with migration usually sit in our $40k to $150k+ modernisation range. See event ticketing platform development and the wider events and ticketing page for how we run these projects.

Sources

Need a number for your project?

Send a short brief and get a written estimate.

A senior engineer replies within one business day. No sales call required.

Zain Khalid Malik, CTO & Co-founder, Innovation Insight

Zain Khalid Malik

CTO & Co-founder, Innovation Insight

Zain owns architecture, engineering standards and the platform team at Innovation Insight. He sets the bar for code quality, security and the tooling every squad ships with.

LinkedIn
FAQ

Related questions.

How do ticketing platforms prevent overselling?

With short inventory holds created by an atomic conditional update in the database, so capacity can never go below zero, plus a re-check of the hold before the card is charged. Expired holds are released by a server job, not by the browser.

What makes checkout idempotent?

A client-generated key on every checkout request, payment calls that pass that key to the provider, a unique constraint that stops a second order for the same key, and side effects processed from a queue by consumers that ignore duplicates.

How do you handle on-sale traffic spikes?

Cache event pages at the CDN, put a virtual waiting room in front of checkout for high-demand events, scale checkout separately, and load test at the expected peak before every large on-sale.

Can we migrate off our ticketing platform without stopping sales?

Yes. Run the old and new systems side by side with sync services between them and move features across in slices, finishing with payouts. That is how we approached the Paint Nite rebuild.

How much does a custom ticketing platform cost?

About $34k to $63k for an MVP. Rebuilds that include migration from a legacy system usually fall in our $40k to $150k+ range, depending on how much has to move.