Event platformGlint2026

Ticket sales, the door, attendee relationships, and refunds run on their own within the rules. The queue only asks for a human decision on what falls outside them.

Client
Role
Product builder: discovery, design, and AI-driven development
Timeline
About 3 weeks (June 2026)
Disciplines
Product strategy, UX/UI, Design system, Business rules
Public page for a fictional event on Glint, with a hero, dates, venue, and a buy ticket button

The context

Event organizers in Brazil work with a patchwork: one tool sells tickets, another handles check-in, a spreadsheet tracks costs, and certificates are put together by hand. The data about who actually showed up ends up scattered and lost.

Glint was built to stitch all of that into a single product. The organizer creates the event, publishes a sales page, gets paid through Stripe, issues tickets with a QR code, runs check-in, talks to the audience, and handles post-sale support. Attendees, door staff, exhibitors, and affiliates each get their own access.

The idea that runs through the product is simple. Whatever has a clear rule runs on its own: capacity, QR scanning, refunds within the window, behavior tags. Whatever falls outside the rule becomes a queue for the organizer to decide.

The pain

Looking at how organizers actually work, the most expensive problems weren’t in the sale itself. They were at the edges, where the work is manual:

  • Capacity that doesn’t match the physical space. Selling tickets by type without tying them to the real area (floor, mezzanine, VIP box) opens the door to selling more places than exist.
  • Valid tickets being shared. A screenshot of the QR code circulates in a group chat and two people try to get in with the same code.
  • Attendees who become a name in a spreadsheet. Someone who bought tickets to three editions, visited a booth, and gave a 10 rating shows up as three unrelated records.
  • Refunds by email. The right of withdrawal exists (7 days under Brazil’s Consumer Protection Code, the CDC), but the process is manual, slow, and full of friction for both sides.

In a comparative analysis against competitors in the market, self-service refunds stood out as Glint’s biggest gap. It was one of the fronts prioritized later.

The research

Before designing, I broke the event down into model layers, because the interface depends on getting the model right:

  • Event → Section → Ticket type → Ticket tier. A section is the physical area, with a capacity. A type is the commercial condition (full price, half price, day pass). A tier is the price ladder, which advances by date, by percentage sold, or when it sells out.
  • One person, one timeline. Every interaction (purchase, check-in, product pickup, booth visit, rating, message, tag) becomes an event in that person’s history.
  • Core and extras. What every organizer needs (event, tickets, checkout, check-in, finances) is core and is never locked. Everything else comes with a plan.

Each feature became a product spec in the repository, with goals, acceptance criteria, happy and unhappy paths, and risks. The catalog has a master document and 21 specs, and it served as the contract between design, code, and tests.

The solution

A sales page with real sections

The public page groups tickets by section and shows the current tier for each one. When a section sells out, the page says so in plain words and offers a waitlist.

Sales page with the Floor available, the Mezzanine marked as a sold-out section, and the VIP Lounge with places remaining
The buyer sees capacity by area, not just by ticket type.

Checkout is short: buyer details, summary, coupon, and payment through Stripe. The price is always recalculated on the server, so changing the amount in the browser does nothing.

Checkout with buyer details, order summary, and an applied coupon with a discount
The coupon is validated on the spot; the total that counts is always the server's.

Hard capacity, enforced at configuration

The most important decision about sections was where to put the lock. The first version blocked at checkout. It worked, but it muddled the metrics: the dashboard showed tiers with availability in a section that had no room left.

The lock moved to configuration. The sum of the tiers across all ticket types in a section can’t exceed its capacity, and lowering the capacity below what’s already configured is rejected. As a result, the number the organizer sees is the number that exists. Per-tier reservations take care of the rest at purchase time, with nobody counting seats.

Ticket dashboard with sections in collapsible boxes, a capacity bar, and the status of each type
A section is a box with a capacity bar. The sold-out Mezzanine shows up in red before anyone has to ask.

Rotating QR: opt-in, for a good reason

For shared tickets, the QR code can rotate. Every 30 seconds the code changes, signed by the server, and the door only accepts the current window and the adjacent one. A screenshot stops working in under a minute. When an event uses rotating QR codes, the old static code is rejected at scan.

The cost is real: the attendee’s phone needs internet at the door for the QR code to refresh. A venue with poor signal plus rotating QR codes means a guaranteed line. That’s why the option is off by default and the organizer turns it on event by event, with the warning written right on the screen. If the connection drops, the app keeps the last code instead of showing an error.

QR security settings with the toggle on and a warning that attendees need internet at the door
The trade-off is on the screen, next to the toggle, not buried in a manual.

