Skip to content

Credit Pricing System

Overview

Credits are the billing unit for PDF conversions. 1 credit = 1 page for every conversion path β€” standard, high-fidelity, hybrid form, and premium form. The per-page rate is configurable system-wide via system_settings.credits_per_page (default 1) and applies uniformly regardless of page content type.

This replaces the previous tiered model (text=1, image=2, table=2, dense-table=3, mixed=3, plus form/high-fidelity multipliers). The high-fidelity 2x multiplier introduced in #940 was retired 2026-06-12 (HIGH_FIDELITY_CREDIT_MULTIPLIER = 1 in workers/api/src/services/system-settings.ts); between those dates high-fidelity pages billed at 2x.

Configuring the Rate

Admins change the rate at Admin β†’ Tools β†’ Settings β†’ Credits per page. The setting writes system_settings.credits_per_page and audits the change to admin_audit_log. Backend workers read the value through getCreditsPerPage() in workers/api/src/services/system-settings.ts, which caches the value in-memory for ~5 seconds β€” so a rate change propagates across all warm Lambda containers and CF Worker isolates within that window.

Credit Deduction Points

Credits are deducted after successful conversion, not before. Every site reads creditsPerPage from system_settings:

PathWhere DeductedFile
Pre-flight balance checkBefore queueing the jobworkers/api/src/routes/convert.ts (snapshot once per request)
Synchronous (struct-table, Mathpix, Marker)Inline after conversion completesworkers/api/src/routes/convert.ts
Chunked async (agentic vision)After chunk assembly completesworkers/api/src/scheduler/chunk-scheduler.ts
Hybrid form uploadAt upload timeworkers/api/src/routes/forms.ts
Premium form conversionAfter completionworkers/api/src/routes/convert.ts (premium-form handler)
Large-PDF batchAt parent-job creationworkers/batch/src/convert-executor.ts

The per-request snapshot pattern ensures a mid-request admin PUT can’t let the pre-flight pass at one rate while the deduction charges another.

Failure Modes

  • DB unreachable / row missing at billing-hot-path sites: safeGetCreditsPerPage() logs a structured error to app_logs and falls back to the default (1) so the conversion does not abort. The fallback is logged so silent under/overcharging is visible to operators.
  • DB unreachable at batch executor: the throwing variant getCreditsPerPage() is used; the job is marked failed and SQS will redeliver. Better to fail a batch job than to silently undercharge a large PDF.
  • Invalid value in DB (negative, non-integer, garbage): coerced to default (1) with a warn log.

Page Classification (UX-only)

Page classification (classifyDocumentPages() in pdf-complexity-detector.ts) is still run pre-conversion and still drives the CreditEstimatePanel breakdown β€” users see how many pages of each content type they’re converting β€” but every row uses the same per-page rate. Classification no longer affects billing.

User-Facing UI

  • Settings β†’ Billing: shows the current rate and confirms β€œ1 credit = 1 page” applies to all conversion paths.
  • Dashboard β€œAuto-convert on upload” toggle: when off, files show a credit estimate before conversion. The estimate now equals pageCount Γ— creditsPerPage regardless of content mix.
  • Admin β†’ Tools β†’ Settings: the only place to change the rate.

Key Files

FilePurpose
supabase/migrations/20260530_133_system_settings.sqlsystem_settings table + seed
workers/api/src/services/system-settings.tsCached getCreditsPerPage / safeGetCreditsPerPage / setSetting
workers/api/src/services/credit-estimator.tscomputeCreditsFromClassification, estimateCredits (flat-rate)
workers/api/src/routes/admin.tsGET/PUT /api/admin/settings
workers/api/src/routes/convert.tsPer-request snapshot + every deduction site
workers/api/src/routes/forms.tsHybrid form upload deduction
workers/api/src/scheduler/chunk-scheduler.tsChunked job deduction (uses _creditsToDeduct from job options; fallback re-reads current rate)
workers/batch/src/convert-executor.tsLarge-PDF pre-computed deduction
apps/web/src/app/admin/settings/page.tsxAdmin UI for the rate
apps/web/src/app/settings/page.tsxUser-facing pricing copy
apps/web/src/components/dashboard/control-center/CreditEstimatePanel.tsxPer-page breakdown UI