Monitor sales
Once an event is published and tickets are on sale, this workflow shows how to surface useful state: how many tickets each spec has sold, who the buyers are, what state their orders are in, and which dates have been redeemed at the door.
All steps require a JWT — get one from the homepage first. The API key needs the events:read scope.
1. Live ticket counts per specification
numberOfTicketsSold is a resolver field on TicketSpecification — a live count, not a snapshot. Combine it with numberOfTicketsForSale to get a "X of Y sold" report.
query salesPerSpec($id: UUID!) {
events(where: { id: { eq: $id } }, first: 1) {
nodes {
id
name
ticketSpecifications {
id
name
price
isActive
numberOfTicketsForSale
numberOfTicketsSold
}
}
}
}
2. Who's bought a ticket
ticketHolders(eventId: ...) returns one row per buyer (a platform user). Default page size is 20, max 50.
query whoIsComing($eventId: UUID!) {
ticketHolders(eventId: $eventId, first: 50) {
totalCount
nodes {
id
issueTime
user {
id
name
email
phoneNumber
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
3. Filter to paid orders only
Buyers may have orders in Pending or Expired states too. Filter the holder's buyOrders list to surface only Paid.
query paidOrdersOnly($eventId: UUID!) {
ticketHolders(eventId: $eventId, first: 50) {
nodes {
id
user { id name email }
buyOrders(where: { state: { eq: Paid } }) {
id
created
comment
chargeSessionId
eventTickets {
id
ticketSpecificationId
ticketSpecification { id name price }
amount
}
}
}
}
}
BuyOrderState values: Pending, Paid, Expired.
4. Pending orders that may still complete
A pending order isn't necessarily lost — expires tells you how long the buyer has to finish payment. Surface them so you can see how much potential revenue is still in flight.
query pendingOrders($eventId: UUID!) {
ticketHolders(eventId: $eventId, first: 50) {
nodes {
user { id name email }
buyOrders(where: { state: { eq: Pending } }) {
id
created
expires
eventTickets {
ticketSpecificationId
amount
}
}
}
}
}
5. Refunds and refunds-in-progress
Refund state lives on the individual EventTicket. Project across every holder's tickets to find the ones in trouble.
query refundActivity($eventId: UUID!) {
ticketHolders(eventId: $eventId, first: 50) {
nodes {
user { id name email }
tickets {
id
purchaseDate
isRefunded
isRefundInProgress
ticketSpecification { id name }
}
}
}
}
After fetching, filter client-side to tickets[*].isRefunded || isRefundInProgress.
6. Door-scan state per ticket
EventTicket.eventDates is a list of EventTicketDate rows — one per date the ticket grants access to. Each carries isRedeemed and timeOfRedemption. During and after the event, this is your live attendance feed.
query attendanceFeed($eventId: UUID!) {
ticketHolders(eventId: $eventId, first: 50) {
nodes {
user { id name }
tickets {
id
ticketSpecification { id name }
eventDates {
id
isRedeemed
timeOfRedemption
redeemedBy
}
}
}
}
}
redeemedBy is the inspector's identifier. Cross-reference it against the event's ticketInspectors list to see which inspector scanned what.