A refunded ticket, on the other hand, dies on the server, with or without rotating QR: every scan rejects the canceled code. On the web there’s no way to prevent a screenshot, so the defense lives at the scan.

Post-sale support that follows the CDC, with no email

Within the window, the attendee requests a refund in the app and the money goes back immediately, straight through Stripe. Outside the window, the request goes to the organizer to approve or decline. Upgrading to a higher category is always allowed (the attendee pays the difference). Downgrading is only allowed within the window (the attendee gets the difference back). The organizer sets the window, the cutoff before the event, and the retention.

Three phone screens: a ticket with a security QR code and countdown, a refund request within the window, and the exhibitor portal
Attendees and exhibitors handle on their own what used to require a message to the production team.

Here the invisible part mattered more than the screen. An adversarial review found a risk of duplicate refunds under simultaneous clicks. The fix was to make sure the item is canceled first, atomically, and only then does the money go back, with an idempotency key on Stripe.

Request queue with completed refunds, one pending request outside the window with approve and decline buttons, an upgrade, and a downgrade
The queue shows the origin and destination of each exchange and only asks for a human decision on what falls outside the rules.

360° CRM and the exhibitor portal

Every attendee has a profile with total spend, events, check-ins, pickups, and a single timeline. Tags are applied automatically based on behavior: “Visited: Lumen Labs” when their badge is scanned at the booth, “Bought at booth” when a purchase comes from a product linked to it. Nobody tags attendees by hand.

There’s also a privacy panel (under LGPD, Brazil’s data protection law) where people can see, export, or anonymize their data, while the financial record is preserved.

Profile of a fictional attendee with indicators, a timeline of purchases, check-ins, and visits, and behavior tags
Three editions, a booth, and a rating become a single story, not three spreadsheet rows.

Exhibitors log into their own portal with a code sent by email and capture leads by scanning the QR code on the badge.

Selling outside Glint

Three embeddable widgets take sales to the organizer’s own site: a ticket cart, a product cart, and a floating chat with the sales assistant. The buy button opens the event page with the cart already filled, so the widget reuses the entire checkout instead of duplicating it.

Preview of the ticket widget embedded in a generic page, with ticket cards and a total
The organizer sees how the widget looks on their own site before pasting the code.

Event overview and plans

The event dashboard adapts to the phase (planning, presale, the day before, event day, post-event) and shows budget, conversion, capacity, check-ins, and results.

Event dashboard with revenue and result indicators, lifecycle phases, and conversion and capacity metrics
The indicators change with the phase: before the event, conversion matters; after, results do.

The business model has three plans. The basic plan is free and covers the entire core. Paid plans unlock widgets, booths, automations, NPS, an AI bot, and affiliates. The lock lives on the server; the menu only hides what the plan doesn’t include. If the subscription payment fails, the account drops back to basic: the organizer loses the extras, but never the event that’s on sale.

How it was built

It’s a modular monolith: NestJS with domain modules, React with Vite, PostgreSQL, Redis, and Stripe, with the interface in Portuguese, English, and Spanish. I handled product, design, and code with AI. Large deliveries were broken into waves executed by several agents in parallel, with adversarial reviews on the parts that touch money and security.

The dark-by-default design system has about 17 components and was applied to around 50 screens. The automated suite passed a thousand tests, including isolation between organizers.

What changed

For event organizers, the work changes in nature. It stops being checking and replying, and becomes deciding on exceptions.

What was done by hand. Selling tickets by type without tying them to the physical area, dealing with screenshots of shared tickets at the door, piecing attendees together from scattered spreadsheets, and answering refund requests by email.

What became automatic. Capacity locked at configuration, a QR code that rotates and rejects screenshots, refunds within the window straight through Stripe, behavior tags applied when the badge is scanned, and a single timeline for every attendee.

Where a person decides. The organizer turns on rotating QR event by event, with the cost written next to the toggle. They set the refund window, cutoff, and retention. And they approve or decline, in the queue, only the requests that fall outside the rules.

What I took from this project

The decisions that mattered most barely show up in the interface. Moving the capacity lock from checkout to configuration didn’t create a new screen, but it made the dashboard number mean something again. Leaving the rotating QR off by default, with the cost written next to the toggle, is more honest than selling security without mentioning the line at the door.

It also became clear that, in a product that handles money, the happy path is the easy part. The real work is asking what happens with two simultaneous clicks, with a screenshot of a refunded ticket, or with a phone that has no signal at the venue entrance.

Next projectSimulators & AI assistant · ZeztraZeztra, simulators and an AI assistant in place of the cold contact form