Skip to main content
Credo supports multiple verifiable credential formats, each with different characteristics, privacy features, and use cases. Understanding these formats helps you choose the right one for your application.

Overview

Verifiable credentials are digital credentials that can be cryptographically verified. Credo supports three major credential formats:
  • W3C Verifiable Credentials (JSON-LD and JWT)
  • SD-JWT VC (Selective Disclosure JWT Verifiable Credentials)
  • AnonCreds (Anonymous Credentials)
Each format has different modules and APIs in Credo.

W3C Verifiable Credentials

W3C Verifiable Credentials follow the W3C VCDM (Verifiable Credentials Data Model) specification. Credo supports both version 1.1 and version 2.0.

W3cCredentialsModule

The W3cCredentialsModule is a default module that provides W3C VC functionality:
import { W3cCredentialsModule } from '@credo-ts/core'

const agent = new Agent({
  modules: {
    // W3cCredentialsModule is registered by default
    // You can customize it if needed:
    w3cCredentials: new W3cCredentialsModule({
      // Custom configuration
    }),
  },
})
Source: packages/core/src/modules/vc/W3cCredentialsModule.ts:20

Data Model v1.1

W3C VCDM 1.1 credentials are accessible through agent.w3cCredentials:
// Sign a credential (JSON-LD with Data Integrity proof)
const signResult = await agent.w3cCredentials.signCredential({
  format: 'jsonld',
  credential: new W3cCredential({
    context: [
      'https://www.w3.org/2018/credentials/v1',
      'https://www.w3.org/2018/credentials/examples/v1',
    ],
    type: ['VerifiableCredential', 'UniversityDegreeCredential'],
    issuer: 'did:key:z6MkpTHR...',
    issuanceDate: '2023-01-01T00:00:00Z',
    credentialSubject: {
      id: 'did:key:z6MkhKLw...',
      degree: {
        type: 'BachelorDegree',
        name: 'Bachelor of Science',
      },
    },
  }),
  proofType: 'Ed25519Signature2018',
  verificationMethod: 'did:key:z6MkpTHR...#z6MkpTHR...',
})

// Sign a credential (JWT format)
const jwtResult = await agent.w3cCredentials.signCredential({
  format: 'jwt',
  credential: myCredential,
  verificationMethod: 'did:key:z6MkpTHR...#z6MkpTHR...',
})

// Verify a credential
const verifyResult = await agent.w3cCredentials.verifyCredential({
  credential: signResult.verifiableCredential,
})

Data Model v2.0

W3C VCDM 2.0 credentials are accessible through agent.w3cV2Credentials:
import { W3cV2Credential } from '@credo-ts/core'

// Create and sign a v2.0 credential
const signResult = await agent.w3cV2Credentials.signCredential({
  format: 'jsonld',
  credential: new W3cV2Credential({
    context: ['https://www.w3.org/ns/credentials/v2'],
    type: ['VerifiableCredential'],
    issuer: 'did:key:z6MkpTHR...',
    validFrom: '2023-01-01T00:00:00Z',
    credentialSubject: {
      id: 'did:key:z6MkhKLw...',
      degree: 'Bachelor of Science',
    },
  }),
  verificationMethod: 'did:key:z6MkpTHR...#z6MkpTHR...',
})

Proof Formats

JSON-LD credentials use Data Integrity proofs (formerly Linked Data Proofs).Features:
  • Semantic interoperability
  • Context-based vocabulary
  • Multiple signature suites
  • Linked data ecosystem
Supported Signature Suites:
  • Ed25519Signature2018
  • Ed25519Signature2020
const result = await agent.w3cCredentials.signCredential({
  format: 'jsonld',
  credential: myCredential,
  proofType: 'Ed25519Signature2020',
  verificationMethod: 'did:key:z6Mk...#z6Mk...',
})
Source: packages/core/src/modules/vc/W3cCredentialsModule.ts:40

When to Use W3C VCs

Use W3C Verifiable Credentials when:
  • You need semantic interoperability with JSON-LD
  • You’re building for broad ecosystem compatibility
  • You want to use standard W3C specifications
  • You need simple, straightforward credentials
  • You’re integrating with OpenID4VC

SD-JWT VC (Selective Disclosure JWT)

SD-JWT VC enables selective disclosure of credential claims, allowing holders to reveal only specific information.

SdJwtVcModule

