flowglad-usage-tracking

0
0
Source

Implement usage-based billing with Flowglad including recording usage events, checking balances, and displaying usage information. Use this skill when adding metered billing, tracking API calls, or implementing consumption-based pricing.

Install

mkdir -p .claude/skills/flowglad-usage-tracking && curl -L -o skill.zip "https://mcp.directory/api/skills/download/6850" && unzip -o skill.zip -d .claude/skills/flowglad-usage-tracking && rm skill.zip

Installs to .claude/skills/flowglad-usage-tracking

About this skill

<!-- @flowglad/skill sources_reviewed: 2026-01-21T12:00:00Z source_files: - platform/docs/features/usage.mdx - platform/docs/sdks/feature-access-usage.mdx -->

Usage Tracking

Abstract

This skill covers implementing usage-based billing with Flowglad, including recording usage events for metered billing, checking usage balances, and displaying usage information to users. Proper implementation ensures accurate billing and prevents users from bypassing usage charges.


Table of Contents

  1. Recording Usage EventsCRITICAL
  2. Usage Meter ResolutionHIGH
  3. Idempotency with transactionIdHIGH
  4. Pre-Check Balance Before Expensive OperationsMEDIUM
  5. Display Patterns for UsageMEDIUM
  6. Handling Exhausted BalanceMEDIUM

1. Recording Usage Events

Impact: CRITICAL

Flowglad supports recording usage events from both client-side and server-side code. Each approach has different APIs and trade-offs.

1.1 Client-Side Recording

Impact: CRITICAL (simplest approach for many use cases)

Use useBilling().createUsageEvent for client-side usage tracking. The client SDK provides smart defaults that simplify implementation.

Client-side smart defaults:

  • amount defaults to 1
  • transactionId is auto-generated for idempotency
  • subscriptionId is auto-inferred from current subscription

Basic client-side usage:

'use client'

import { useBilling } from '@flowglad/nextjs'

function RecordUsageButton({ usageMeterSlug }: { usageMeterSlug: string }) {
  const billing = useBilling()

  const handleClick = async () => {
    if (!billing.createUsageEvent) return

    const result = await billing.createUsageEvent({
      usageMeterSlug,
      // amount defaults to 1
      // transactionId auto-generated
      // subscriptionId auto-inferred
    })

    if ('error' in result) {
      console.error('Failed to record usage:', result.error)
      return
    }

    console.log('Usage recorded:', result.usageEvent.id)
  }

  return <button onClick={handleClick}>Use Feature</button>
}

With explicit values:

const result = await billing.createUsageEvent({
  usageMeterSlug: 'api-calls',
  amount: 5, // Override default of 1
  // transactionId and subscriptionId still auto-handled
})

Important: Client-side usage events do not automatically refresh billing data. Call billing.reload() after recording if you need to update displayed balances.

await billing.createUsageEvent({ usageMeterSlug: 'generations' })
await billing.reload() // Refresh to show updated balance

1.2 Server-Side Recording

Impact: CRITICAL (required for atomic operations)

Use flowglad(userId).createUsageEvent for server-side usage tracking. Server-side requires explicit values for all parameters.

Server-side required parameters:

  • subscriptionId - must be provided explicitly
  • transactionId - must be provided explicitly
  • amount - must be provided explicitly

Basic server-side usage:

// API route - app/api/generate/route.ts
import { flowglad } from '@/lib/flowglad'
import { auth } from '@/lib/auth'

export async function POST(req: Request) {
  const session = await auth()
  if (!session?.user?.id) {
    return Response.json({ error: 'Unauthorized' }, { status: 401 })
  }

  // Get billing to find subscriptionId
  const billing = await flowglad(session.user.id).getBilling()
  const subscriptionId = billing.currentSubscription?.id

  if (!subscriptionId) {
    return Response.json({ error: 'No active subscription' }, { status: 402 })
  }

  // Perform the operation
  const result = await generateContent()

  // Record usage with explicit parameters
  await flowglad(session.user.id).createUsageEvent({
    usageMeterSlug: 'generations',
    amount: 1,
    subscriptionId,
    transactionId: `gen_${result.id}`,
  })

  return Response.json(result)
}

1.3 Choosing Client vs Server

Impact: CRITICAL (architectural decision)

Both approaches are valid. Choose based on your needs:

Use CaseRecommended ApproachWhy
Simple button click trackingClient-sideSmart defaults make it easy
Feature usage countersClient-sideNo server round-trip needed
AI generation / expensive operationsServer-sideTrack atomically with operation
API endpoint meteringServer-sideAlready on server
Operations that cost you moneyServer-sideEnsure tracking happens
Quick prototypingClient-sideFewer files to create

Pattern: Atomic server-side tracking

When an operation costs you money (e.g., calling OpenAI), track usage atomically with the operation:

export async function POST(req: Request) {
  const session = await auth()
  const billing = await flowglad(session.user.id).getBilling()

  // 1. Check balance first
  const balance = billing.checkUsageBalance('generations')
  if (!balance || balance.availableBalance <= 0) {
    return Response.json({ error: 'No credits' }, { status: 402 })
  }

  // 2. Perform expensive operation
  const result = await openai.images.generate({ prompt })

  // 3. Record usage (atomic with operation)
  await flowglad(session.user.id).createUsageEvent({
    usageMeterSlug: 'generations',
    amount: 1,
    subscriptionId: billing.currentSubscription!.id,
    transactionId: `gen_${result.data[0].url}`,
  })

  return Response.json(result)
}

