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

RoleAccessDID Binding
CLIENTVerify credentials, issuer lookup, credential status, list disputes. Dispute filing requires DID binding.Optional
ADMINAll CLIENT endpoints plus credential issuance, revocation, issuer list, verification requests, reputation, webhooks, fees, and usage statsRequired (auto-bound on onboarding)
PLATFORM_ADMINAll ADMIN endpoints plus issuer registration/removal, tier changes, staking, slashing, dispute resolution, fee chargingNot 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"}'
Response includes: Your DID, public key, API key (shown once), and trust registry entry at the UNVERIFIED tier.

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.

MethodPathDescription
GET/api/v1/healthService health check (returns status and version)
POST/api/v1/api-keysCreate a CLIENT API key (returns raw secret once)
POST/api/v1/api-keys/platform-admin/bootstrapBootstrap the first PLATFORM_ADMIN key (one-time; 403 if one exists)
POST/api/v1/onboardOne-command onboarding: generates key pair, DID, registers issuer, creates ADMIN API key
POST/api/v1/identities/subjectGenerate 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

Save your API key! The raw API key secret is shown only once at creation. Store it securely.

Client / Admin Endpoints

Requires a CLIENT or ADMIN API key.

MethodPathDescription
POST/api/v1/verifyVerify a credential with optional policy constraints
POST/api/v1/verify-presentationVerify a Verifiable Presentation and its embedded credentials
GET/api/v1/issuers/{did}Look up an issuer by DID
GET/api/v1/issuers/{did}/can-issueCheck if an issuer can issue a credential type (?type= required)
GET/api/v1/issuers/{did}/whitelistedCheck if an issuer is registered and active
GET/api/v1/credentials/{hash}/statusCheck on-chain attestation status
GET/api/v1/credentials/{id}/detailLook up a previously issued credential by ID
GET/api/v1/subjects/{did}/credentialsList all credentials issued to a subject DID
POST/api/v1/disputesOpen a new dispute (requires DID-bound key)
GET/api/v1/disputesList disputes (optional ?issuer= filter)
GET/api/v1/disputes/{id}Look up a single dispute by ID
GET/api/v1/disputes/{id}/evidenceList evidence for a dispute

Admin Endpoints

Requires an ADMIN or PLATFORM_ADMIN API key.

MethodPathDescription
POST/api/v1/issueIssue a credential and anchor on-chain
GET/api/v1/issuersList issuers (optional ?tier= filter)
POST/api/v1/issuers/{did}/verify-requestSubmit a verification request
GET/api/v1/issuers/{did}/verify-requestGet stored verification request
POST/api/v1/credentials/revokeRevoke an on-chain attestation
POST/api/v1/credentials/renewRenew a credential (revoke old + issue new)
GET/api/v1/reputation/{did}Get reputation score
GET/api/v1/reputation/{did}/historyGet reputation event history
POST/api/v1/reputation/{did}/feedbackSubmit verifier feedback (rate limited)
POST/api/v1/disputes/{id}/evidenceSubmit evidence to a dispute
GET/api/v1/usageAggregate usage counts
POST/api/v1/webhooksRegister a webhook endpoint
GET/api/v1/webhooksList 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/scheduleView fee schedule
GET/api/v1/fees/historyView fee history
POST/api/v1/keys/{did}/rotateRotate the signing key for a DID
GET/api/v1/keys/{did}/versionsList all key versions for a DID
GET/api/v1/api-keysList 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/authorizeAuthorize 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.

MethodPathDescription
POST/api/v1/api-keys/platform-adminCreate an additional PLATFORM_ADMIN key
POST/api/v1/issuersRegister a new issuer (201) or return existing (200)
DELETE/api/v1/issuers/{did}/removeRemove an issuer
PATCH/api/v1/issuers/{did}/tierUpdate issuer tier
PATCH/api/v1/issuers/{did}/statusUpdate issuer status
POST/api/v1/issuers/{did}/stakeAdd stake
POST/api/v1/issuers/{did}/slashSlash stake
POST/api/v1/issuers/{did}/approveApprove issuer verification and set tier
PATCH/api/v1/disputes/{id}/resolveResolve a dispute
POST/api/v1/fees/chargeCharge a fee manually
GET/api/v1/fees/treasuryTreasury balance
GET/api/v1/usage/detailedPer-issuer usage breakdown
GET/api/v1/audit/logQuery tamper-evident audit log
GET/api/v1/audit/verifyVerify HMAC chain integrity
DELETE/api/v1/chain/authorizeRevoke 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)

TypeKYC LevelFieldsUse Case
KYCBasicCredentialbasickycLevel, countryOfResidence, verificationMethod, verifiedAtLightweight identity check
KYCStandardCredentialstandardkycLevel, fullName, dateOfBirth, countryOfResidence, documentType, documentCountry, verifiedAtDocument-verified identity
KYCEnhancedCredentialenhancedkycLevel, fullName, dateOfBirth, countryOfResidence, sourceOfFunds, riskScore (0-100), pepStatus, sanctionsCheck, verifiedAtFull due diligence with risk assessment
Note: All KYC credential types require an expiration date at issuance time.

Other Credentials

TypeFieldsUse Case
AMLScreeningCredentialsanctionsStatus, pepStatus, adverseMedia, riskScore (0-100), screenedAtAnti-money laundering screening
AccreditedInvestorCredentialaccredited, jurisdiction, verificationMethod, verifiedAtSEC 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"
}
Self-issuance rejected: The issuer DID and subject DID must be different entities. Using the same DID for both will return a 422 error.

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:

