Skip to main content
You can’t fix what you can’t see. The manager writes one row to ai_call per attempt — success, error, or cancelled — including tokens (if the provider reports them), latency, and any error message. That single table powers the admin dashboard at /admin/usage and is all you need to spot a runaway model or a broken provider.

Prerequisites

  • Migrated DB so the ai_call table exists (pnpm db:push).
  • A few real AI calls behind you — the table is empty until then.
  • Read src/ai/manager.ts recordCall() — that’s where every row is born.

The columns that matter

Indexes on userId, (provider, model), status, createdAt cover the queries the dashboard runs.

Why no cost column

We deliberately don’t compute or store dollar cost. Provider pricing is moving target — caching tiers, fine-tune surcharges, agreement discounts — and any local copy drifts within weeks. Vibestrap’s job is to record what happened (calls, tokens, latency, errors); your provider’s billing dashboard is the source of truth for what it cost. If your product does need a cost view (e.g., you bill end-users by provider call), add a cost_micro_cents column back with your own estimation logic. The schema is intentionally trivial to extend.

Why tokens can be NULL

Token-returning APIs (OpenAI / Anthropic / OpenRouter chat) populate inputTokens and outputTokens. Image / video / audio APIs (Replicate / fal.ai) typically do not — those columns stay NULL for those rows. Don’t sum them as “0” silently; treat NULL as “not applicable” in your queries.

Step-by-step: add a usage report

Three useful queries straight against ai_call.

Daily call volume, last 30 days

Error rate per model

Latency p95 per model

Postgres has percentile_cont:

Tying calls to credits

Every successful chat call passes through the manager’s token-based credit reservation. The same ai_call row that records inputTokens / outputTokens is the basis for tokensToCredits() (in src/credits/index.ts). If you change siteConfig.credits.perKToken, historical rows don’t get re-priced — only future calls. For images, the manager charges siteConfig.credits.perImage per generated image, regardless of provider. Override in your own code if you want compute-time-based pricing.