Pattern: Simple client-side tracking

For tracking feature usage where bypassing isn't a concern:

function FeatureButton() {
  const billing = useBilling()

  const handleUse = async () => {
    // Track usage - smart defaults handle the rest
    await billing.createUsageEvent({ usageMeterSlug: 'feature-uses' })
    await billing.reload()
    // Do the feature thing
  }

  return <button onClick={handleUse}>Use Feature</button>
}

2. Usage Meter Resolution

Impact: HIGH

When creating usage events, you can identify the usage price by slug or ID. Understanding how these resolve helps you structure your billing correctly.

2.1 Using usageMeterSlug vs priceSlug

Impact: HIGH (determines which price is charged)

You can identify usage with exactly one of:

  • priceSlug or priceId - targets a specific price directly
  • usageMeterSlug or usageMeterId - resolves to the meter's default price

Using priceSlug (explicit):

await createUsageEvent({
  priceSlug: 'api-calls-standard', // Specific price
  amount: 1,
  // ...
})

Using usageMeterSlug (resolves to default):

await createUsageEvent({
  usageMeterSlug: 'api-calls', // Resolves to meter's default price
  amount: 1,
  // ...
})

When using usageMeterSlug, the system uses the meter's configured default price. If no custom default is set, it uses the auto-generated no-charge price.

2.2 Default No-Charge Prices

Impact: HIGH (understanding automatic pricing)

Every usage meter automatically has a no-charge price with:

  • Slug pattern: {usagemeterslug}_no_charge
  • Unit price: $0.00 (always free)
  • Cannot be archived or deleted

This means you can start tracking usage immediately without creating a price first:

// Works even if no custom price exists for 'api-calls' meter
await createUsageEvent({
  usageMeterSlug: 'api-calls',
  amount: 1,
  // Resolves to 'api-calls_no_charge' if no other default is set
})

When you need paid usage:

  1. Create a usage price in your Flowglad dashboard (e.g., api-calls-standard at $0.001/call)
  2. Set it as the default price for the meter, OR
  3. Reference it directly with priceSlug: 'api-calls-standard'

Checking which price was used:

The response from createUsageEvent always includes the resolved priceId:

const result = await createUsageEvent({ usageMeterSlug: 'api-calls', amount: 1 })

if (!('error' in result)) {
  console.log('Charged to price:', result.usageEvent.priceId)
}

3. Idempotency with transactionId

Impact: HIGH

Network failures and retries can cause duplicate usage events. Always include a transactionId to ensure each logical operation is only billed once.

3.1 Preventing Double-Charging

Impact: HIGH (prevents billing disputes and customer trust issues)

Without idempotency, a network timeout followed by a retry could charge the user twice for the same operation.

Incorrect: no idempotency key

// API route
export async function POST(req: Request) {
  const session = await auth()
  const result = await generateImage(prompt)

  // If this request times out and retries, user gets double-charged!
  await flowglad(session.user.id).createUsageEvent({
    usageMeterSlug: 'image-generations',
    amount: 1,
  })

  return Response.json(result)
}

**Correct: always include transacti


Content truncated.

You might also like

flutter-development

aj-geddes

Build beautiful cross-platform mobile apps with Flutter and Dart. Covers widgets, state management with Provider/BLoC, navigation, API integration, and material design.

643969

drawio-diagrams-enhanced

jgtolentino

Create professional draw.io (diagrams.net) diagrams in XML format (.drawio files) with integrated PMP/PMBOK methodologies, extensive visual asset libraries, and industry-standard professional templates. Use this skill when users ask to create flowcharts, swimlane diagrams, cross-functional flowcharts, org charts, network diagrams, UML diagrams, BPMN, project management diagrams (WBS, Gantt, PERT, RACI), risk matrices, stakeholder maps, or any other visual diagram in draw.io format. This skill includes access to custom shape libraries for icons, clipart, and professional symbols.

591705

ui-ux-pro-max

nextlevelbuilder

"UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 8 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient."

318398

godot

bfollington

This skill should be used when working on Godot Engine projects. It provides specialized knowledge of Godot's file formats (.gd, .tscn, .tres), architecture patterns (component-based, signal-driven, resource-based), common pitfalls, validation tools, code templates, and CLI workflows. The `godot` command is available for running the game, validating scripts, importing resources, and exporting builds. Use this skill for tasks involving Godot game development, debugging scene/resource files, implementing game systems, or creating new Godot components.

339397

nano-banana-pro

garg-aayush

Generate and edit images using Google's Nano Banana Pro (Gemini 3 Pro Image) API. Use when the user asks to generate, create, edit, modify, change, alter, or update images. Also use when user references an existing image file and asks to modify it in any way (e.g., "modify this image", "change the background", "replace X with Y"). Supports both text-to-image generation and image-to-image editing with configurable resolution (1K default, 2K, or 4K for high resolution). DO NOT read the image file first - use this skill directly with the --input-image parameter.

451339

fastapi-templates

wshobson

Create production-ready FastAPI projects with async patterns, dependency injection, and comprehensive error handling. Use when building new FastAPI applications or setting up backend API projects.

304231

Stay ahead of the MCP ecosystem

Get weekly updates on new skills and servers.