Skip to content

ZKProva Integration Guide

API reference for credit union partners integrating ZKP-powered identity verification.

Base URL: https://api.zkprova.com/api/v1

Authentication

ZKProva uses two auth methods:

Method Header Used by
JWT Bearer Authorization: Bearer <token> Members (register/login flow)
API Key X-API-Key: <key> Lenders (programmatic access)

Register as a Lender

curl -X POST https://api.zkprova.com/api/v1/auth/lender/register \
  -H "Content-Type: application/json" \
  -d '{
    "email": "integrations@lender.com",
    "password": "securePassword123!",
    "organization_name": "Acme Lending",
    "verifier_type": "lender"
  }'

Response includes a JWT token and an API key:

{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "bearer",
  "api_key": "zkp_live_abc123..."
}

Obtain a JWT (Login)

curl -X POST https://api.zkprova.com/api/v1/auth/lender/login \
  -H "Content-Type: application/json" \
  -d '{"email": "integrations@lender.com", "password": "securePassword123!"}'

Rotate API Key

curl -X POST https://api.zkprova.com/api/v1/auth/lender/rotate-key \
  -H "Authorization: Bearer <jwt>"

Returns a new api_key. The previous key is immediately invalidated.


Lender Verification Flow

This is the primary integration path. A member presents a QR code; your system verifies it.

Step 1: Resolve QR Token

Extract the member's DID and bundled claims from a QR token.

curl:

curl -X POST https://api.zkprova.com/api/v1/verify/token \
  -H "X-API-Key: zkp_live_abc123" \
  -H "Content-Type: application/json" \
  -d '{"token": "qr_abc123def456"}'

SDK:

const result = await client.resolveToken('qr_abc123def456');
console.log(result.member_did);  // "did:key:z6Mk..."
console.log(result.claims);      // [{claim_type: "income_range", threshold: 50000, ...}]

Response:

{
  "member_did": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
  "claims": [
    {
      "claim_type": "income_range",
      "threshold": 50000,
      "public_signals": ["1", "0"]
    }
  ],
  "token_valid": true
}

Step 2: Verify ZKP Proofs

Run full cryptographic verification — ZKP proof check, issuer auth, revocation status, holder binding.

curl:

curl -X POST https://api.zkprova.com/api/v1/verify/proof \
  -H "X-API-Key: zkp_live_abc123" \
  -H "Content-Type: application/json" \
  -d '{"token": "qr_abc123def456"}'

SDK:

const proof = await client.verifyProof('qr_abc123def456');

if (proof.all_verified) {
  console.log('All proofs valid');
  console.log(proof.auto_fill);  // Pre-fill your loan application
} else {
  console.log(`Failed at step: ${proof.failed_step}`);
}

Response (success):

{
  "member_did": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
  "claims_verified": {"income_range": true, "membership_status": true},
  "all_verified": true,
  "verification_id": "550e8400-e29b-41d4-a716-446655440000",
  "steps": [
    {"step": "DECODE", "label": "Decode credential", "passed": true, "detail": "W3C VC decoded"},
    {"step": "FRESHNESS", "label": "Check expiry", "passed": true, "detail": "Credential valid"},
    {"step": "ISSUER_AUTH", "label": "Verify issuer", "passed": true, "detail": "Issuer signature valid"},
    {"step": "ZKP_VERIFY", "label": "ZKP proof", "passed": true, "detail": "Groth16 proof verified"},
    {"step": "REVOCATION", "label": "Revocation check", "passed": true, "detail": "Not revoked"},
    {"step": "HOLDER_BINDING", "label": "Holder binding", "passed": true, "detail": "DID matches"}
  ],
  "issuer_info": {
    "did": "did:key:z6MkIssuer...",
    "name": "Sample Credit Union",
    "ncua_charter": "12345",
    "trusted": true
  },
  "auto_fill": [
    {"field": "income_range", "value": "Above $50,000", "claim_type": "income_range"},
    {"field": "membership_status", "value": "Active member", "claim_type": "membership_status"}
  ]
}

Step 3: Check Trust Registry

Verify the issuing credit union is in the trust registry.

curl:

curl https://api.zkprova.com/api/v1/trust-registry \
  -H "X-API-Key: zkp_live_abc123"

SDK:

const registry = await client.getTrustRegistry();
const issuer = registry.find(r => r.did === proof.issuer_info?.did);
console.log(issuer?.status);  // "active"

Response:

[
  {
    "id": "550e8400-...",
    "did": "did:key:z6MkIssuer...",
    "name": "Sample Credit Union",
    "ncua_charter": "12345",
    "public_key_hex": "04abcdef...",
    "status": "active",
    "created_at": "2026-01-15T12:00:00"
  }
]