The SdJwtVcModule is a default module that provides SD-JWT VC functionality:
import { SdJwtVcModule } from '@credo-ts/core'

const agent = new Agent({
  modules: {
    // SdJwtVcModule is registered by default
    sdJwtVc: new SdJwtVcModule(),
  },
})
Source: packages/core/src/modules/sd-jwt-vc/SdJwtVcModule.ts:10

Creating SD-JWT VCs

// Sign an SD-JWT VC with selective disclosure
const sdJwtVc = await agent.sdJwtVc.sign({
  holder: { method: 'did', didUrl: 'did:key:z6MkhKLw...' },
  issuer: {
    method: 'did',
    didUrl: 'did:key:z6MkpTHR...#z6MkpTHR...',
  },
  payload: {
    vct: 'https://example.com/credentials/person',
    given_name: 'John',
    family_name: 'Doe',
    email: '[email protected]',
    birth_date: '1990-01-01',
  },
  disclosureFrame: {
    _sd: ['given_name', 'family_name', 'email', 'birth_date'],
  },
})

Selective Disclosure

The holder can choose which claims to disclose:
// Present with selective disclosure
const presentation = await agent.sdJwtVc.present({
  sdJwtVc: sdJwtVc.compact,
  // Only disclose given_name and family_name
  disclosedPayload: {
    given_name: 'John',
    family_name: 'Doe',
  },
})

Verification

// Verify an SD-JWT VC presentation
const verifyResult = await agent.sdJwtVc.verify({
  sdJwtVc: presentation.compact,
})

if (verifyResult.isValid) {
  console.log('Disclosed claims:', verifyResult.payload)
}

When to Use SD-JWT VC

Use SD-JWT VC when:
  • You need selective disclosure of claims
  • Privacy is a primary concern
  • You want to minimize data sharing
  • You’re building for OpenID4VC with SD-JWT support
  • You need JWT-based credentials with privacy

AnonCreds (Anonymous Credentials)

AnonCreds provides advanced privacy features including zero-knowledge proofs and credential revocation.

AnonCredsModule

AnonCreds support requires the @credo-ts/anoncreds package:
import { AnonCredsModule } from '@credo-ts/anoncreds'
import { anoncreds } from '@hyperledger/anoncreds-nodejs'

const agent = new Agent({
  modules: {
    anoncreds: new AnonCredsModule({
      anoncreds,
      registries: [/* ledger registries */],
    }),
  },
})

AnonCreds Architecture

AnonCreds has a more complex architecture:
  1. Schema: Defines the attributes of a credential
  2. Credential Definition: Links schema to an issuer
  3. Revocation Registry: Manages credential revocation
  4. Credential: The actual credential issued to a holder
  5. Proof: Zero-knowledge proof presentation

Creating AnonCreds

// 1. Create a schema
const schema = await agent.modules.anoncreds.createSchema({
  issuerId: 'did:indy:bcovrin:test:...',
  name: 'Person',
  version: '1.0',
  attrNames: ['name', 'age', 'email'],
})

// 2. Create a credential definition
const credentialDefinition = await agent.modules.anoncreds.createCredentialDefinition({
  issuerId: 'did:indy:bcovrin:test:...',
  schemaId: schema.schemaId,
  tag: 'default',
  supportRevocation: true,
})

// 3. Issue credential (typically done through DIDComm protocols)
// See DIDComm issue-credential protocol documentation

AnonCreds Proofs

AnonCreds enables zero-knowledge proofs:
// Request proof with predicates
const proofRequest = {
  name: 'Age Verification',
  version: '1.0',
  requested_attributes: {
    name: {
      name: 'name',
      restrictions: [{ cred_def_id: credDefId }],
    },
  },
  requested_predicates: {
    age: {
      name: 'age',
      p_type: '>=',
      p_value: 18,
      restrictions: [{ cred_def_id: credDefId }],
    },
  },
}

// Holder creates proof proving age >= 18 without revealing actual age
// See DIDComm present-proof protocol documentation

Revocation

AnonCreds supports efficient revocation:
// Create revocation registry
const revocationRegistry = await agent.modules.anoncreds.createRevocationRegistry({
  issuerId: 'did:indy:bcovrin:test:...',
  credentialDefinitionId: credDefId,
  maximumCredentialNumber: 100,
})

// Revoke a credential
await agent.modules.anoncreds.updateRevocationState({
  revocationRegistryId: revRegId,
  credentialIndex: 5,
  revoked: true,
})

