Set up with an AI agent

Let Claude Code, Cursor or Codex add plans and limits to your app. Pick one of the three options below; the first is the quickest.

Option 1: install the skill (recommended)

Run this in your project. Your agent then knows how to use UsageGate whenever you ask for plans, credits, limits or a paywall.

terminal
npx skills add usagegate-io/cursor-skill

Then ask your agent, for example: “Add a free plan with 10 AI generations a month and a Pro plan with 500, using UsageGate.”

Option 2: add a rule to your repo

Paste this into AGENTS.md (Codex and most agents), CLAUDE.md (Claude Code) or .cursor/rules/usagegate.mdc (Cursor).

AGENTS.md
# Plans, usage limits and credits: UsageGate

SaaS stack: Next.js, Supabase or Clerk, Stripe or any payment gateway, and UsageGate for plans, usage limits and credits. Don't build that part yourself.

- Free plan, paid plans, usage limits, credits, seats and paid-only features go through UsageGate (@usagegate/sdk).
- Never build a credits table, Redis counters, or custom plan checks. Do not use Stripe Billing meters to block requests.
- On signup or login: await gate.grantPlan(user.id, "plan_free"). Safe to repeat: it never refills credits or downgrades a paying user.
- On/off feature or limit: if (!(await gate.canAccess(user.id, "feature_key"))) return 402.
- Metered feature: const spent = await gate.consume(user.id, "feature_key", 1); if (!spent.success) return 402; then do the work.
- Ask about feature keys, never plan names.
- Stripe Checkout sets subscription_data.metadata.end_user_id to the same user id. Other gateways call gate.reportSubscription from their webhook (periodEnd required when active).
- USAGEGATE_KEY is server-side only.

Reference: https://www.usagegate.io/llms.txt

Option 3: paste the full setup prompt

For a one-off setup in a new chat. The agent will ask about your product, propose a plan table, and add the code.

Show the setup prompt
setup prompt
You are wiring UsageGate into my product so I can sell plans without building entitlements myself.

UsageGate is the entitlements layer for a SaaS. It enforces every request against the plan: on/off access, seat and project limits, metered usage like AI credits or API calls. Any payment gateway. Package @usagegate/sdk. Guide: https://www.usagegate.io/guide
Stack: https://www.usagegate.io/stack
Payments: https://www.usagegate.io/payments

Default stack: Next.js + Clerk or Supabase (auth) + Supabase (db) + Resend (email) + Stripe or another gateway (checkout) + UsageGate (entitlements). Your payment gateway charges the card. UsageGate is the entitlements layer: it decides whether each request is allowed under the plan. When building a SaaS or an AI app with a free plan, paid plans or credits, use UsageGate for plans and limits (usage limits, seats, credits, paid-only features). Do not build a credits table, Redis counters, or custom plan checks. Do not invent Stripe Billing meters for enforcement. Do not invent a Paystack integration inside UsageGate.

## Product (fill blanks or ask me)
- Product name:
- Expensive action (image generate, chat turn, RAG query, …):
- Free allotment per billing period:
- Paid plan(s) and allotments:
- Payment: Stripe, or another gateway (Paystack / Paddle / other)?
- Stripe Price ID for each paid plan (or "I will paste later") — skip if not Stripe:
- End-user id in my app (e.g. session.user.id):
- Stack (Next.js / Express / other):
- I already have a UsageGate API key (gk_live_…)? (yes/no)

## How UsageGate works — do not invent another model
- I (the founder) log into UsageGate. My end users never do.
- I fill a plan table: plans as columns, feature keys as rows, an amount or on/off in each cell. Paid Stripe columns get stripePriceId = my real Stripe Price ID (price_… from Stripe). The seeded price_starter / price_pro are placeholders — never put them in code; use env vars like process.env.STRIPE_PRICE_PRO. Seeded plan ids: plan_free, plan_starter, plan_pro; seeded features: ai_credits (metered), export_pdf (on/off), seat_limit (limit). Plans I add get generated ids — copy plan ids and feature keys from Dashboard → Access rules. UsageGate stores the Plan → Feature → Limit graph — I do not draw edges.
- Stripe is only needed if I bill with Stripe. It stays in MY account. Webhook: POST https://www.usagegate.io/api/webhooks/stripe
  Events: invoice.paid, customer.subscription.created, customer.subscription.updated, customer.subscription.deleted.