Step 4: View Verification History

Retrieve past verifications for audit/compliance.

curl:

curl "https://api.zkprova.com/api/v1/verify/history?limit=20&offset=0" \
  -H "X-API-Key: zkp_live_abc123"

SDK:

const history = await client.getVerificationHistory({ limit: 20, offset: 0 });
console.log(`Total verifications: ${history.items.length}`);

Response:

{
  "items": [
    {
      "id": "550e8400-...",
      "member_did": "did:key:z6Mk...",
      "claims_verified": {"income_range": true},
      "all_verified": true,
      "issuer_info": {"did": "did:key:z6MkIssuer...", "name": "Sample CU", "ncua_charter": "12345", "trusted": true},
      "verified_at": "2026-02-28T14:30:00"
    }
  ],
  "total": 142,
  "limit": 20,
  "offset": 0
}


Member Credential Flow

For credit union apps issuing credentials to their members.

1. Register

curl -X POST https://api.zkprova.com/api/v1/auth/member/register \
  -H "Content-Type: application/json" \
  -d '{
    "email": "member@example.com",
    "password": "securePassword123!",
    "full_name": "Jane Doe"
  }'

2. Verify Email

curl -X POST https://api.zkprova.com/api/v1/auth/email/verify \
  -H "Content-Type: application/json" \
  -d '{"code": "123456"}'

3. Issue Credential

curl -X POST https://api.zkprova.com/api/v1/credentials/issue \
  -H "Authorization: Bearer <member_jwt>" \
  -H "Content-Type: application/json" \
  -d '{"claim_type": "income_range", "threshold": 50000}'

Supported claim_type values: income_range, credit_tier, account_age, membership_status, employment_verification, dti_ratio.

For async issuance (recommended for production), add ?async=true:

curl -X POST "https://api.zkprova.com/api/v1/credentials/issue?async=true" \
  -H "Authorization: Bearer <member_jwt>" \
  -H "Content-Type: application/json" \
  -d '{"claim_type": "income_range", "threshold": 50000}'

Returns 202 Accepted with a job_id to poll:

curl https://api.zkprova.com/api/v1/credentials/issue/<job_id> \
  -H "Authorization: Bearer <member_jwt>"

4. Generate QR Code

curl -X POST https://api.zkprova.com/api/v1/qr/generate \
  -H "Authorization: Bearer <member_jwt>" \
  -H "Content-Type: application/json" \
  -d '{"credential_ids": ["<credential_id>"]}'

Returns a token that the member presents to a lender.


Two-Factor Authentication

Enable TOTP

curl -X POST https://api.zkprova.com/api/v1/auth/2fa/enable \
  -H "Authorization: Bearer <jwt>"

Returns a totp_uri for authenticator apps and recovery_codes.

Verify Login with TOTP

If 2FA is enabled, login returns requires_2fa: true. Complete with:

curl -X POST https://api.zkprova.com/api/v1/auth/2fa/verify-login \
  -H "Content-Type: application/json" \
  -d '{"email": "user@example.com", "totp_code": "123456"}'

Webhook Setup

Receive real-time notifications for verification events. See Webhook Event Reference for full details.

Register a Webhook

curl -X POST https://api.zkprova.com/api/v1/webhooks \
  -H "X-API-Key: zkp_live_abc123" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/webhooks/zkprova",
    "events": ["verification.completed", "verification.failed", "credential.revoked"]
  }'

Response includes a secret for signature verification.


Error Handling

HTTP Status Codes

Code Meaning
200 Success
202 Accepted (async job queued)
400 Bad request — invalid input or proof failure
401 Unauthorized — missing or invalid credentials
404 Resource not found
429 Rate limit or quota exceeded
500 Server error

Error Response Format

{
  "detail": "Human-readable error message"
}

Rate Limit Headers

Every response includes:

Header Description
X-RateLimit-Limit Requests allowed in window
X-RateLimit-Remaining Requests remaining
X-RateLimit-Reset Seconds until window resets
Retry-After Seconds to wait (on 429 only)

See Rate Limits & Quotas for per-endpoint limits and plan tiers.

Retry Strategy

  • 429 responses: Wait for Retry-After seconds, then retry.
  • 5xx responses: Exponential backoff (1s, 2s, 4s), max 3 retries.
  • The SDK handles retries automatically — configure with retries option.
const client = new ZKProvaClient({
  apiKey: 'zkp_live_abc123',
  baseUrl: 'https://api.zkprova.com/api/v1',
  retries: 3,      // default
  timeout: 10000,  // 10s default
});