Skip to main content

Marketplace billing meters: Credit Reports vs Bureau Report PDFs

Internal reference for the two GHL marketplace meters this app charges against. Not a Mintlify page. Operator-facing behavior (price, wallet gate, free cached re-downloads) is unchanged by which meter a charge lands on.

The pieces

Both env vars must be set wherever charge bodies are built:
  • Vercel (Next.js): lib/billing.ts builds the pre-provider secured charge for app/api/credit-report/[requestId]/bureau-pdf/route.ts and the approved-collections replay in app/api/admin/collect-approved-credit-charges/route.ts.
  • Convex deployment: convex/billingRetryQueueNode.ts builds the fallback charge body for retry-queue rows that stored no meter.
GHL_MARKETPLACE_APP_ID stays the single app id for both meters.

How the meter is chosen

lib/billing/bureauPdfMeter.ts is the only place that maps a charge to a meter. The eventId already names the billable product, so marketplaceMeterIdFor(eventId) returns the PDF meter for bureau-pdf-* events and the Credit Reports meter for everything else. There is no other routing, no per-location override, and no pricing logic in that module. Rules the tests pin (tests/bureauPdfMeter.test.ts):
  • New PDF charges → PDF meter. Secured charge and approved-collections replay both send meterId = GHL_MARKETPLACE_BUREAU_PDF_METER_ID for bureau-pdf-* keys.
  • Fail closed, never fall back. A bureau-pdf-* charge with the PDF meter unset returns billing_unconfigured (secured path) or Billing not configured (replay path). It never borrows the Credit Reports meter. Credit pulls are unaffected by a missing PDF meter, and $0 complimentary PDFs bypass billing without needing any meter.
  • Historical rows keep their meter. A billingRetryQueue row stores the full charge body it was enqueued with, including meterId. The retry drain replays a stored meter as-is; only a row with no stored meter resolves one by product.
  • Refunds and reconciliation need no meter. releaseSecuredCharge reverses by chargeId (DELETE /marketplace/billing/charges/{chargeId}), and approved-collections claim releases likewise act on chargeId. Whatever meter a charge was created under, its reversal targets that same charge.

Production rollout note

Deploy order matters: set GHL_MARKETPLACE_BUREAU_PDF_METER_ID on Vercel and the Convex production deployment before shipping code that reads it, or bureau PDF pulls refuse with a billing-unconfigured error until it lands. Production env writes are a human step under AGENTS.md Safety. Do not create a second meter or change pricing on either meter from code; both live in the marketplace app config.

CLI-compatible meter configuration (2026-10-06)

Brock authorized correcting the existing Marketplace app 697a568ce94ccaee772a84b0 billing configuration in the Codex conversation after reviewing the three-field repair. The official Marketplace CLI 1.0.0 API client updated the two existing tiers; it did not create meters or change subscriptions. Both pricing-page URLs are https://myfundingmachine.com. The Credit Reports URL previously lacked the HTTPS scheme; both minimums were previously zero. The CLI requires unit-price bounds of 0.000001–200. Defaults, maxima, tier names, IDs, volume ranges, execution limits, and the free subscription were preserved. Complimentary work remains free: chargeForCreditPull, chargeForAiOverview, and secureChargeBeforeProviderCall bypass Marketplace charging at a zero price. Credit-pull retry enqueueing occurs after that bypass; the legacy webhook and credit-pull retry drain send $5. Approved collections use chargeForCreditPull. No application code change was needed for this configuration repair. Exact API readback matched the prepared configuration, and the full ghl app pull --with-types now succeeds. Use the official pinned CLI in an isolated workspace, then ghl app billing validate and ghl app billing diff to check future billing changes. Never use an action test to verify billing: it can execute a real endpoint and trigger a billable pull. The public site’s Pricing page currently omits credit-report and bureau-PDF rates. Adding that disclosure is separate website work; this repair only normalized the existing URL and minimums.