<-- ./projects
in-progress|Apr 2026|4 min read

Event Swag Agent

AI swag allocation for a large events program—a Copilot Studio agent that proposes and negotiates packages within budget, paired with a dense admin app for the ops team, on one Dataverse backend.

Copilot StudioPower FxPower AutomateDataverseMicrosoft EntraViteReactTypeScriptFluent UI

The Problem

A central team runs the events program for a company with more than 5,000 account managers and sellers. On average that's an event a day—a seller hosting customers somewhere between the C-suite and the technical staff, where each tier of attendee warrants a different level of swag. Every event needs the right swag for the right people, allocated against a single budget that has to last the whole period.

The sellers requesting swag just want to describe an event and get a sensible package back. The ops team needs budget, inventory, allocation rules, and orders in one place instead of scattered across spreadsheets.

There was also a hard constraint up front: it had to be built Power-Platform-first—Copilot Studio for the agent, Dataverse for the data—so it lived natively inside the org's existing tools, identity, and governance rather than as one more outside app to adopt and secure.

The Insight

This is really two products with two opposite users, and forcing them into one interface would have made both worse. So I built two surfaces on a single shared Dataverse backend:

  • A Copilot Studio agent for the people requesting swag—conversational, low-friction, "describe your event."
  • An admin Code App for the ops team—dense, data-first, decision-oriented.

Same data underneath, two surfaces each tuned to a completely different user.

What I Built

The agent. Someone describes an event in natural language; the agent proposes a swag package, negotiates within budget, and finalizes it. It does portfolio-aware budget optimization—allocating against the program's remaining budget across all events, not just the one in front of it—with the negotiation math in Power Fx and two Power Automate flows handling context-loading and finalization.

The admin app. A Vite + React + TypeScript app in Fluent UI, built in the spirit of the Azure Portal's Cost Management rather than a marketing page: an events-first budget dashboard, a packages editor, personas managed as data, inventory, and orders. Budget health is a real business rule, not decoration—green / yellow / red derived from remaining budget against the program total.

The Hard Part

Two things were genuinely hard, and both came partly from the Power-Platform-first constraint.

First, budget-aware negotiation inside Copilot Studio. A Copilot Studio agent isn't a free-form LLM loop—you build within its topics, flows, and Power Fx. Getting it to propose a package, hold a budget constraint, and negotiate down without going off the rails meant pushing real optimization math into Power Fx and keeping the agent's state honest across the conversation, all inside those rails. The constraint cuts both ways: you trade raw flexibility for something governed, native, and auditable that a bespoke agent wouldn't be.

Second, personas-as-data. Those customer-attendee tiers—C-suite down to technical staff—started life hardcoded as a TypeScript union, which meant a code change and a deploy every time the program renamed or re-cut a tier. I tore that out and made personas editable data: rename a tier in settings and it propagates everywhere, no deploy. Boring-sounding, but it's the difference between a tool the ops team owns and a tool they have to file a ticket against.

Building on the Microsoft Stack

Everything lives natively: Dataverse for storage, an Entra-backed people picker for identity, Dataverse security roles for access, Power Automate for orchestration. Deployment is pac solution import for the agent and pac code push for the app. The UI follows Fluent 2 with WCAG-redundant status encoding (color and icon), feature flags for chrome surfaces, and a test suite pinned with fake timers so date-bucketed assertions don't flake on the CI clock.

Going Power-Platform-first is a real tradeoff: you give up some flexibility and you gain native identity, permissions, governance, and adoption you'd otherwise have to build and defend yourself.

What I Learned

  • Two users means two surfaces. A conversational agent for requesters and a dense admin for ops—one interface for both would have failed both.
  • Config that needs a deploy isn't config. Personas-as-data was the unlock for ops ownership.
  • Power-Platform-first is a deliberate tradeoff. Less flexibility, but native identity and governance—worth it when the tool has to live inside an existing org.