When to Use AnonCreds

Use AnonCreds when:
  • You need zero-knowledge proofs
  • You need predicates (prove age >= 18 without revealing age)
  • You need efficient revocation
  • You’re building on Hyperledger Indy networks
  • Privacy is critical
  • You need unlinkability between presentations
AnonCreds requires Hyperledger Indy infrastructure (or compatible ledgers). It has more setup complexity than W3C VCs but provides stronger privacy guarantees.

Credential Format Comparison

FeatureW3C VC (JSON-LD)W3C VC (JWT)SD-JWT VCAnonCreds
Selective Disclosure❌ No❌ No✅ Yes✅ Yes
Zero-Knowledge Proofs❌ No❌ No❌ No✅ Yes
Predicates❌ No❌ No❌ No✅ Yes
Revocation⚠️ Limited⚠️ Limited⚠️ Limited✅ Efficient
Setup Complexity✅ Low✅ Low✅ Low❌ High
Infrastructure✅ None✅ None✅ None❌ Ledger Required
Semantic Interop✅ Yes❌ No❌ No❌ No
Unlinkability❌ No❌ No⚠️ Partial✅ Yes
Standard Status✅ W3C Standard✅ W3C Standard✅ IETF Draft⚠️ Community

Storage and Repositories

Each credential format has its own repository for storage:

W3C Credential Storage

import { W3cCredentialRepository } from '@credo-ts/core'

// Access repository
const credentialRepository = agent.context.resolve(W3cCredentialRepository)

// Query credentials
const credentials = await credentialRepository.findByQuery(agent.context, {
  type: 'UniversityDegreeCredential',
})

SD-JWT VC Storage

import { SdJwtVcRepository } from '@credo-ts/core'

// Access repository
const sdJwtRepository = agent.context.resolve(SdJwtVcRepository)

// Query SD-JWT VCs
const sdJwtVcs = await sdJwtRepository.getAll(agent.context)

AnonCreds Storage

AnonCreds uses multiple repositories:
import {
  AnonCredsSchemaRepository,
  AnonCredsCredentialDefinitionRepository,
  AnonCredsRevocationRegistryRepository,
} from '@credo-ts/anoncreds'

// Each component has its own repository
Source: packages/core/src/storage/Repository.ts:13

Presentation Exchange

Credo includes the DIF Presentation Exchange module for requesting and presenting credentials:
import { DifPresentationExchangeModule } from '@credo-ts/core'

// Create a presentation definition
const presentationDefinition = {
  id: 'university-degree-verification',
  input_descriptors: [
    {
      id: 'degree',
      constraints: {
        fields: [
          {
            path: ['$.type'],
            filter: {
              type: 'array',
              contains: { const: 'UniversityDegreeCredential' },
            },
          },
        ],
      },
    },
  ],
}

// The module is registered by default as 'pex'

Best Practices

Format Selection:
  • Start with W3C VCs for simple use cases
  • Add SD-JWT VC when you need selective disclosure
  • Use AnonCreds for maximum privacy and ZKP features
  • Consider your infrastructure requirements
Security:
  • Always verify credentials before trusting them
  • Check credential status (revocation)
  • Validate issuer DIDs
  • Verify proof signatures
Privacy:
  • Use selective disclosure to minimize data sharing
  • Consider unlinkability requirements
  • Implement proper data retention policies
  • Use predicates instead of revealing exact values when possible
Interoperability:
  • Use W3C VCs for maximum interoperability
  • Document which formats your application supports
  • Test with multiple verifier implementations
  • Follow credential type conventions

OpenID4VC Integration

Credo supports OpenID for Verifiable Credentials (OpenID4VCI and OpenID4VP):
import { OpenId4VcModule } from '@credo-ts/openid4vc'

const agent = new Agent({
  modules: {
    openid4vc: new OpenId4VcModule(),
  },
})

// Issue credentials via OpenID4VCI
const credentialOffer = await agent.openid4vc.issuer.createCredentialOffer({
  credentials: [/* ... */],
})

// Present credentials via OpenID4VP
const authRequest = await agent.openid4vc.holder.handleAuthorizationRequest({
  authorizationRequest: request,
})
  • Agent - Learn about agent architecture
  • DID Methods - DIDs used for credential issuance
  • Modules - Understanding credential modules
  • Storage - How credentials are stored

Build docs developers (and LLMs) love