FreshnessDays Until ExpiryMeaning
fresh>90No action needed
aging30–90Consider renewal
stale7–30Renewal recommended
critical0–7Renewal urgent
expired0Credential 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"
  }'
Issuer-only revocation: Both the SDK and smart contract enforce that only the original issuing address can revoke. Attempting to revoke another issuer's credential will return a 403 error.

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:

TierAllowed Credential TypesMinimum Stake
INSTITUTIONALAll 5 types1000.0
REGULATEDAll 5 types500.0
VERIFIEDKYCBasic, KYCStandard, AMLScreening100.0
UNVERIFIEDKYCBasic only0.0

Operational Status

Tiers are separate from operational status. An issuer has both a tier and a status:

StatusEffect
ACTIVENormal operation — issuer can issue credentials per their tier
SUSPENDEDBlocked from issuing — triggered by stake falling below minimum
REVOKEDPermanently blocked from issuing

Reputation Scoring

Every issuer starts with a base score of 50 (range 0–100). Events adjust the score:

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

MethodFormatResolutionBest For
did:keydid:key:z6Mk...Offline (key embedded in DID)Testing, self-certifying identities
did:ethrdid:ethr:0x...Ethereum address derivationOn-chain subjects, wallet-based identity
did:webdid:web:example.comHTTPS fetch of /.well-known/did.jsonOrganizations, institutional issuers

Key Types

TypeAlgorithmJWT AlgorithmUse When
ed25519EdDSA (Edwards-curve)EdDSAGeneral-purpose credentials, did:key, did:web
secp256k1ECDSA (Koblitz curve)ES256KEthereum 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

  1. Onboard: POST /api/v1/onboard → UNVERIFIED + ADMIN key
  2. Submit request: POST /api/v1/issuers/{did}/verify-request → declares target tier + required fields
  3. Platform admin approves: POST /api/v1/issuers/{did}/approve → validates data, sets tier

Per-Tier Requirements (Cumulative)

FieldVERIFIEDREGULATEDINSTITUTIONAL
entityTyperequiredrequiredrequired
jurisdictionrequiredrequiredrequired
licenseIdrequiredrequiredrequired
websiterequiredrequiredrequired
regulatoryBodyrequiredrequired
licenseTyperequiredrequired
complianceOfficerNamerequiredrequired
complianceOfficerEmailrequiredrequired
primaryRegulatorrequired
institutionCharterNumberrequired
soc2Certifiedrequired
lastExaminationDaterequired

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

EventTrigger
verification.passedPOST /api/v1/verify returns valid: true
verification.failedPOST /api/v1/verify returns valid: false
credential.revokedPOST /api/v1/credentials/revoke succeeds
dispute.openedPOST /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:

AttemptDelayAction
10sInitial delivery
21sFirst retry (on 5xx or connection error)
35sSecond retry
430sFinal 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:

EndpointLimitWindowTracking
POST /api/v1/onboard3 requests1 hourPer IP
POST /api/v1/identities/subject5 requests1 hourPer IP
POST /api/v1/issue60 requests1 minutePer API key
POST /api/v1/verify60 requests1 minutePer API key
POST /api/v1/credentials/revoke60 requests1 minutePer 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

PlanMonthly IssuancesWebhooksRate LimitAttestation
Issuer Starter (Free)2525 req/minFile-backed
Issuer Professional5001060 req/minOn-chain
Issuer EnterpriseUnlimitedUnlimited300 req/minOn-chain

Verifier Plans

PlanWebhooksRate Limit
Verifier Starter (Free)210 req/min
Verifier Professional10120 req/min
Verifier EnterpriseUnlimited600 req/min

Billing Endpoints

MethodPathDescription
POST/api/v1/billing/subscribeSubscribe to a plan
GET/api/v1/billing/subscriptionView current subscription
POST/api/v1/billing/portalGet 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

  1. Credential is issued and signed as a JWT
  2. SHA-256 hash of the credential is computed (deterministic canonical JSON)
  3. Hash is anchored on-chain with optional expiration
  4. Any verifier can check attestation status via the contract (public read, no API key needed)

Attestation States

StatusValueDescription
NOT_FOUND0No attestation anchored for this hash
ACTIVE1Attestation anchored and valid
REVOKED2Attestation has been revoked by the issuer
EXPIRED3Attestation 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"}'
Privacy by design: Only credential hashes are stored on-chain — never raw credential data. The smart contract serves as a tamper-proof registry of attestation status.

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

  1. Issuer creates an SD-JWT marking which claims are selectively disclosable
  2. Holder presents only the claims the verifier requires (with key binding proof)
  3. 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
Key Binding JWT (KB-JWT): When the holder provides their key pair, a short JWT is signed proving possession of the private key. This prevents replay attacks — a stolen SD-JWT cannot be presented without the holder's private key.

Error Handling

The API uses standard HTTP status codes. Error responses include a detail field with a human-readable message.

StatusMeaning
400Bad request — invalid parameters or malformed JSON
401Unauthorized — missing or invalid API key
402Payment required — plan quota exceeded
403Forbidden — insufficient permissions for this operation
404Not found — resource does not exist
422Unprocessable entity — validation failed (e.g., self-issuance, missing fields)
429Too many requests — rate limit exceeded (check Retry-After header)
500Internal server error
503Service 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) or pip

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
Self-hosted vs. Hosted: Self-hosted gets the full SDK for private/internal use. On-chain anchoring (public verifiability by third parties) requires the hosted API with a paid plan.

Links