Pilot: Verifiable API billing
Primary integration path: two HTTP calls against the QuantZK verifier API (POST /api/vdi/billing/attest, then POST /api/vdi/billing/verify). Alternate flows (external meter signing, offline verifier, production smoke, dashboard) are listed below.
Pilot-grade infrastructure surface
What makes api-billing-v2 past “demo infrastructure” and into serious pilot territory is the full trust chain, not Phase-2 binding alone:
| Layer | What it proves | Where to verify |
|---|---|---|
| Deterministic billing commitment | Meter + tariff + charge pinned to billing_verify_fingerprint | Attest/verify JSON; integration tests |
| Phase-2 in-circuit binding | public_commitments bound by Groth16 (apiBillingV2) | incircuit_binding_verification on verify |
| Crash-safe replay protection | Same event_id cannot commit twice per tenant/window after crashes | Migration 031; replay tests |
| Durable persistence | Replay guard + transparency log survive restarts | PostgreSQL; pilot integration suite |
| Transparency surface | Append-only log head + inclusion proofs | GET /api/vdi/billing/transparency/head |
| Revocation feed | Strict profile can require published revocation snapshot | /.well-known/vdi-billing-revocation.json |
| Customer-side offline verifier | Customer need not trust issuer, dashboard, or your server | /protocol/verify.html |
| Strict-profile verification | Manifest authority, revocation, circuit governance | VDI_VERIFY_STRICT_V1; production doc |
| Trust-anchor handling | External meter_envelope + meter_signing_kid | VDI_BILLING_METER_TRUST_JSON |
| Tamper rejection | Signature, fingerprint, binding mismatches fail closed | Pilot tests + browser tamper demo |
Commercial core: the offline verifier supports the QuantZK claim that verification is independent of QuantZK infrastructure.
Alternate flows
| Flow | When to use | Doc / surface |
|---|---|---|
| Two HTTP calls (primary) | Integrator pilot; design partners | This page (sections below) |
| External meter envelope | Your meter signs events; API only verifies | VDI_BILLING_METER_TRUST_JSON in production |
| Browser offline verifier | Customer/dispute team verifies without API trust | /protocol/verify.html |
| Live billing demo | Sales / design-partner walkthrough | quantzk.com/vdi-billing |
| Production smoke | Post-deploy attest → verify → tamper | scripts/smoke-billing-phase2.sh; smoke evidence |
| Strict profile + auditors | Enterprise procurement | API billing production |
Integration tests (15/15 — 14 functional + 1 perf budget)
Requires local PostgreSQL. Canonical command (same in AGENTS.md, production doc, Phase-2 deploy):
cd verifier-api
DATABASE_URL="postgresql://zkcaptcha:development@localhost:5432/zkcaptcha" \
REDIS_ENABLED="false" \
REDIS_DISABLED="true" \
NODE_ENV=test \
DB_SSL=false \
npm run test:billing-pilotFull setup (Docker vs apt, extended troubleshooting): docs/dev/verifier-api-test-setup.md.
Troubleshooting: If Railway or Cursor inject a remote DATABASE_URL, override with the local URL above for test runs. When DATABASE_URL is set, it takes precedence over DB_PORT in verifier-api/src/config/database.js.
What this is
You send a metered usage event and tariff line item. The API returns a deterministic charge, a VDI attestation, a cryptographic verification result, and a signed receipt. You can independently re-verify the attestation, receipt, and pinned charge fingerprint.
What it does not replace
- It does not replace your Stripe (or other PSP) charge capture or dunning.
- It does not replace contract negotiation or legal terms.
- It is an integrity and reconciliation layer: a verifiable artifact for a single usage-based line item.
Setup in about 10 minutes
Repository: clone this repo and ensure
protocol/andzk-cp/build/are present (default layout).Meter key (Ed25519): from the repo root run:
bash./scripts/generate-billing-meter-key.shCopy
VDI_BILLING_METER_PRIVATE_KEY_PEMandVDI_BILLING_METER_KIDintoverifier-api/.env.Verifier API: in
verifier-api/.envset at minimum:PORT=3001NODE_ENV=developmentDEMO_MODE=true(orVDI_ATTEST_ENABLED=trueplusVDI_ATTEST_SECRETand header on attest routes in production)VDI_SIGNING_KEY— 64+ hex chars (no0xprefix)VDI_BILLING_METER_PRIVATE_KEY_PEMandVDI_BILLING_METER_KIDfrom the script output
Start the API:
bashcd verifier-api && node src/app.js
API request 1 — attest
POST /api/vdi/billing/attest
curl -sS -X POST "http://localhost:3001/api/vdi/billing/attest" \
-H "Content-Type: application/json" \
-d '{
"billing_version": "v2",
"profileId": "VDI_VERIFY_STANDARD_V1",
"meter_event": {
"event_id": "evt_pilot_001",
"event_timestamp": "2026-05-12T12:00:00.000Z",
"tenant_id": "tenant_acme",
"api_key_id": "api_live_001",
"endpoint": "/v1/completions",
"usage_units": 1842,
"contract_version": "contract-v1",
"billing_window_id": "2026-05"
},
"tariff_snapshot": {
"tariff_id": "tariff_default",
"tariff_version": "1.0.0",
"tariff_class": "usage_linear",
"unit_price_micros": 120,
"min_charge_micros": 50000,
"max_charge_micros": 5000000000,
"rounding_mode": "floor",
"currency": "USD"
}
}' | jq .Expected response (shape)
The JSON body includes at least:
billing.expected_charge_micros— integer micro-dollarsbilling.billing_verify_fingerprint— pinned fields for reconciliationattestation— VDI decision attestationverification— verifier output from issuance-time checkreceipt— signed verification receipt
Save the full JSON for step 2.
API request 2 — verify
POST /api/vdi/billing/verify
Re-checks the attestation and receipt, then compares billed vs expected pinned fields via billing_verify_fingerprint (same role as a line-item arithmetic check for this pilot).
Use the same billing.billing_verify_fingerprint for both billed_billing_v2 and expected_billing_v2 when you are checking that nothing drifted since issuance.
curl -sS -X POST "http://localhost:3001/api/vdi/billing/verify" \
-H "Content-Type: application/json" \
-d @- <<'EOF' | jq .
{
"profileId": "VDI_VERIFY_STANDARD_V1",
"attestation": <paste attestation from step 1>,
"receipt": <paste receipt from step 1>,
"billed_billing_v2": <paste billing.billing_verify_fingerprint from step 1>,
"expected_billing_v2": <paste billing.billing_verify_fingerprint from step 1>
}
EOFIn practice, substitute the three pasted values from the attest response (or reference the full billing object; the server accepts the nested fingerprint).
Verification command (minimal)
After saving attest.json from step 1:
jq -n \
--slurpfile a attest.json \
'{
profileId: "VDI_VERIFY_STANDARD_V1",
attestation: $a[0].attestation,
receipt: $a[0].receipt,
billed_billing_v2: $a[0].billing.billing_verify_fingerprint,
expected_billing_v2: $a[0].billing.billing_verify_fingerprint
}' \
| curl -sS -X POST "http://localhost:3001/api/vdi/billing/verify" \
-H "Content-Type: application/json" \
-d @- | jq .Expect verification.valid: true, receipt_verification.valid: true (when a receipt is present), and billing_check.valid: true.
Failure modes and troubleshooting (HTTP pilot)
| Symptom | Likely cause | What to do |
|---|---|---|
400 on attest, meter key | Missing VDI_BILLING_METER_PRIVATE_KEY_PEM (this doc uses server-side signing) | Run ./scripts/generate-billing-meter-key.sh; copy PEM + VDI_BILLING_METER_KID into verifier-api/.env |
400 Replay | Same event_id + tenant_id + billing_window_id already committed | New event_id (ULID); or wait for expired reservation reclaim (crash-safe path) |
billing_check.valid: false | expected_billing_v2 ≠ attested fingerprint | Dispute signal — do not settle; investigate tariff/meter drift |
verification.valid: false | Attestation, receipt, or profile inputs invalid | Hard failure; do not treat as billed |
incircuit_binding.valid: false | Tampered commitments or wrong circuit artifacts | Re-attest; confirm VDI_BILLING_INCIRCUIT_BINDING artifacts match deploy |
| Integration tests cannot connect DB | Remote DATABASE_URL from secrets, or Postgres down | Override local DATABASE_URL (see Integration tests above); start Docker postgres |
| Tests pass locally, fail in CI | No PostgreSQL service in CI job | Provision Postgres or use the canonical env block with a reachable host |
Pilot success metrics
- Fewer billing questions: disputes reference one JSON bundle (attestation + receipt + fingerprint).
- Clearer reconciliation: charge is pinned to meter fields + tariff snapshot digests.
- Enterprise trust signal: receipts are verifier-signed and independently re-checkable.
Minimal SDK (browser or Node)
Configure once, then call attest and verify:
import { quantzk } from '../lib/quantzkBillingPilot';
quantzk.billing.configure({ baseUrl: 'http://localhost:3001' });
const bundle = await quantzk.billing.attest({
tenantId: 'tenant_acme',
eventId: 'evt_pilot_001',
usageUnits: 1842,
unitPriceMicros: 120,
billingWindowId: '2026-05',
});
const check = await quantzk.billing.verify(bundle);See src/lib/quantzkBillingPilot.ts in this repo.
Demo UI
The VDI Billing Dashboard includes a Verified billing badge after a successful run (charge, verified state, receipt drill-down).
