Event Pass
Admission to an event as ticket stubs, with seats, the holder, each ticket's state and a scannable entry code from the provider.
Experience · Shares patterns with Flight Ticket, QR Code
≈ 1,434 tokens per use (tool 985 · guidance 317 · typical payload 132)How token costs are estimated
Example: Event Pass
Show my ticket for tonight's concert
Juniper Avenue: Late Light tour
· Pier 9 Music Hall
General admission
- Entrance
- Main doors
- Doors
- 7:00 PM
Name: Maya Cohen
Entry rules: No re-entry · Bags under 30×20 cm
When to use
- The user wants to see their ticket, badge or pass for an event and the agent has it from the ticketing provider: "show my ticket for tonight", "my conference badge", "tickets for the match".
- They want to add it to their wallet, transfer a ticket or download it.
When not to use
- The agent doesn't have the tickets from a tool result. Never invent seats or entry codes.
- The user asks about the event itself: the agenda, dress code or RSVP. Use Event Details.
- It's a flight. Use Flight Ticket.
How the agent should use it
- Pass
code.valueexactly as the provider returns it. If the agent only knows the code lives in the provider's app, useformat: "barcode-placeholder"with a label saying where. - Set
ticketStatefor used, transferred and refunded tickets. Their codes are never shown. - Pass times with the venue's UTC offset and its IANA
timeZone. Never convert them to the user's timezone. - Every action is a handoff. On
add-to-wallet,transferordownload, open the provider's or your own flow for thatticketId. Never transfer a ticket without confirming. - Lifecycle: set
expiresAton the call to the event's end, so the pass turns inert in the Renderer once the event is over. When the provider reissues the tickets (a seat change, a new code), supersede the old call.fetchedAtisn't needed.
Layout
- Header: the event title, then one quiet line with the date and time in the venue's timezone and the venue name. With several tickets, arrows on the right page between them ("1 of 3"); on touch screens, swipe. The first usable ticket shows first.
- Pass fields, large: section, row, seat, gate, entrance and doors time, whichever the ticket has. Unseated tickets show their type ("General admission") above. Then the holder's name.
- Perforated divider, then the entry code, with the entry rules in one line beneath it.
Entry codes
A qr code is drawn from the provider's payload with qrcode-generator, encoded as UTF-8 at error correction level M. It sits on a white tile with the standard four-module quiet zone, in dark mode too, because scanners need the contrast. It's an image labelled "Entry code for ticket 1". Tell users to turn up their screen brightness at the gate in your own copy if your audience needs it. The code is only drawn once the tool call has finished streaming, so it never changes under a scanner.
A barcode-placeholder shows a neutral placeholder with the agent's label ("Barcode in the Acme Tickets app"); the component never generates a code.
Codes are never shown for used, transferred or refunded tickets. The stub dims and the code area says why: "This ticket was used at 7:42 PM", "Transferred to Noa Levi", "This ticket was refunded". Those tickets have no actions.
Actions
Each usable ticket offers, in order: Add to wallet (when walletAvailable), Transfer (when transferable) and Download. They act on the ticket shown; the first two are buttons and a third goes into a "More actions" menu. Each returns { action, ticketId } once, locks its button for that ticket, and the footer confirms it quietly ("Opening the transfer for ticket 2…"); the other actions and tickets stay available.
Installation
npx shadcn@latest add https://your-site.com/r/event-pass.json
Usage
import { tool } from "ai"
import { eventPassTool } from "@/components/genui/event-pass.schema"
const tools = {
// No execute: the user's first action is the tool result.
[eventPassTool.name]: tool({
description: eventPassTool.description,
inputSchema: eventPassTool.inputSchema,
}),
}Render the tool call and hand each action to your own flow:
import { EventPass } from "@/components/genui/event-pass"
function ToolPart({ part, addToolResult }) {
if (part.type !== "tool-show_event_pass") return null
return (
<EventPass
{...part.input}
status={part.state === "input-streaming" ? "streaming" : undefined}
result={part.state === "output-available" ? part.output : undefined}
onResult={(result) => {
if (result.action === "add-to-wallet") openWalletPass(result.ticketId)
if (result.action === "download") downloadTicket(result.ticketId)
if (part.state !== "output-available") {
addToolResult({ tool: "show_event_pass", toolCallId: part.toolCallId, output: result })
} else {
sendMessage({ text: `Event pass action: ${JSON.stringify(result)}` })
}
}}
/>
)
}The agent receives one of:
{ action: "add-to-wallet", ticketId: "RFC-20931-07" }
{ action: "transfer", ticketId: "RFC-20931-08" }
{ action: "download", ticketId: "RFC-20931-07" }With LangGraph
Push it from a graph node with ui.push({ name: "show_event_pass", props: { toolCallId, ...args } }, { message }), and render the thread's UI messages with the Renderer.
Restoring from history
When a conversation is reloaded, pass the stored result back as result, or an array when several actions were taken. Those actions show as done for their tickets and stay locked; they never call onResult again.
<EventPass
{...part.input}
result={part.state === "output-available" ? part.output : undefined}
/>Schema
| Prop | Type | Notes |
|---|---|---|
event | { title, start, end?, timeZone, venue: { name } } | ISO 8601 times with the venue's offset, or local time. Data-bound |
holder | { name }? | Data-bound |
tickets | Ticket[] | 1–6, in the provider's order. Data-bound |
tickets[].id, type | string | type like "General admission", "VIP", "Day pass" |
tickets[].section, row, seat, gate, entrance | string? | |
tickets[].code | { format: "qr", value } | { format: "barcode-placeholder", label }? | value is the provider's payload, never generated |
tickets[].ticketState | "valid" | "used" | "transferred" | "refunded"? | Default valid. Only valid tickets show a code |
tickets[].usedAt | string? | ISO 8601, for "used at 7:42 PM" |
tickets[].transferredTo | string? | Who a transferred ticket went to |
doorsOpen | string? | ISO 8601. Data-bound |
rules | string[]? | Up to 4. Data-bound |
transferable, walletAvailable | boolean? | Add Transfer and Add to wallet. Data-bound |
Host props: status (streaming, ready, submitting, done), locale, onResult, result and className.