API Documentation
Complete reference for the Vercre Verified Credentials API. Issue, verify, and revoke W3C verifiable credentials with on-chain attestation anchoring.
Overview
Vercre is a credential issuance and verification infrastructure API for W3C verifiable credentials with on-chain attestation anchoring on Base (Ethereum L2). It provides:
- W3C Verifiable Credential Data Model v2.0 compliance
- Custodial key management (private keys encrypted at rest, never exposed)
- Multi-algorithm support (Ed25519, secp256k1)
- Three DID methods (did:key, did:ethr, did:web)
- On-chain attestation anchoring for public verifiability
- Four-tier issuer trust registry with staking and reputation
- Usage-based billing via Stripe
Base URL
All API requests should be made to:
https://vercre.vectorguardlabs.com/api/v1/
All request and response bodies use camelCase JSON.
Authentication
Most endpoints require an API key passed in the X-API-Key header. Keys use the format:
vercre_client_<32-hex> (CLIENT role)
vercre_admin_<32-hex> (ADMIN role)
vercre_platform_<32-hex> (PLATFORM_ADMIN role)
Include the header in all authenticated requests:
X-API-Key: vercre_admin_your_key_here
Roles & Permissions
| Role | Access | DID Binding |
|---|---|---|
| CLIENT | Verify credentials, issuer lookup, credential status, list disputes. Dispute filing requires DID binding. | Optional |
| ADMIN | All CLIENT endpoints plus credential issuance, revocation, issuer list, verification requests, reputation, webhooks, fees, and usage stats | Required (auto-bound on onboarding) |
| PLATFORM_ADMIN | All ADMIN endpoints plus issuer registration/removal, tier changes, staking, slashing, dispute resolution, fee charging | Not bound |
Quickstart
Get up and running in 3 steps:
1. Install the SDK
pip install vercre-sdk
2. Onboard (get your API key)
curl -X POST https://vercre.vectorguardlabs.com/api/v1/onboard \
-H "Content-Type: application/json" \
-d '{"name":"Your Organization Name"}'
3. Issue your first credential
curl -X POST https://vercre.vectorguardlabs.com/api/v1/issue \
-H "X-API-Key: vercre_admin_your_key" \
-H "Content-Type: application/json" \
-d '{
"issuerDid": "did:key:z6MkYourDID",
"subjectDid": "did:key:z6MkSubjectDID",
"credentialType": "KYCBasicCredential",
"claims": {
"kycLevel": "basic",
"countryOfResidence": "US",
"verificationMethod": "document",
"verifiedAt": "2025-01-01T00:00:00Z"
},
"expiration": "2026-01-01T00:00:00+00:00"
}'
Unauthenticated Endpoints
These endpoints require no API key.
| Method | Path | Description |
|---|---|---|
| GET | /api/v1/health | Service health check (returns status and version) |
| POST | /api/v1/api-keys | Create a CLIENT API key (returns raw secret once) |
| POST | /api/v1/api-keys/platform-admin/bootstrap | Bootstrap the first PLATFORM_ADMIN key (one-time; 403 if one exists) |
| POST | /api/v1/onboard | One-command onboarding: generates key pair, DID, registers issuer, creates ADMIN API key |
| POST | /api/v1/identities/subject | Generate a subject DID:key identity (rate limited 5/hr per IP) |
| GET | /api/v1/status-lists/{issuer} | Published StatusList2021Credential for portable revocation checking |
Health Check
curl https://vercre.vectorguardlabs.com/api/v1/health
Response:
{"status": "ok", "version": "1.0.0"}
Onboarding
curl -X POST https://vercre.vectorguardlabs.com/api/v1/onboard \
-H "Content-Type: application/json" \
-d '{"name": "Acme Corp"}'
Response includes: did, name, tier (UNVERIFIED), status, reputation, stake, publicKey, apiKey, apiKeyId
Client / Admin Endpoints
Requires a CLIENT or ADMIN API key.
| Method | Path | Description |
|---|---|---|
| POST | /api/v1/verify | Verify a credential with optional policy constraints |
| POST | /api/v1/verify-presentation | Verify a Verifiable Presentation and its embedded credentials |
| GET | /api/v1/issuers/{did} | Look up an issuer by DID |
| GET | /api/v1/issuers/{did}/can-issue | Check if an issuer can issue a credential type (?type= required) |
| GET | /api/v1/issuers/{did}/whitelisted | Check if an issuer is registered and active |
| GET | /api/v1/credentials/{hash}/status | Check on-chain attestation status |
| GET | /api/v1/credentials/{id}/detail | Look up a previously issued credential by ID |
| GET | /api/v1/subjects/{did}/credentials | List all credentials issued to a subject DID |
| POST | /api/v1/disputes | Open a new dispute (requires DID-bound key) |
| GET | /api/v1/disputes | List disputes (optional ?issuer= filter) |
| GET | /api/v1/disputes/{id} | Look up a single dispute by ID |
| GET | /api/v1/disputes/{id}/evidence | List evidence for a dispute |
Admin Endpoints
Requires an ADMIN or PLATFORM_ADMIN API key.
| Method | Path | Description |
|---|---|---|
| POST | /api/v1/issue | Issue a credential and anchor on-chain |
| GET | /api/v1/issuers | List issuers (optional ?tier= filter) |
| POST | /api/v1/issuers/{did}/verify-request | Submit a verification request |
| GET | /api/v1/issuers/{did}/verify-request | Get stored verification request |
| POST | /api/v1/credentials/revoke | Revoke an on-chain attestation |
| POST | /api/v1/credentials/renew | Renew a credential (revoke old + issue new) |
| GET | /api/v1/reputation/{did} | Get reputation score |
| GET | /api/v1/reputation/{did}/history | Get reputation event history |
| POST | /api/v1/reputation/{did}/feedback | Submit verifier feedback (rate limited) |
| POST | /api/v1/disputes/{id}/evidence | Submit evidence to a dispute |
| GET | /api/v1/usage | Aggregate usage counts |
| POST | /api/v1/webhooks | Register a webhook endpoint |
| GET | /api/v1/webhooks | List all registered webhooks |
| GET | /api/v1/webhooks/{id} | Look up a single webhook |
| DELETE | /api/v1/webhooks/{id} | Remove a webhook |
| GET | /api/v1/fees/schedule | View fee schedule |
| GET | /api/v1/fees/history | View fee history |
| POST | /api/v1/keys/{did}/rotate | Rotate the signing key for a DID |
| GET | /api/v1/keys/{did}/versions | List all key versions for a DID |
| GET | /api/v1/api-keys | List API keys |
| GET | /api/v1/api-keys/{key_id} | Get a single API key by ID |
| DELETE | /api/v1/api-keys/{key_id} | Delete an API key |
| POST | /api/v1/chain/authorize | Authorize issuer on-chain (requires paid plan) |
| GET | /api/v1/chain/authorization/{did} | Check on-chain authorization status |
Platform Admin Endpoints
Requires a PLATFORM_ADMIN API key.
| Method | Path | Description |
|---|---|---|
| POST | /api/v1/api-keys/platform-admin | Create an additional PLATFORM_ADMIN key |
| POST | /api/v1/issuers | Register a new issuer (201) or return existing (200) |
| DELETE | /api/v1/issuers/{did}/remove | Remove an issuer |
| PATCH | /api/v1/issuers/{did}/tier | Update issuer tier |
| PATCH | /api/v1/issuers/{did}/status | Update issuer status |
| POST | /api/v1/issuers/{did}/stake | Add stake |
| POST | /api/v1/issuers/{did}/slash | Slash stake |
| POST | /api/v1/issuers/{did}/approve | Approve issuer verification and set tier |
| PATCH | /api/v1/disputes/{id}/resolve | Resolve a dispute |
| POST | /api/v1/fees/charge | Charge a fee manually |
| GET | /api/v1/fees/treasury | Treasury balance |
| GET | /api/v1/usage/detailed | Per-issuer usage breakdown |
| GET | /api/v1/audit/log | Query tamper-evident audit log |
| GET | /api/v1/audit/verify | Verify HMAC chain integrity |
| DELETE | /api/v1/chain/authorize | Revoke on-chain authorization for an issuer |
Credential Types
The API supports five credential types with tiered schemas. Claim keys use camelCase (W3C convention).
KYC Credentials (Tiered)
| Type | KYC Level | Fields | Use Case |
|---|---|---|---|
KYCBasicCredential | basic | kycLevel, countryOfResidence, verificationMethod, verifiedAt | Lightweight identity check |
KYCStandardCredential | standard | kycLevel, fullName, dateOfBirth, countryOfResidence, documentType, documentCountry, verifiedAt | Document-verified identity |
KYCEnhancedCredential | enhanced | kycLevel, fullName, dateOfBirth, countryOfResidence, sourceOfFunds, riskScore (0-100), pepStatus, sanctionsCheck, verifiedAt | Full due diligence with risk assessment |
expiration date at issuance time.
Other Credentials
| Type | Fields | Use Case |
|---|---|---|
AMLScreeningCredential | sanctionsStatus, pepStatus, adverseMedia, riskScore (0-100), screenedAt | Anti-money laundering screening |
AccreditedInvestorCredential | accredited, jurisdiction, verificationMethod, verifiedAt | SEC accredited investor verification |
Issuing Credentials
POST /api/v1/issue
Issue a credential and anchor its hash on-chain. The signing key is looked up server-side by the issuer DID.
Request Body
{
"issuerDid": "did:key:z6MkIssuer",
"subjectDid": "did:key:z6MkSubject",
"credentialType": "KYCBasicCredential",
"claims": {
"kycLevel": "basic",
"countryOfResidence": "US",
"verificationMethod": "document",
"verifiedAt": "2025-01-01T00:00:00Z"
},
"expiration": "2026-01-01T00:00:00+00:00"
}
Optional Fields
Add issuer metadata and schema version:
{
"issuerDid": "did:key:z6MkIssuer",
"subjectDid": "did:key:z6MkSubject",
"credentialType": "AMLScreeningCredential",
"claims": {
"sanctionsStatus": "clear",
"pepStatus": false,
"adverseMedia": false,
"riskScore": 10,
"screenedAt": "2025-01-01T00:00:00Z"
},
"schemaVersion": "1.0.0",
"issuerName": "Acme Compliance",
"issuerJurisdiction": "US-SEC",
"issuerLicense": "LIC-001"
}
Verifying Credentials
POST /api/v1/verify
Verify a credential's JWT signature and on-chain attestation status. Optionally apply policy constraints.
Basic Verification
curl -X POST https://vercre.vectorguardlabs.com/api/v1/verify \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{"credential": {...}}'
Verification with Policy
curl -X POST https://vercre.vectorguardlabs.com/api/v1/verify \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"credential": {...},
"policy": {
"minTier": "REGULATED",
"minReputation": 60
}
}'
Response Fields
The response includes a confidence score (0.0-1.0) weighted across three dimensions:
- Credential validity (40%) — signature valid, not expired, not revoked
- Issuer tier (30%) — higher tier = higher confidence
- Issuer reputation (30%) — reputation score mapping
The response also includes a freshness heuristic:
| Freshness | Days Until Expiry | Meaning |
|---|---|---|
| fresh | >90 | No action needed |
| aging | 30–90 | Consider renewal |
| stale | 7–30 | Renewal recommended |
| critical | 0–7 | Renewal urgent |
| expired | 0 | Credential expired |
Revoking Credentials
POST /api/v1/credentials/revoke
Revoke an on-chain attestation. Only the original issuer can revoke their own credentials.
curl -X POST https://vercre.vectorguardlabs.com/api/v1/credentials/revoke \
-H "X-API-Key: $ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{
"credential": {...},
"issuerDid": "did:key:z6MkIssuer"
}'
Credential Renewal
POST /api/v1/credentials/renew
Renew a credential in a single API call. The old credential is revoked and a new one is issued with the same claims but a new expiration.
curl -X POST https://vercre.vectorguardlabs.com/api/v1/credentials/renew \
-H "X-API-Key: $ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{
"credential": {...},
"issuerDid": "did:key:z6MkIssuer",
"newExpiration": "2027-06-01T00:00:00Z"
}'
Issuer Trust Tiers
Issuers are classified into four tiers that control which credential types they may issue:
| Tier | Allowed Credential Types | Minimum Stake |
|---|---|---|
| INSTITUTIONAL | All 5 types | 1000.0 |
| REGULATED | All 5 types | 500.0 |
| VERIFIED | KYCBasic, KYCStandard, AMLScreening | 100.0 |
| UNVERIFIED | KYCBasic only | 0.0 |
Operational Status
Tiers are separate from operational status. An issuer has both a tier and a status:
| Status | Effect |
|---|---|
| ACTIVE | Normal operation — issuer can issue credentials per their tier |
| SUSPENDED | Blocked from issuing — triggered by stake falling below minimum |
| REVOKED | Permanently blocked from issuing |
Reputation Scoring
Every issuer starts with a base score of 50 (range 0–100). Events adjust the score:
| Event | Score Delta |
|---|---|
| Credential issued | +1 |
| Credential revoked | -3 |
| Dispute won | +5 |
| Dispute lost | -10 |
| Verifier positive feedback | +2 |
| Verifier negative feedback | -5 |
DID Methods
The API supports three Decentralized Identifier methods:
| Method | Format | Resolution | Best For |
|---|---|---|---|
did:key | did:key:z6Mk... | Offline (key embedded in DID) | Testing, self-certifying identities |
did:ethr | did:ethr:0x... | Ethereum address derivation | On-chain subjects, wallet-based identity |
did:web | did:web:example.com | HTTPS fetch of /.well-known/did.json | Organizations, institutional issuers |
Key Types
| Type | Algorithm | JWT Algorithm | Use When |
|---|---|---|---|
ed25519 | EdDSA (Edwards-curve) | EdDSA | General-purpose credentials, did:key, did:web |
secp256k1 | ECDSA (Koblitz curve) | ES256K | Ethereum integration, did:ethr, wallet-based identity |
Key Rotation
Signing keys can be rotated without invalidating existing credentials. Old keys are preserved so previously-signed credentials remain verifiable.
Rotate a Key
POST /api/v1/keys/{did}/rotate
curl -X POST https://vercre.vectorguardlabs.com/api/v1/keys/did:key:z6MkIssuer/rotate \
-H "X-API-Key: $ADMIN_KEY"
Response: message, did, newPublicKey, keyType
List Key Versions
GET /api/v1/keys/{did}/versions
curl https://vercre.vectorguardlabs.com/api/v1/keys/did:key:z6MkIssuer/versions \
-H "X-API-Key: $ADMIN_KEY"
Response: array of {version, active, publicKey, keyType, createdAt, rotatedAt}
Issuer Verification Flow
Onboarding creates an issuer at the UNVERIFIED tier. To move to a higher tier, submit a verification request declaring your target tier with required documentation.
Flow
- Onboard:
POST /api/v1/onboard→ UNVERIFIED + ADMIN key - Submit request:
POST /api/v1/issuers/{did}/verify-request→ declares target tier + required fields - Platform admin approves:
POST /api/v1/issuers/{did}/approve→ validates data, sets tier
Per-Tier Requirements (Cumulative)
| Field | VERIFIED | REGULATED | INSTITUTIONAL |
|---|---|---|---|
| entityType | required | required | required |
| jurisdiction | required | required | required |
| licenseId | required | required | required |
| website | required | required | required |
| regulatoryBody | — | required | required |
| licenseType | — | required | required |
| complianceOfficerName | — | required | required |
| complianceOfficerEmail | — | required | required |
| primaryRegulator | — | — | required |
| institutionCharterNumber | — | — | required |
| soc2Certified | — | — | required |
| lastExaminationDate | — | — | required |
Example: Submit Verification Request
curl -X POST https://vercre.vectorguardlabs.com/api/v1/issuers/did:key:z6Mk.../verify-request \
-H "X-API-Key: $ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{
"requestedTier": "VERIFIED",
"entityType": "bank",
"jurisdiction": "US",
"licenseId": "FDIC-12345",
"website": "https://acmecorp.com"
}'
Webhooks
Register webhook endpoints to receive real-time HTTP POST notifications when events occur. Webhooks are scoped to the registering issuer's DID.
Event Types
| Event | Trigger |
|---|---|
verification.passed | POST /api/v1/verify returns valid: true |
verification.failed | POST /api/v1/verify returns valid: false |
credential.revoked | POST /api/v1/credentials/revoke succeeds |
dispute.opened | POST /api/v1/disputes creates a dispute |
Register a Webhook
curl -X POST https://vercre.vectorguardlabs.com/api/v1/webhooks \
-H "X-API-Key: $ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/hook",
"events": ["verification.passed", "verification.failed", "credential.revoked"]
}'
Retry Policy
Webhook dispatch uses exponential backoff:
| Attempt | Delay | Action |
|---|---|---|
| 1 | 0s | Initial delivery |
| 2 | 1s | First retry (on 5xx or connection error) |
| 3 | 5s | Second retry |
| 4 | 30s | Final retry |
HTTP 4xx responses are treated as terminal (no retry). Maximum 10 webhooks per issuer.
Rate Limiting
Rate limits are applied to sensitive endpoints using a Redis-backed sliding window algorithm:
| Endpoint | Limit | Window | Tracking |
|---|---|---|---|
POST /api/v1/onboard | 3 requests | 1 hour | Per IP |
POST /api/v1/identities/subject | 5 requests | 1 hour | Per IP |
POST /api/v1/issue | 60 requests | 1 minute | Per API key |
POST /api/v1/verify | 60 requests | 1 minute | Per API key |
POST /api/v1/credentials/revoke | 60 requests | 1 minute | Per API key |
When exceeded, returns 429 Too Many Requests with Retry-After header.
Billing & Plans
Usage-based billing is integrated via Stripe with six plans:
Issuer Plans
| Plan | Monthly Issuances | Webhooks | Rate Limit | Attestation |
|---|---|---|---|---|
| Issuer Starter (Free) | 25 | 2 | 5 req/min | File-backed |
| Issuer Professional | 500 | 10 | 60 req/min | On-chain |
| Issuer Enterprise | Unlimited | Unlimited | 300 req/min | On-chain |
Verifier Plans
| Plan | Webhooks | Rate Limit |
|---|---|---|
| Verifier Starter (Free) | 2 | 10 req/min |
| Verifier Professional | 10 | 120 req/min |
| Verifier Enterprise | Unlimited | 600 req/min |
Billing Endpoints
| Method | Path | Description |
|---|---|---|
| POST | /api/v1/billing/subscribe | Subscribe to a plan |
| GET | /api/v1/billing/subscription | View current subscription |
| POST | /api/v1/billing/portal | Get Stripe customer portal URL |
On-Chain Anchoring
Paid-tier issuers can anchor credential hashes on the production AttestationRegistry smart contract on Base (Ethereum L2). This makes credentials publicly verifiable by any third party on BaseScan.
How It Works
- Credential is issued and signed as a JWT
- SHA-256 hash of the credential is computed (deterministic canonical JSON)
- Hash is anchored on-chain with optional expiration
- Any verifier can check attestation status via the contract (public read, no API key needed)
Attestation States
| Status | Value | Description |
|---|---|---|
| NOT_FOUND | 0 | No attestation anchored for this hash |
| ACTIVE | 1 | Attestation anchored and valid |
| REVOKED | 2 | Attestation has been revoked by the issuer |
| EXPIRED | 3 | Attestation expiration has passed |
Authorize On-Chain (One-Time)
curl -X POST https://vercre.vectorguardlabs.com/api/v1/chain/authorize \
-H "X-API-Key: $ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{"issuerDid": "did:key:z6MkYourDID"}'
SD-JWT (Selective Disclosure)
The SDK supports SD-JWT for privacy-preserving credential presentation. Issuers create JWTs with selectively disclosable claims, and holders reveal only what verifiers need.
How It Works
- Issuer creates an SD-JWT marking which claims are selectively disclosable
- Holder presents only the claims the verifier requires (with key binding proof)
- Verifier verifies the signature and reconstructs only the disclosed claims
Example (Python SDK)
from vercre.crypto.sd_jwt import create_sd_jwt, present_sd_jwt, verify_sd_jwt
# Issuer: create SD-JWT with selectively disclosable claims
sd_jwt, disclosures = create_sd_jwt(
payload={"iss": issuer_did, "sub": holder_did,
"name": "Alice", "age": 30, "country": "US"},
key_pair=issuer_kp,
sd_claims=["name", "age", "country"],
holder_did=holder_did,
)
# Holder: present only "age" (with key binding proof)
presentation = present_sd_jwt(
sd_jwt, disclosures, ["age"],
holder_key_pair=holder_kp,
audience="verifier.example.com",
nonce="challenge123",
)
# Verifier: verify and reconstruct only disclosed claims
claims = verify_sd_jwt(
presentation, issuer_kp.public_key_bytes, KeyType.ED25519,
holder_public_key_bytes=holder_kp.public_key_bytes,
holder_key_type=KeyType.ED25519,
expected_audience="verifier.example.com",
expected_nonce="challenge123",
)
# claims = {"iss": "...", "sub": "...", "age": 30}
# "name" and "country" are NOT revealed
Error Handling
The API uses standard HTTP status codes. Error responses include a detail field with a human-readable message.
| Status | Meaning |
|---|---|
400 | Bad request — invalid parameters or malformed JSON |
401 | Unauthorized — missing or invalid API key |
402 | Payment required — plan quota exceeded |
403 | Forbidden — insufficient permissions for this operation |
404 | Not found — resource does not exist |
422 | Unprocessable entity — validation failed (e.g., self-issuance, missing fields) |
429 | Too many requests — rate limit exceeded (check Retry-After header) |
500 | Internal server error |
503 | Service unavailable — provisioning in progress or upstream failure |
Example Error Response
{
"detail": "Rate limit exceeded. Try again in 3600 seconds."
}
Common Troubleshooting
- 429 on onboarding: Wait 1 hour (3 requests/hour per IP)
- 403 "API key must be bound to a DID": Create a CLIENT key with DID binding to file disputes
- 403 "not authorized for this issuer DID": Use the ADMIN key from onboarding for that specific issuer
- 422 self-issuance: Issuer DID and subject DID must be different
SDK Installation
Install the Python SDK from PyPI:
# Core SDK
pip install vercre-sdk
# With EVM (on-chain) integration
pip install "vercre-sdk[evm]"
# With YugabyteDB backend
pip install "vercre-sdk[pg]"
# With OpenTelemetry tracing
pip install "vercre-sdk[tracing]"
# All production features
pip install "vercre-sdk[evm,pg,tracing]"
Requirements
- Python 3.13+
uv(recommended) orpip
Self-Hosted Server
Run your own instance with full SDK functionality (file-backed attestation):
pip install vercre-sdk
vercre-server # Starts on 0.0.0.0:8000