Inspiration

Every group plan I've been part of goes the same way. One or two people pick, everyone else goes along, and the same quiet friend ends up somewhere they don't like. A group poll doesn't fix it. It just picks what most people like, so the same person loses every time. I wanted the plan to give everyone's taste an equal say, and Qloo is the first API that can actually score how much a specific person will like a specific place.

What it does

  • An organizer creates an event (city, date, budget) and shares a private invite link. Friends join with no account.
  • Each person picks three favorites (artists, movies, places). These are resolved through Qloo /search, so they're real Qloo entity IDs.
  • A swarm of agents builds the plan. The Scout gets candidates from Qloo /v2/insights and /v2/tags for the city. Each person's taste agent scores every candidate with that person's Qloo affinity. The Negotiator checks every complete plan (two dining picks, an activity and a playlist artist, plus a destination for trips) and keeps the one where the least happy person is as happy as possible (maximin), with Borda points breaking ties.
  • Measured impact panel: for each plan it shows the least happy person's score under Quorum vs a popularity vote, the group average, a Jain fairness index, how many plans were compared, and how many Qloo requests were live vs cached. All of these numbers are computed for that plan, and none are invented. If the group already agrees, the panel says so.
  • Happiness matrix: every pick against every person, with the Qloo affinity score in each cell.
  • Swarm timeline: a replayable record of each agent's steps and objections.
  • Demo mode is clearly labeled synthetic. Live mode uses only Qloo data. If Qloo leaves out an affinity score, that candidate is skipped and never filled in with a guess.

How we built it

  • Next.js 16 (App Router) + React 19 + TypeScript, deployed on Vercel.
  • Qloo Taste AI hackathon API: /search to resolve favorites, /v2/insights with signal.interests.entities, filter.location.query, filter.tags and filter.results.entities to score a fixed candidate pool per person, and /v2/tags for activity tags. All responses are validated with Zod.
  • Neon Postgres + Drizzle store events, attendees, plans and a Qloo response cache, so we stay under the request budget (a live 4-person plan took 25 requests, 10 of them from the cache).
  • Neon Auth for organizers, plus signed, httpOnly attendee session cookies for invite links.
  • The fairness engine is a small TypeScript module with exhaustive search, maximin, Borda tie-breaks and Jain's index. It's covered by unit tests.
  • Optional LLM narration (OpenAI, Gemini or Groq) is switched off by default. The deterministic trace is always the source of truth.

Challenges we ran into

  • The hackathon API doesn't always return query.affinity, so we built a normalizer and skip rows that have no score instead of guessing.
  • The first /v2/tags hit for "restaurant" wasn't reliable, so dining uses Qloo's restaurant genre tag plus a filter that keeps only real food places.
  • To get scores everyone can compare, each person has to be scored on the same candidate pool. So we build the pool first, then re-query /v2/insights per person with filter.results.entities.
  • Staying inside the 60-requests-per-minute budget for larger groups, which the Neon cache solves.

Accomplishments that we're proud of

  • It really is live: a 4-person New York plan compared 1,568 complete plans using real Qloo scores.
  • Every number on screen can be traced back to a Qloo response or a formula.
  • It's honest when fairness doesn't change anything. When tastes line up, Quorum says "everyone already agreed" instead of claiming a win.

What we learned

Fairness needs a baseline to mean anything. Showing the popularity-vote plan next to the maximin plan made the trade-off clear. Qloo's cross-domain affinity (music taste → restaurants) is what makes per-person agents possible. An LLM alone can't produce those scores.

What's next for Quorum

  • Booking and availability handoff (OpenTable/Resy links) for the chosen picks.
  • Weighted fairness for birthdays or a guest of honor.
  • Recurring groups, so the person who compromised last time gets more say next time.
  • A Slack/Discord bot so teams can plan offsites where they already chat.

Built With

Share this project:

Updates

Submission history