Half of every support ticket is asking what the user already did. Attach the answer instead.
The ticket says "it's not working" - and the next twenty minutes go to figuring out who this user is, what plan they're on, and whether "it" is a bug, a quota, or a misunderstanding. The actual fix usually takes two. All of that context lives in your usage data; here is how to attach it to every ticket automatically.
The hook
Support for a solo founder is a context-reconstruction job. The ticket says "it's not working" - and the next twenty minutes go to figuring out who this user is, what plan they're on, what they were doing when it broke, and whether "it" is a bug, a quota, or a misunderstanding. The actual fix usually takes two. All of that context lives in your usage data at the moment the ticket arrives, so I attach it to every ticket automatically - a generated context card, written for me, before I've read the message. My app meters usage with Vevee (@vevee/sdk). When a ticket comes in (helpdesk webhook, support@ pipe, contact form - anything with a user identifier), one compose() call builds the card:
import { createClient } from "@vevee/sdk";
const vevee = createClient({ apiKey: process.env.VEVEE_SECRET_KEY! });
interface SupportContext {
who: string; // plan, tenure, usage level in one line
recentActivity: string; // what they did in the last 48h, in order
likelyIssue: string; // best inference: bug / quota / confusion
quotaState: string; // where they stand vs. limits RIGHT NOW
watchOut: string; // anything that changes the tone (churn-risk, VIP, repeat issue)
}
async function onTicketCreated(ticket: Ticket) {
const ctx = await vevee.compose<SupportContext>(
"support-context",
ticket.userId,
{ subject: ticket.subject } // the ticket subject steers the inference
);
if (ctx.status === "generated") {
await addInternalNote(ticket.id, formatCard(ctx.output));
}
}The prompt behind the card
The compose type's prompt: "A support ticket just arrived from this user with subject '{subject}'. From their recent events and usage, reconstruct what they were most likely doing when the problem occurred. State their exact quota position. Infer whether this smells like a bug, a limit, or a misunderstanding - and say which evidence points there. One line of anything the responder should know about this account." Data sources: user usage, user events, user attributes - and if prompt logging is enabled for the app, their recent prompts, which is often the difference between guessing and knowing.
The card on a real ticket
Ticket subject: "generation keeps failing???" The card that landed on it as an internal note:
- Who: Pro ($15/mo), customer for 7 months, heavy daily user.
- Last 48h: 22 successful image_generation events, then at 14:12 four consecutive reserve() denials, then two more attempts, then the ticket (14:31).
- Quota state: 498/500 monthly generations. The "failures" are limit denials, not errors.
- Likely issue: quota, not bug - denials line up exactly with the cap, and nothing failed before 14:12.
- Watch out: second month in a row she's capped before month-end. She's outgrown Pro; this ticket is an upgrade conversation wearing a bug report's clothes.
The ninety-second reply
My reply took ninety seconds and didn't open with "could you tell me more about the issue?" It opened with: "You're not seeing a bug - you hit your monthly cap at 14:12 (you're at 498/500). Second month running you've maxed it, so you might genuinely want the next tier; here's what that looks like." Accurate, fast, and the support ticket just became the most credible upsell surface in the product - because it's true and it solved their problem.
Why founders should steal this even with a support tool that "has AI"
Helpdesk AI summarizes the conversation. This summarizes the product reality - quota positions, event sequences, reservation denials - which the helpdesk cannot see and which is where the answer usually is. The two compose nicely: their AI drafts the prose, your card supplies the facts. Mechanics: server-side webhook handler (sk_* key), fractions of a cent per card (usage.costMicroUsd reports each one; the dashboard's monthly compose budget caps the pile). The math that sold me: ~20 minutes of context archaeology per ticket becomes ~2. At even five tickets a week that's 90 founder-minutes back, every week, for one webhook and one dashboard config.
Keep reading
I rewrote my cancel flow with one LLM call. It argues better than I do.
The cancel flow is where SaaS revenue goes to die politely. "Are you sure? You'll lose access to Premium features" has never changed a single mind - but reminding a user of their own 312 generations this month is an argument.
ComposeThe trial-ending email everyone sends is the same email. Here's the one that converts
"Your trial ends in 3 days! Upgrade now to keep access." You've received a hundred of these. You've deleted a hundred of these. The email fails because it's about your product, not about the user's trial.