> ## Documentation Index
> Fetch the complete documentation index at: https://docs.myfundingmachine.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Marketplace meters

# 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

| Product | Marketplace productId | Env var | Charge eventId prefix |
| - | - | - | - |
| Credit Reports (Experian pulls, AI Overview, legacy webhook pulls) | existing shared meter | `GHL_MARKETPLACE_METER_ID` | `credit-pull-`, `credit-pull-webhook-`, `ai-overview-` |
| Bureau Report PDFs (TransUnion / Equifax PDF pulls) | `custom_bureau_report_pdfs` | `GHL_MARKETPLACE_BUREAU_PDF_METER_ID` | `bureau-pdf-` |

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.

| Product | Meter ID | Tier ID | Minimum | Default | Maximum |
| - | - | - | - | - | - |
| Credit Reports | `69b84989fb091a6c05bb1576` | `69b84989fb091a7fe5bb157d` | \$0.000001 | \$5 | \$7 |
| Bureau Report PDFs | `6aaaf49df3289a8a5a076817` | `6aaaf49df3289a8a5a076820` | \$0.000001 | \$3 | \$5 |

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.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.