restarticket.com - Boris Brejcha & Alphaville @ MVM Dome Budapest
Ticketing site for concerts at MVM Dome Budapest. Boris Brejcha (techno) Oct 31 and Alphaville (synth-pop) Oct 30, 2026. Hungarian market, TicketLive integration, bilingual UI.
2 concerts / 1 venue
Brief
Restarticket.com is a marketing + ticketing site for two back-to-back events at MVM Dome Budapest:
- Boris Brejcha (techno, high-energy) - October 31, 2026
- Alphaville ("Forever Young", synth-pop legend) - October 30, 2026
The promoter wanted a single domain for both shows because the audiences partially overlap (synth-pop fans 40+ frequently attend atmospheric techno, and the reverse 5–10 % crossover) and sharing marketing budget, hosting, and ticketing integration makes economic sense.
Why two events on one site
Two options on the table: two domains or one with two routes. I went with one domain, two route segments:
restarticket.com/
├── / ← landing with both events
├── /boris-brejcha ← Brejcha hero, lineup, ticketing
├── /alphaville ← Alphaville hero, ticketing
└── /venue ← shared venue info
Reasons:
- Free cross-promo. A Brejcha visitor sees a sticky banner "in town that morning? Catch Alphaville the night before" - measurable 12 % click-through to the second event.
- Shared infra. One Vercel project, one TicketLive vendor account, one analytics property.
- SEO authority accumulates on a single domain instead of being split between two.
- Budget. Two domains = two SSL certs, two DNS setups, two Vercel projects. For a one-shot event run, that's overhead.
The trade-off is purity of brand identity. A Boris Brejcha fan landing on "restarticket.com" momentarily wonders what it is. I solved it with contextual hero - if the user arrives via /boris-brejcha Google search, they see the Brejcha hero immediately, with restarticket branding secondary.
TicketLive vs SixArt
In the DJ BOBO project I worked with the SixArt API. Here the vendor is TicketLive (Hungarian). Key differences:
| SixArt (CZ/SK) | TicketLive (HU) | |
|---|---|---|
| Auth | HMAC-signed header | OAuth2 client credentials |
| Token | none (per-request signature) | access_token, TTL 3600 s |
| Availability | flat tier list | hierarchical section → row → seat |
| Realtime | polling 60 s | webhook + polling fallback |
| API stability | 99.5 % uptime | ~96 % uptime (flaky) |
| Latency p95 | 240 ms | 940 ms |
Their API is slower and less stable. Cache strategy had to be more aggressive.
Hungarian localization
Hungarian has its quirks:
- Number format:
1 234,56(space as thousands separator, comma as decimal) -Intl.NumberFormat('hu-HU') - HUF currency: no decimals (
Intl.NumberFormat('hu-HU', { style: 'currency', currency: 'HUF', maximumFractionDigits: 0 })) - Family-given order in UI ("Brejcha Boris" instead of "Boris Brejcha") on contact forms
- Date:
2026. október 31.not31. 10. 2026
UI is Hungarian primary, English fallback. The English version exists for foreign visitors (~30 % of traffic - Boris Brejcha has a global fanbase, international fans fly into Budapest).
Realtime seat counts
TicketLive returns counts per row, but aggregating to section is expensive. I wrapped it with unstable_cache (Next.js) + revalidateTag on webhook events:
import { unstable_cache, revalidateTag } from 'next/cache';
export const getEventAvailability = unstable_cache(
async (eventId: string): Promise<Availability> => {
const token = await getTicketLiveToken();
const res = await fetch(
`https://api.ticketlive.hu/v1/events/${eventId}/availability`,
{
headers: { Authorization: `Bearer ${token}` },
// no Next caching - controlled via unstable_cache
cache: 'no-store',
}
);
if (!res.ok) {
// vendor occasionally throws 502 - fallback to last-known data
throw new TicketLiveError(res.status, 'availability fetch failed');
}
const raw = await res.json();
return aggregateSections(raw);
},
['ticketlive:availability'],
{ revalidate: 60, tags: ['availability'] }
);
// webhook handler
export async function POST(req: Request) {
const event = await verifyTicketLiveWebhook(req);
if (event.type === 'inventory.changed') {
revalidateTag('availability');
}
return new Response('ok');
}Two layers of invalidation:
- Time-based (
revalidate: 60) - insurance if the webhook drops - Event-based (webhook →
revalidateTag) - instant update on a sale
Post-launch we had several incidents where TicketLive webhooks lied by 5–10 minutes. Without the time-based fallback, users would have seen "30 tickets" when the inventory was actually 0.
Lessons
- TicketLive is unstable. I prepared
try / catchwith a fallback "tickets selling, verify on vendor page" message. Better than a 500 error for an end user. - Webhooks alone aren't enough. Always have a time-based fallback.
- OAuth2 token refresh is a trap. First version refreshed every request - 1.4 s latency. Caching the token for 3500 s (TTL minus margin) brought p95 from 1.4 s to 240 ms.
- The cross-promo banner is a stupidly underpaid MVP feature. 12 % click-through from one event to the other is pure profit for the promoter and costs ~30 lines of code.
- A Hungarian locale isn't just an English locale with green flags. Number format, name order, currency - all different. I built
formatHuf()andformatHungarianDate()helpers; the rest of the team just calls the functions.
Site shipped 5 days from kickoff. Availability fetch p95 240 ms (down from 940 ms raw vendor latency, thanks to cache). Conversion rate on ticketing CTA 6.8 % (vs the promoter's 4.2 % industry baseline).