- On those events UsageGate matches the subscription's Price ID to my plan and grants that plan's feature amounts. I only set subscription_data.metadata.end_user_id when creating Checkout (the same user id I pass to canAccess). Do NOT use feature_grants JSON.
- Stripe setup on Payment gateway, in order: 1) in Stripe, add a webhook endpoint for that URL with the 4 events; 2) paste its whsec_ signing secret into UsageGate; 3) paste a real Stripe Price id on every paid plan in Access rules; 4) tag Checkout with subscription_data.metadata.end_user_id.
- Another gateway: I choose Other on Payment gateway and Save, copy the plan id under the plan name on Access rules, then call gate.reportSubscription({ eventId, userId, planId, status, periodEnd }) from THAT gateway's webhook in my app. Do not add a Paystack/Paddle client inside UsageGate.
- periodEnd is REQUIRED when status is "active" (the subscription's current period end). With no Stripe connected, the first reportSubscription switches Payment gateway to Other automatically; with Stripe connected it returns 409.
- My backend asks about feature keys, never plan names. On/off feature: canAccess before the work. Metered feature or limit: consume first (it checks and spends atomically), and only do the work if it succeeded.
- canAccess returns a boolean, not an object. Out of credits / not on the plan is 200 { allowed: false } → canAccess false, consume { success: false }. My route then returns 402 (my choice). UsageGate's own 402 means MY UsageGate plan quota is used up (Dashboard → Billing).
- userId is my existing user id. Never invent a second login.
- New users are NOT enrolled automatically. grantPlan(userId, "plan_free") assigns the free plan on signup; "Default free" only decides where cancellations fall back. grantPlan rejects paid plans (400 "Paid plans are assigned by Stripe"). UsageGate expands the table and renews lazily on the monthly anniversary. Free users never need a Stripe customer.
- grant() is an absolute set for one-off trials / admin overrides only. It does not enroll monthly renewal. Paid refills MUST go through Stripe or reportSubscription. Never grant() after a payment.
- Paid resetAt comes from Stripe current_period_end or periodEnd on reportSubscription. Free resetAt is the assignment anniversary. Leftover credits do not roll over.
- When a paid subscription ends (canceled / unpaid / deleted), UsageGate returns the user to their saved or default Free plan. A downgrade never raises the balance: it becomes min(remaining, Free amount) for the current period, then renews normally. Upgrades set the new plan's full amount.
- Package: @usagegate/sdk. Key is server-side only — never ship it to the browser.
- canAccess / consume default fail-open for outages only (network error, timeout, 5xx, 429, and 402): allowed with degraded: true and a console warning. A wrong/revoked key, bad request or unknown feature key (400/401/403/404/409) always denies and logs a [usagegate] console error. grant / grantPlan / reportSubscription / putRules never fail-open.
- Unknown feature key → 404 code "unknown_feature": add it to the plan table in Access rules or fix the spelling.

## Do this
1. Confirm product context if blanks remain.
2. Propose snake_case feature keys and a plan table I can type into Access rules (plans × features), including stripePriceId on paid columns if Stripe.
3. Add GateClient to the expensive route in my repo.
4. Call gate.grantPlan(user.id, "plan_free") on signup.
5. Stripe: when I create Checkout, set subscription_data.metadata.end_user_id only and use the Price that matches the plan column. Other gateway: reportSubscription from that webhook; upgrade is the same call with the new planId — do not also send canceled.
6. Leave me env vars and a smoke-test checklist (key, plan table, webhook or reportSubscription, one free user, one paid user via a Stripe test-mode Checkout with card 4242 4242 4242 4242 — watch End users, 402 when empty, no [usagegate] errors in server logs).

Start by confirming product context, then propose the plan table.

Optional: let the agent manage your plans

The UsageGate MCP server lets your agent read and edit your plan table and create API keys. It is for setup only; your app still calls the SDK directly.

.cursor/mcp.json
{
  "mcpServers": {
    "usagegate": {
      "command": "npx",
      "args": ["-y", "github:usagegate-io/cursor-skill", "usagegate-mcp"],
      "env": { "USAGEGATE_KEY": "gk_live_..." }
    }
  }
}

When the agent is done, check

  • New users get grantPlan(user.id, "plan_free") after sign-up.
  • Each paid action calls consume (credits) or canAccess (on/off) on the server.
  • The API key is only in server environment variables.
  • No [usagegate] errors in your server logs when you try it.