Skip to content

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:

LayerWhat it provesWhere to verify
Deterministic billing commitmentMeter + tariff + charge pinned to billing_verify_fingerprintAttest/verify JSON; integration tests
Phase-2 in-circuit bindingpublic_commitments bound by Groth16 (apiBillingV2)incircuit_binding_verification on verify
Crash-safe replay protectionSame event_id cannot commit twice per tenant/window after crashesMigration 031; replay tests
Durable persistenceReplay guard + transparency log survive restartsPostgreSQL; pilot integration suite
Transparency surfaceAppend-only log head + inclusion proofsGET /api/vdi/billing/transparency/head
Revocation feedStrict profile can require published revocation snapshot/.well-known/vdi-billing-revocation.json
Customer-side offline verifierCustomer need not trust issuer, dashboard, or your server/protocol/verify.html
Strict-profile verificationManifest authority, revocation, circuit governanceVDI_VERIFY_STRICT_V1; production doc
Trust-anchor handlingExternal meter_envelope + meter_signing_kidVDI_BILLING_METER_TRUST_JSON
Tamper rejectionSignature, fingerprint, binding mismatches fail closedPilot tests + browser tamper demo

Commercial core: the offline verifier supports the QuantZK claim that verification is independent of QuantZK infrastructure.

Alternate flows

FlowWhen to useDoc / surface
Two HTTP calls (primary)Integrator pilot; design partnersThis page (sections below)
External meter envelopeYour meter signs events; API only verifiesVDI_BILLING_METER_TRUST_JSON in production
Browser offline verifierCustomer/dispute team verifies without API trust/protocol/verify.html
Live billing demoSales / design-partner walkthroughquantzk.com/vdi-billing
Production smokePost-deploy attest → verify → tamperscripts/smoke-billing-phase2.sh; smoke evidence
Strict profile + auditorsEnterprise procurementAPI 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):

bash
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-pilot

Full 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

  1. Repository: clone this repo and ensure protocol/ and zk-cp/build/ are present (default layout).

  2. Meter key (Ed25519): from the repo root run:

    bash
    ./scripts/generate-billing-meter-key.sh

    Copy VDI_BILLING_METER_PRIVATE_KEY_PEM and VDI_BILLING_METER_KID into verifier-api/.env.

  3. Verifier API: in verifier-api/.env set at minimum:

    • PORT=3001
    • NODE_ENV=development
    • DEMO_MODE=true (or VDI_ATTEST_ENABLED=true plus VDI_ATTEST_SECRET and header on attest routes in production)
    • VDI_SIGNING_KEY — 64+ hex chars (no 0x prefix)
    • VDI_BILLING_METER_PRIVATE_KEY_PEM and VDI_BILLING_METER_KID from the script output
  4. Start the API:

    bash
    cd verifier-api && node src/app.js

API request 1 — attest

POST /api/vdi/billing/attest

bash
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-dollars
  • billing.billing_verify_fingerprint — pinned fields for reconciliation
  • attestation — VDI decision attestation
  • verification — verifier output from issuance-time check
  • receipt — 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.

bash
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>
}
EOF

In 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:

bash
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)

SymptomLikely causeWhat to do
400 on attest, meter keyMissing 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 ReplaySame event_id + tenant_id + billing_window_id already committedNew event_id (ULID); or wait for expired reservation reclaim (crash-safe path)
billing_check.valid: falseexpected_billing_v2 ≠ attested fingerprintDispute signal — do not settle; investigate tariff/meter drift
verification.valid: falseAttestation, receipt, or profile inputs invalidHard failure; do not treat as billed
incircuit_binding.valid: falseTampered commitments or wrong circuit artifactsRe-attest; confirm VDI_BILLING_INCIRCUIT_BINDING artifacts match deploy
Integration tests cannot connect DBRemote DATABASE_URL from secrets, or Postgres downOverride local DATABASE_URL (see Integration tests above); start Docker postgres
Tests pass locally, fail in CINo PostgreSQL service in CI jobProvision 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:

typescript
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).

Verification keys are embedded in attestations. The verifier is open source.