name: subscription-tracker description: "Scan a user's Gmail to find upcoming subscription renewals, recurring payments, and billing deadlines. Use this skill whenever the user asks about their subscriptions, recurring charges, upcoming renewals, payment schedules, billing timelines, or wants to audit what services they're paying for. Also trigger when users say things like 'what am I subscribed to', 'when is my next bill', 'show me my recurring payments', 'subscription audit', 'what's renewing soon', 'am I still paying for X', or any variation involving tracking, managing, or reviewing subscription costs from their email. This skill requires the Gmail MCP connector to be enabled."
Subscription Tracker
Surface upcoming subscription renewals and payment timelines by scanning the user's Gmail inbox.
Overview
This skill searches Gmail for subscription-related emails (receipts, renewal notices, payment confirmations, trial expiry alerts, billing reminders) from the last 30 days, extracts structured data, and presents it as an interactive React artifact with a downloadable spreadsheet.
The primary goal is to surface upcoming renewals and payment timelines — what's due soon and what the user needs to act on. Renewals projected far into the future (3–12 months out) are still shown but visually deprioritized so the user's attention goes to what's imminent.
Prerequisites
- Gmail MCP must be connected. If not available, tell the user: "This skill needs access to your Gmail. Please connect Gmail from the integrations menu and try again."
Workflow
Step 1: Search Gmail for Subscription Emails
Run multiple targeted Gmail searches to catch different types of subscription communications. Use Gmail:gmail_search_messages with these queries, scoped to the last 30 days using after:YYYY/M/D (calculate 30 days before today's date):
Run all of these searches (each with maxResults: 50):
"subscription renewal" OR "subscription confirmed" OR "your subscription" after:YYYY/M/D"payment receipt" OR "payment confirmation" OR "payment processed" after:YYYY/M/D"billing statement" OR "invoice" OR "your bill is ready" after:YYYY/M/D"trial ending" OR "trial expires" OR "trial will end" OR "free trial" after:YYYY/M/D"auto-renewal" OR "auto-renew" OR "will be charged" OR "upcoming charge" after:YYYY/M/D"membership renewal" OR "plan renewal" OR "renews on" after:YYYY/M/Dsubject:(receipt OR invoice OR payment OR renewal OR subscription) after:YYYY/M/D
Deduplicate results by messageId across all searches before proceeding.
False positive filtering: Before reading the full message, do a quick triage on the snippet and sender:
- Keep: Emails from billing/payment senders (stripe.com, razorpay.com, paypal.com, billing-noreply@, invoice+@, receipts+@, subscriptions@), or emails whose snippet contains currency amounts, receipt/invoice numbers, or payment confirmation language.
- Skip: Newsletter/Substack emails that merely contain the word "subscription" in their body (e.g., "You're reading the free edition of..." or "Your subscription to this newsletter"). Look at the sender domain — substack.com, every.to, beehiiv.com are almost always newsletters, not billing emails.
- Skip: Job alerts, HR tickets, password resets, and marketing emails that happen to contain billing keywords in unrelated context.
This filtering step is important because broad keyword searches will return many false positives from newsletters and marketing emails. Focus on emails where a financial transaction actually occurred or is about to occur.
Step 2: Read and Parse Emails
For each unique message found, use Gmail:gmail_read_message to get the full content. Important: Some billing emails (especially from Razorpay, Stripe) have rich data in the snippet field but a minimal plain-text body. Always check BOTH the snippet and body for data extraction — the snippet often contains the most useful structured information (amounts, dates, plan names) that may be stripped from the body. Extract the following fields from each email:
| Field | What to look for |
|---|---|
| Service Name | Sender name, brand name in subject/body (e.g., "Netflix", "Spotify", "Adobe Creative Cloud") |
| Amount | Dollar/currency amounts near keywords like "total", "charged", "amount due", "price" — include currency symbol |
| Renewal / Payment Date | Dates near "renews on", "next billing date", "due date", "will be charged on", "payment date" |
| Plan / Tier | Plan names like "Premium", "Pro", "Family", "Enterprise", "Basic", billing cycle (monthly/annual/quarterly) |
| Billing Cycle | "monthly", "annually", "yearly", "quarterly", "weekly" — infer from amount patterns if not stated |
| Cancellation Deadline | Dates near "cancel before", "cancellation deadline", "to avoid being charged", "trial ends" |
| Payment Link | URLs near "pay now", "view invoice", "manage subscription", "update payment", "view bill" — extract the href |
| Has Invoice Attachment | Note if the email has a PDF attachment (invoice, receipt) — flag it as "Invoice attached" so the user knows to check |
| Email Date | The date the email was received |
| Status | Classify as one of: upcoming, paid, trial_ending, past_due, cancelled, one_time based on email content |
Additionally, for each subscription compute and store:
days_until_renewal: integer — number of days from today to the next renewal date (negative if past due, null if unknown)urgency_tier: one ofurgent(≤7 days),soon(8–30 days),upcoming(31–90 days),later(91–365 days),unknown— derived fromdays_until_renewal
Parsing guidance:
- If a field isn't found, set it to
null— don't guess or hallucinate values - For amounts, normalize to include currency symbol (e.g., "$9.99", "₹299", "€14.99")
- For Razorpay subscription emails, the snippet often contains the next due date, plan name, and amount — extract these directly. Look for "Next due on" and "Subscription Plan" fields.
- For Stripe receipt emails, the subject contains a receipt number (e.g., "#2360-9962-4668") and the snippet often contains "Amount paid" and "Date paid".
- For dates, normalize to ISO format
YYYY-MM-DD - Group multiple emails from the same service — keep the most recent/relevant one
- When the same service appears multiple times, consolidate: use the latest email's data but note the payment history
Step 3: Consolidate, Project Renewals, and Prioritize
After parsing all emails:
- Group by service name — merge entries from the same service (e.g., multiple Spotify receipts → one Spotify entry)
- Pick the most informative email per service — prefer renewal notices or upcoming billing alerts over past receipts
- Calculate next renewal date — if only past payment dates are available and the billing cycle is known, project the next renewal date forward from the last payment date. Keep projecting until you find the next date that is in the future (relative to today). For annual subscriptions, this could be up to 12 months out.
- Assign urgency tier based on the next renewal date (or cancellation deadline, whichever is sooner):
| Tier | Window | Visual Treatment |
|---|---|---|
| 🔴 Urgent | Within 7 days | Red accent, top of list, bold row |
| 🟠 Soon | 8–30 days | Orange accent, prominent placement |
| 🟡 Upcoming | 31–90 days | Yellow/amber accent, standard row |
| ⚪ Later | 91–365 days | Muted/gray text, collapsed or bottom section |
- Sort by urgency, then by date — within each tier, sort by nearest renewal date first. The output should read like a timeline: what's due this week → this month → this quarter → later this year. One-time purchases and items with no projected renewal go to the very bottom.
Step 4: Generate the Output
Build the output as a React (.jsx) artifact saved to /mnt/user-data/outputs/subscription-tracker.jsx.
The artifact should include:
Header Section
- Title: "Subscription Tracker"
- Urgency banner: If any renewals are due within 7 days, show a prominent alert: "⚠️ X renewal(s) due this week"
- Summary stats: total monthly spend (estimate), number of active subscriptions, count of renewals in next 30 days
- Date range scanned
Interactive Table
The table should be organized into urgency sections with clear visual separation:
Section headers: "Due This Week", "Due This Month", "Next 3 Months", "Later This Year" — each section only appears if it has entries. Empty sections are hidden.
Columns (all sortable by clicking headers):
- Urgency indicator (color-coded dot matching tier: 🔴 red, 🟠 orange, 🟡 yellow, ⚪ gray)
- Service Name
- Amount
- Billing Cycle
- Days Until Renewal (e.g., "3 days", "2 weeks", "4 months" — human-readable relative time, with exact date on hover/tooltip)
- Plan/Tier
- Cancellation Deadline (highlight in red if within 7 days)
- Payment Link (clickable "Pay" button if available, otherwise "—")
Features
- Search/filter bar to filter by service name
- Urgency filter tabs: All / This Week / This Month / 3+ Months — default view is "All" but the visual weight is on near-term items
- Sort by any column (click header to toggle asc/desc) — default sort is by renewal date ascending (soonest first)
- Rows in the "Later This Year" section should appear with reduced opacity or muted colors so they don't compete for attention with urgent items
- The "Due This Week" section should have a subtle red/warm background to draw the eye
- Responsive layout
Design Direction
- Clean, dashboard-style aesthetic — not generic
- Use a warm color palette: soft backgrounds with sharp accent colors for status indicators
- Distinct typography: use a characterful sans-serif for headers (import from Google Fonts)
- Subtle card-based layout with hover states
- No purple gradients or generic AI look
Download Button
Include a "Download as Spreadsheet" button that triggers generation of the .xlsx file by calling sendPrompt('Generate the subscription spreadsheet for download').
Step 5: Generate the Spreadsheet
When the user clicks "Download as Spreadsheet" or asks for a spreadsheet export, create an .xlsx file using the script at scripts/generate_xlsx.py.
Run the script with the subscription data as JSON input:
echo '<JSON_DATA>' | python3 /path/to/subscription-tracker/scripts/generate_xlsx.py /mnt/user-data/outputs/subscription-renewals.xlsx
The script expects a JSON array of subscription objects piped to stdin and outputs a formatted .xlsx file.
Then present the file to the user with present_files.
Important Notes
- Privacy: All data stays within the conversation. Remind the user that their email content is being read to extract subscription info.
- Accuracy disclaimer: Tell the user that the extracted data is based on what's visible in their emails from the last 30 days, and some subscriptions may not appear if no related emails were received in that window.
- No hallucination: If a data field cannot be confidently extracted from the email, leave it as null/unknown rather than guessing. The artifact should show "—" for missing fields.
- Currency: Detect and preserve the original currency from the email. Don't convert currencies.
- One-time purchases: Some receipts may be for one-time purchases (workshops, courses, lifetime deals) rather than recurring subscriptions. If there's no billing cycle or renewal date, classify these as "one-time" and include them in a separate section at the bottom of the table. They're still useful for the user to see but shouldn't appear as "upcoming" renewals.
- Time sensitivity: When presenting dates, highlight anything due within the next 7 days prominently.