Per-Customer AI Cost Attribution: Building the Margin Map That Changed Our Pricing
Part of LangChain Agent to Production: WhatsApp Hotel Booking
We had revenue numbers and usage numbers on separate dashboards and assumed the math worked out. It took building a per-tenant P&L to discover that some of our busiest customers were quietly costing us more than they paid.
This post is a deep dive from our WhatsApp hotel booking case study. For the billing tools and implementation patterns, see How SaaS Companies Should Be Billing for AI Features.
The Blind Spot
For the first month of our WhatsApp hotel booking system, all LLM spend showed up as one line item from the gateway. Total monthly cost across hundreds of properties, expressed as a single number. Which properties drove it? Which conversation patterns were expensive? Was our subscription model working better than pay-as-you-go, or worse? We couldn't answer any of it.
We say "property" throughout this piece, but the pattern applies to any per-customer AI system: support bots, copilots, enterprise tools. If your customers have variable AI usage and your pricing doesn't account for it, you have the same blind spot.
What we ended up building - a per-tenant AI P&L - became the most important tool we shipped. More important than any AI feature.
The Numbers Nobody Plans For
Our back-of-the-envelope math assumed 3–4 turns per booking, 500–700 tokens total. A few rupees per conversation. Reality disagreed.
The median conversation sat under 1,000 tokens end-to-end (excluding the 8,000-token system prompt, cached via Gemini's implicit caching at a 90% discount). But the 90th percentile was 3–4x the median. The 99th percentile stretched to 20–30x. The top 5% of conversations consumed 35–45% of all tokens. The top 1% alone accounted for a low-teens share of total spend.
What drove the skew: guests who asked 15–20 questions about local sights and policies then disappeared without booking. Budget homestays at ₹1,500/night (about $18) that couldn't absorb the same AI cost as a ₹8,000/night (roughly $95) resort. And complex multi-room requests that triggered repeated availability passes through our LangGraph pipeline.
After optimization - about 40% of calls routed to the reasoning-tier model, about 60% to the cheaper classification-tier model, system prompt cached across both - the per-conversation cost landed sub-rupee at the median (around a US cent), a few rupees at P90, and low double-digits at P99 for the outliers who treated the bot as a personal travel concierge.
Each number looks trivial on its own. Add them up across a property's monthly volume with no way to connect cost to revenue, and you have no idea whether you're building a business or funding free customer support.
Two Commercial Models, Two Different Problems
The subscription tier started at a low monthly fee scaled by room count. A small homestay at that tier generating 60–100 conversations in a busy month looked survivable at the median, roughly 10–12% of its subscription revenue. But an engaged property, with guests asking about treks, restaurants, weather, and festivals, could push 20–25 conversations into P90 territory and 5–10 into P99. That sent inference alone to 30–50% of the monthly fee. The uncomfortable irony: the properties where the product worked best were the ones most likely to be unprofitable for us.
The pay-as-you-go math was different but equally fragile. We took a single-digit percentage commission on each booking, payment processing took its cut of that, and net revenue per booking was thin to begin with. At 15% conversion it worked, with LLM cost running 5–7% of revenue. At 5% conversion on a chatty property, AI spend crossed 10–15% of per-tenant revenue, approaching OTA commission rates. Our entire value proposition was "zero-commission alternative to OTAs." AI spend at OTA-commission levels wasn't a line item to trim; it undercut the reason customers signed in the first place.
None of this showed up until we built the per-tenant AI P&L.
Building the Attribution Pipeline
In practice, we ended up with three layers that each did one thing: a gateway for instrumentation, Langfuse for measurement, and Stigg for enforcement. We tried combining them early on and it was a mess. They need to stay separate.
The gateway
All LLM calls routed through a LiteLLM-style gateway that stamped metadata onto each request before forwarding it to the model provider: tenant, conversation, pipeline stage, model, and guest segment. The gateway was the right place to instrument because it sat on the path of every call regardless of which node, model, or retry logic triggered it. Instrument at the application layer instead and you miss retries, fallbacks, and error recovery calls that bypass your code.
We added these tags after the first month of production, which meant that month of spend stayed unattributed forever. There's no backfilling it.
Langfuse
Langfuse received traces on every generation with the full metadata context, token counts, latency, and computed cost. The per-call granularity - not per-conversation, per individual LLM call - turned out to be the thing that mattered most. It showed us that RankAndExplainOptions consumed 60% of a typical conversation's cost despite being just one of 2–3 calls in the pipeline. Without that granularity we'd have optimized the wrong thing.
We could slice cost by property, by stage, by model, by date range, or any combination. When a property's cost spiked, we drilled from the aggregate down to specific conversations, then to specific calls, and saw exactly what happened. The gateway forwarded already-redacted prompts, so cost attribution never touched raw guest data.
For the full observability architecture beyond cost: AI Observability and Debugging in Production with Langfuse.
The Per-Property P&L
We joined three data sources into a single per-property view: inference cost from Langfuse, booking revenue from our platform, and subscription fees from billing records.
Three profiles emerged. Healthy properties, where LLM cost barely registered against margin. Subscription traps, loved by guests and deeply unprofitable for us. And conversion sinkholes, where guests chatted a lot, booked rarely, and every unbooked conversation was pure loss. Before this dashboard every property looked the same. After it, we knew exactly which ones needed restructuring.
Not All Tokens Are Equal
Most teams try to optimize total token consumption. We learned to focus on the 1–2 nodes that actually dominate cost.
Langfuse showed us that RankAndExplainOptions - where the model picked and explained property recommendations - ate the majority of per-conversation spend and genuinely needed Gemini 2.5 Flash's reasoning quality. But intent classification and constraint extraction performed identically on Flash-Lite at a fraction of the cost. So we split: classification nodes got Flash-Lite (~60% of calls), reasoning nodes got Flash (~40%). We validated with our DeepEval suite - classification accuracy was identical on the cheaper model, ranking quality dropped without Flash. The split preserved quality where it mattered.
When a property hit its entitlement soft cap, all calls dropped to Flash-Lite. Worse ranking explanations, but within budget. Stigg enforced this automatically.
Cost Leaks That Only Show Up in Per-Call Data
Per-call attribution also surfaced problems that aggregate monitoring would have missed entirely.
On one property with a flaky PMS integration, failed API calls triggered LangGraph retries, and each retry meant a fresh LLM call. Without per-call tagging, those retries looked like normal traffic. They were doubling LLM cost on 15% of that property's conversations.
Our gateway also had a fallback path: if Flash returned a 503, the call re-routed to a backup model at different pricing. That worked exactly as designed and conversations never broke, which is precisely why nobody looked at it. The cost delta only showed up once we started reading per-call data.
And then there were the guests who just wouldn't stop chatting. 30, 40, 50+ messages with zero booking intent, indistinguishable from a legitimate long booking flow unless you can price the conversation. Once we could, we added turn limits.
Individually, each leak was small. They compounded.
What the P&L Actually Changed
The per-tenant AI P&L didn't just inform pricing conversations. It changed how the product behaved.
For subscription traps and conversion sinkholes, the agent stopped playing concierge after a configurable number of turns without booking progression. Instead of answering "what are the best restaurants nearby?" for the fifteenth time, it steered toward: "I'd love to help - shall I first check room availability for your dates?"
On reasoning-heavy nodes, we trimmed prompt verbosity for budget-tier properties. That cut output tokens by about 20% on RankAndExplainOptions without quality degradation on our DeepEval suite.
Conversations without a booking reference after 12 turns got a gentle escalation: "Would you like me to connect you with the property team directly?" Tuned per property profile using the P&L data.
And the entry-level subscription tier got a conversation soft cap calibrated to median consumption. Properties consistently exceeding it were offered tier upgrades - backed by their actual usage data, not a sales pitch.
Wiring Stigg for Enforcement
Every LLM call emitted a Stigg metering event from the same gateway metadata: tenant, feature, token count, and pipeline stage. Stigg aggregated these against the property's entitlement for the billing period.
Before each LLM call, the gateway checked with Stigg: is this property within budget? If yes, proceed with the configured model. Approaching the soft cap - drop everything to Flash-Lite and log the downgrade. Over the hard cap - activate turn limits and steer toward booking. About 5ms of added latency via Stigg's sidecar cache. We didn't block usage. We degraded it intelligently. That mattered - hard limits would have killed the guest experience, and unlimited usage would have killed our margins.
For the full tool evaluation: How SaaS Companies Should Be Billing for AI Features.
Two Dashboards
Property managers got a weekly WhatsApp summary - conversations, bookings, conversion rate, AI usage against their allocation, and the top guest topic that week. No token counts, no model names. Just the numbers they cared about.
We got the margin picture. Three alerts came out of it, and unlike most dashboards we've built, these ones actually changed decisions. The cost alert fired when weekly LLM spend exceeded a sustainable share of revenue for two consecutive weeks. The conversion-to-cost alert fired when PAYG conversion dropped while volume stayed high. The most useful of the three watched for weekly tokens deviating from the trailing 4-week average, which is how we caught seasonal spikes and new marketing campaigns before they hit margins. All three fed a weekly ops review with three possible outcomes: change the AI behavior, start a pricing conversation, or restructure the commercial model.
What We'd Do Differently
Tag every LLM call with tenant_id, conversation_id, and stage from day one. Not after the first billing surprise.
Price against projected LLM cost by property profile, not room count. At a low monthly subscription, there was no margin to discover problems gradually.
Check that your metering tools work with your payment gateway before you commit. Razorpay is the default in India. Several entitlement platforms only integrate with Stripe.
The model is the easiest part of shipping AI. The economics around it - knowing what each customer costs you, enforcing budgets without wrecking the experience, connecting LLM spend to revenue at the tenant level - that's the system that determines whether you have a business. If you can't measure margin per customer, you're not running an AI product. You're running a subsidy program.
Content on this page may not be reproduced, distributed, or republished without prior written permission. Sharing links is encouraged. See our Terms of Use for details.