arrow-left

All pages
gitbookPowered by GitBook
1 of 22

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Credo

Credo SDK with cheqd support for Decentralized Identifiers (DIDs), DID-Linked Resources (DLRs) and Credentials.

hashtag
Get Started with Credo

Credo is a TypeScript-based framework for building Self-Sovereign Identity (SSI) agents and services. It is designed for modularity, interoperability, and compliance with the European Architecture and Reference Framework (ARF).

Previously known as Aries Framework JavaScript, Credo has recently been rebranded and transitioned from the Hyperledger Foundation to the Open Wallet Foundation, reflecting its broader mission and growing community.

Credo has evolved into a more flexible and general-purpose SSI framework. A major milestone in this evolution is the full integration with — making Credo the first framework to support AnonCreds on non-Indy ledgers.

This integration showcases Credo’s expanding support for:

  • Multiple credential formats

  • Diverse DID methods

  • Interoperable ledger technologies

Using Credo, developers can now issue Verifiable Credentials, create Credential Schemas, and define Credential Definitions using cheqd-native AnonCreds, enabled by the cheqd AnonCreds Object Method.

hashtag
Key Features

Credo may be the right choice as it has the following benefits:

  • TypeScript-native — strongly typed, developer-friendly, and production-ready

  • Modular and extensible — use only what you need, plug in new components

  • Cross-ledger compatibility — supports Indy, cheqd, and beyond

hashtag
Breaking New Ground: Credo + cheqd

Credo is now the first framework to support AnonCreds on non-Indy ledgers, thanks to a deep integration with . ACA-Py has since followed suit.

This includes:

  • Issuance of Verifiable Credentials using cheqd DIDs

  • Creation of Credential Schemas and Credential Definitions

  • Support for the cheqd AnonCreds Object Method

hashtag
Get started

Get setup with your Credo agent and begin using cheqd's functionality below:

hashtag
Supported Credential types

Credo supports the following Credential formats:

hashtag
Watch our demo video

hashtag
Enterprise Support for Credo

Below are a list of enterprise applications that leverage Credo with full did:cheqd support under the hood:

Flexible credential support — AnonCreds, W3C JSON-LD, JWT
  • Aligned with EU standards — interoperable with EU ARF and continuing to align with eIDAS 2.0 implementing acts

  • Backed by the Open Wallet Foundation — with growing community adoption

  • Native DID and resource support for did:cheqd
    cheqdarrow-up-right
    cheqdarrow-up-right

    Setup your Credo Agent

    Setup the cheqd agent to start being able to use cheqd inside Credo.

    Create a Decentralized Identifier (DID)

    Use the cheqd DID module to start creating cheqd DIDs.

    Create DID-Linked Resources (DLRs)

    Create generic DID-Linked Resources, as well as schemas and credential definitions.

    Issue a Verifiable Credential

    Issue and present Verifiable Credentials using a cheqd DID.

    AnonCreds

    AnonCreds is a Zero-Knowledge Credential format, offering full Holder privacy and selective disclosure.

    SD-JWT

    Selective Disclosure JWT (SD-JWT) is the most commonly adopted credential format for European Digital Identity Ecosystems, allowing users to selectively disclose which attributes they would like to share in a presentation.

    JSON-LD

    JSON-LD (Linked Data) Credentials are a richer data format, allowing applications to follow embedded links to other pieces of Linked Data across the web.

    Cover

    Paradym (Animo Solutions)

    Paradym by Animo Solutions fully supports EU Architecture & Reference Framework standards with cheqd under the hood.

    Cover

    Hovi Studio (Hovi)

    Hovi provides an all-in-one platform for issuing and managing Verifiable Credentials with cheqd DIDs supported.

    Cover

    Sudo Platform (Anonyome Labs)

    API-first developer platform for cheqd DIDs and support for multiple Verifiable Credential formats.

    Decentralized Identifiers (DIDs)

    Create Decentralized Identifiers (DIDs) on cheqd using Credo.

    A Decentralized Identifier "DID" is a globally unique identifier that does not require a centralized registration authority because it is registered with distributed ledger technology or other form of decentralized network.

    circle-info

    Learn about DIDs If you want to learn about what DIDs are, please go over to our learning site here.arrow-up-right

    hashtag
    Get started

    hashtag
    Alternatives

    Below are a list of alternatives for creating cheqd DIDs.

    Setup Credo Agent

    Setup your Credo Agent to get started.

    This guide walks you through the setup process for a Credo Agent with support for cheqd DIDs and AnonCreds.

    hashtag
    Prerequisites

    Before you begin, make sure you have the following:

    Resolve a DID-Linked Resource

    Resolve a DID-Linked Resource (DLR) on cheqd using the Credo Agent.

    The resolveResource method in the Credo cheqd module enables you to retrieve DID-Linked Resources stored on the cheqd network. These resources can be resolved in two primary ways:

    hashtag
    Resolution Methods

    hashtag
    1. Resolve by Resource ID (UUID)

    Use the full DID + /resources/<resourceId> format to directly resolve a known resource.

    This approach is ideal when you know the exact UUID of the resource.


    hashtag
    2. Resolve by Resource Name and Type

    You can also resolve a resource using a DID URL query format with resourceName and resourceType parameters:

    This method is useful when:

    • You want to resolve the latest version of a known logical resource

    • You don’t have or track the UUID

    hashtag
    3. Fetch the Resource Version Closest to a Specific Time


    hashtag
    4. Alternative parameters

    For a comprehensive list and detailed explanations of these parameters, refer to ADR-005arrow-up-right.

    await agent.modules.cheqd.resolveResource(
      'did:cheqd:testnet:92874297-d824-40ea-8ae5-364a1ec9237d/resources/6de33634-6439-4e46-aa3f-bfe03606b000'
    )
    await agent.modules.cheqd.resolveResource(
      'did:cheqd:testnet:92874297-d824-40ea-8ae5-364a1ec9237d?resourceName=exampleTrustRegistry&resourceType=TrustRegistry'
    )
    await agent.modules.cheqd.resolveResource(
      'did:cheqd:testnet:<uuid>?resourceName=example&resourceType=exampleType&resourceVersionTime=2023-01-01T00:00:00Z'
    )
    Node.js v16 or v18
  • Yarn or npm installed

  • A working Node.js or React Native project

  • Follow the Getting Started guide if you're new to Credo

  • hashtag
    Installing Cheqd

    To enable cheqd in your agent, install the required package:

    This package provides everything needed to register, resolve, and interact with did:cheqd identifiers and AnonCreds objects.

    hashtag
    React Native Support

    If you're using React Native, additional steps are needed to support cosmjs:

    hashtag
    1. Use cosmjs-rn packages

    Update your package.json using npm overrides or Yarn resolutions:

    Example (NPM overrides):

    Example (Yarn resolutions):

    hashtag
    2. Add Buffer Polyfill

    Install the buffer package:

    Create a shim.js file:

    Then, import this shim in your entry point (e.g. before your App.tsx is rendered):

    hashtag
    Adding cheqd to the Agent​arrow-up-right

    After installing the dependencies, we can register the cheqd module on the agent by adding the below code snippet to the agent constructor.

    The cosmosPayerSeed can be a 32-bit seed value or mnemonic. It can be managed using Leap wallet, which is available as a mobile app or browser extension for Chrome and Safari. Leap enables users to create accounts, exchange tokens, etc. To setup Leap wallet for cheqd follow the tutorial below:

    hashtag
    Next steps

    Now that your Credo agent is successfully set up to work with cheqd, try following our tutorials for creating a new DID or issuing Verifiable Credentials.

    Create DID

    Create an Issuer DID with the did:cheqd DID method using Credo.

    Update DID

    Update a did:cheqd DID using Credo.

    Deactivate DID

    Deactivate a did:cheqd DID using Credo.

    Cover

    cheqd Studio

    Our API product enables users to use cheqd's functionality with minimal complexity and easily integrate APIs into existing apps.

    Cover

    DID Registrar

    Simple setup for building cheqd DIDs into existing applications using REST APIs, building into the .

    Cover

    ACA-Py

    ACA-Py plugin supports full cheqd support for DIDs as well as Verifiable Credentials.

    Cover

    Veramo

    The Veramo SDK plugin supports JSON, JSON-LD credentials as well as cheqd Credential Payments in an SDK.

    Cover

    Walt.id Community Stack

    Walt.id Community Stack is an SDK that supports the standards for identity, with full cheqd support.

    Cover

    cheqd Cosmos CLI

    Cosmos CLI which directly communicates with the cheqd network. This should only be used for testing environments.

    Deactivate a DID

    Deactivate a did:cheqd DID using the Credo Agent.

    This guide explains how to deactivate a did:cheqd using a configured Credo Agent. Deactivating a DID marks it as no longer usable but does not remove it from the ledger — the DID can still be resolved and its deactivated state will be reflected in its metadata.

    hashtag
    Prerequisites

    Before deactivating a DID, ensure that:

    yarn add @credo-ts/cheqd
    # or
    npm install @credo-ts/cheqd
    {
      "overrides": {
        "@cosmjs/amino": "npm:@cosmjs-rn/amino@^0.27.1",
        "@cosmjs/encoding": "npm:@cosmjs-rn/encoding@^0.27.1",
        "@cosmjs/math": "npm:@cosmjs-rn/math@^0.27.1",
        "@cosmjs/stargate": "npm:@cosmjs-rn/stargate@^0.27.1",
        "@cosmjs/tendermint-rpc": "npm:@cosmjs-rn/tendermint-rpc@^0.27.1",
        "@cosmjs/utils": "npm:@cosmjs-rn/utils@^0.27.1",
        "@cosmjs/proto-signing": "npm:@cosmjs-rn/proto-signing@^0.27.1",
        "@cosmjs/crypto": "npm:@cosmjs-rn/crypto@^0.27.1"
      }
    }
    {
      "resolutions": {
        "@cosmjs/amino": "npm:@cosmjs-rn/amino@^0.27.1",
        "@cosmjs/encoding": "npm:@cosmjs-rn/encoding@^0.27.1",
        "@cosmjs/math": "npm:@cosmjs-rn/math@^0.27.1",
        "@cosmjs/stargate": "npm:@cosmjs-rn/stargate@^0.27.1",
        "@cosmjs/tendermint-rpc": "npm:@cosmjs-rn/tendermint-rpc@^0.27.1",
        "@cosmjs/utils": "npm:@cosmjs-rn/utils@^0.27.1",
        "@cosmjs/proto-signing": "npm:@cosmjs-rn/proto-signing@^0.27.1",
        "@cosmjs/crypto": "npm:@cosmjs-rn/crypto@^0.27.1"
      }
    }
    bashCopyEdityarn add buffer
    tsCopyEditimport { Buffer } from 'buffer'
    global.Buffer = Buffer
    tsCopyEditimport './shim'
    import { Agent, DidsModule, KeyType, DidDocument } from '@credo-ts/core'
    import { agentDependencies } from '@credo-ts/node'
    import { AskarModule } from '@credo-ts/askar'
    import { askar } from '@openwallet-foundation/askar-nodejs'
    import {
      ConnectionsModule,
      V2ProofProtocol,
      V2CredentialProtocol,
      ProofsModule,
      AutoAcceptProof,
      AutoAcceptCredential,
      CredentialsModule,
      HttpOutboundTransport,
      getDefaultDidcommModules,
    } from '@credo-ts/didcomm'
    import {
      CheqdAnonCredsRegistry,
      CheqdDidRegistrar,
      CheqdDidResolver,
      CheqdModule,
      CheqdModuleConfig,
    } from '@credo-ts/cheqd'
    import { AnonCredsModule } from '@credo-ts/anoncreds'
    import { anoncreds } from '@hyperledger/anoncreds-nodejs'
    
    const agent = new Agent({
      config,
      dependencies: agentDependencies,
      modules: {
        dids: new DidsModule({
          registrars: [new CheqdDidRegistrar()],
          resolvers: [new CheqdDidResolver()],
        }),
    
        // AnonCreds
        anoncreds: new AnonCredsModule({
          registries: [new CheqdAnonCredsRegistry()],
          anoncreds,
        }),
    
        // Add cheqd module
        cheqd: new CheqdModule(
          new CheqdModuleConfig({
            networks: [
              {
                network: '<mainnet or testnet>',
                cosmosPayerSeed: '<cosmos payer seed or mnemonic>',
              },
            ],
          })
        ),
        // Indy VDR can optionally be used with Askar as wallet and storage implementation
        askar: new AskarModule({
          askar,
        }),
        connections: new ConnectionsModule({
          autoAcceptConnections: true,
        }),
        credentials: new CredentialsModule({
          autoAcceptCredentials: AutoAcceptCredential.ContentApproved,
          credentialProtocols: [
            new V2CredentialProtocol({
              credentialFormats: [new LegacyIndyCredentialFormatService(), new AnonCredsCredentialFormatService()],
            }),
          ],
        }),
      },
    })
    Universal Registrararrow-up-right
    European Architecture and Reference Framework (ARF)arrow-up-right

    Leap Wallet

    Manage your CHEQ natively through the Leap Wallet with full support on desktop, browser and mobile.

    Create a DID

    Create an Issuer DID with the did:cheqd DID method using Credo.

    Issue a Verifiable Credential

    Issue a Verifiable Credential (AnonCreds) using Credo signed by a did:cheqd DID.

    Cover

    The Credo agent is configured with cheqd support

  • The DID you are deactivating was created and is controlled by your agent

  • You have access to the signing key used to authorize DID updates

  • hashtag
    Behavior of Deactivated DIDs

    • Deactivated DIDs remain resolvable

    • DID resolvers will indicate that the DID is deactivated

    • The DID and its associated document become immutable and non-functional

    • Deactivation is permanent

    hashtag
    Parameters

    Parameter
    Required
    Description

    did

    ✅

    The full did:cheqd identifier you wish to deactivate

    options

    ❌

    Optional settings, including a versionId to track the update

    hashtag
    Example: Deactivate a cheqd DID


    hashtag
    Notes

    • The optional versionId parameter allows you to assign a custom version identifier to the deactivation transaction

    • Once deactivated, the DID cannot be updated, reactivated, or used for issuing credentials

    • You can still resolve the DID to verify its deactivated status via the deactivated: true metadata

    await agent.dids.deactivate({
      did: 'did:cheqd:testnet:b84817b8-43ee-4483-98c5-f03760816411',
      options: {
        versionId: '3.0', // Optional: for tracking version history or audit purposes
      },
    })

    Create a DID

    Create a did:cheqd DID using the Credo Agent.

    This tutorial shows you how to create a cheqd DID using your configured Credo Agent, and how to publish the associated DID Document to the cheqd ledger.

    hashtag
    ⚠️ Prerequisites

    Before you begin:

    • ✅ Ensure your Credo agent is correctly configured with the @credo-ts/cheqd module

    • ✅ You’ve set up a cosmosPayerSeed for publishing to the cheqd network

    • ✅ You're connected to the correct network (mainnet or testnet)

    hashtag
    Method Overview

    Credo supports two approaches to creating a DID:

    hashtag
    Option 1 – Manually construct a full DID Document

    Use when you want full control over the DID structure and already have key(s) in your wallet.

    hashtag
    Option 2 – Auto-generate the DID Document

    Use when you want Credo to create the DID Document from a key you specify in secret.

    hashtag
    Create DID

    hashtag
    Parameters

    1. method*: cheqd

    2. secret

    3. options

    hashtag
    Option 1: Create a DID from a full DID Document

    1. First, generate a key pair:

    1. Use that key to construct and register the DID Document:

    📝 Make sure the publicKeyBase58 matches the key in your wallet. The DID will be written to the testnet unless you specify "mainnet" in options.network.

    hashtag
    Option 2

    If you don’t want to manually build a DID Document, you can let Credo handle it based on your input key type and ID.

    🔐 Credo will generate the DID Document using the key referenced in secret and publish it to the network.

    hashtag
    What’s Next?

    Now that your DID is live on the cheqd network, try:

    Verifiable Credentials and Presentations

    Issue Verifiable Credentials with Credo, signed by cheqd Decentralized Identifiers (DIDs).

    circle-info

    📚 Learn about Verifiable Credentials and Presentations

    If you want to learn more about what Verifiable Credentials and Presentations are, please go over to our learning site here.arrow-up-right

    Credo provides full support for working with Verifiable Credentials (VCs) and Verifiable Presentations (VPs), based on the standards defined by the W3C and the Aries RFCs.

    Credo enables users to issue, hold, present, and verify credentials in a secure and interoperable way using DIDComm messaging and OpenID for Verifiable Credential protocols. This functionality forms the foundation of any Self-Sovereign Identity (SSI) ecosystem.

    hashtag
    Get started

    Get started issuing and presenting credentials with your Credo agent, either with AnonCreds, JSON-LD or SD-JWT VC:

    hashtag
    Get started

    hashtag
    Credential Format Support

    Credo supports multiple credential formats and exchange protocols out of the box, including:

    hashtag
    ✅ SD-JWT VC

    • Alignment with the European Digital Identity Wallet initiative and standards.

    • Selective disclosure baked in at credential-format level.

    • Issuance and presentation support with OpenID4VCI and OpenID4VP.

    hashtag
    ✅ AnonCreds

    • Ideal for privacy-preserving use cases that require zero-knowledge proofs, selective disclosure, and non-revocation proofs

    • Backed by a credential definition and revocation registry stored on a supported ledger (e.g., cheqd, Indy)

    • Common in enterprise and government deployments

    hashtag
    ✅ W3C Verifiable Credentials (JSON-LD)

    • Standards-compliant with the W3C VC Data Model

    • Extensible for cheqd DID-Linked Resources and Trust Registries

    • Issuance and presentation over DIDComm v2.

    hashtag
    Alternatives

    Below are a list of alternatives for using Credentials with cheqd support. Each offers a different set of protocols and underlying technical capabilities.

    DID-Linked Resources

    Create cheqd DID-Linked Resources (DLRs) linked to cheqd Decentralized Identifiers (DIDs) using Credo.

    DID-Linked Resources are digital files or data blobs that are permanently and verifiably linked to a specific did:cheqd identifier. Each resource is stored on the cheqd ledger and assigned a UUID, called the Resource ID, which acts as a unique, persistent reference.

    Applications can retrieve these resources using either:

    • A fully-qualified Resource ID (DID + /resources/<UUID>)

    • Or a DID URL with query parameters like resourceName and resourceType

    hashtag
    Common Use Cases

    DID-Linked Resources enable storage of a wide variety of DID-associated metadata, including:

    • Trust Registry entries

    • Status Lists (e.g. credential revocation)

    • Logos and brand assets

    • Governance and policy documents

    hashtag
    Get started

    hashtag
    Alternatives

    Below are a list of SDK alternatives for creating cheqd DID-Linked Resources.

    Create an AnonCreds Schema

    Create an AnonCreds Schema as a DID-Linked Resource on cheqd for issuing AnonCreds.

    To issue Verifiable Credentials, the issuer will have to first create a Schema and then a Credential Definition.

    ⚠️ Before you begin...

    Make sure you've correctly configured the Credo agent with cheqd.

    hashtag
    Import a DID (optional)

    In order to register a schema and credential definition, a DID must be added to the agent first. This can be done by calling agent.dids.create(), but this does require an endorser DID to be present as the submitterDid. This is optional, if you have created a DID from the agent before as described .

    hashtag
    Register a Schema

    When you have a registered (or imported) a DID on the network and in your wallet, you can register a schema. Registering a schema requires four fields: issuerId, name, version and attrNames. It is important to note that the issuerId must be the same as a DID in your wallet.

    hashtag
    Parameters

    chevron-rightschema (mandatory)hashtag

    "attrNames": Array of attributes for the schema definition.

    "issuerId": Issuer DID, use the did created by the Issuer.

    "name": Name of the Schema

    "version": Version of the Schema (To update existing Schema, use same name and different version)

    chevron-rightoptions (optional)hashtag

    Optional Key-Value pairs of additional options.

    Update a DID

    Update a did:cheqd DID using the Credo Agent.

    This guide walks you through how to update an existing cheqd DID using your configured Credo Agent. Updating a DID Document lets you add or modify keys, service endpoints, or other metadata associated with the DID.

    hashtag
    Prerequisites

    Before you begin, make sure:

    AnonCreds

    Issue, present and revoke AnonCreds signed by cheqd Decentralized Identifiers (DIDs), using Credo.

    AnonCreds is a privacy-preserving Verifiable Credential format designed for selective disclosure, non-correlatable presentations, and revocation support. In Credo, AnonCreds is fully supported using the Issue Credential v1/v2 and Present Proof v1/v2 protocols.

    Credo allows users to issue, hold, and verify AnonCreds credentials using a range of supported ledgers—including full integration with the cheqd network, a modern, scalable alternative to legacy Indy-based networks.

    hashtag

    Present a Verifiable Credential

    Present a Verifiable Credential, signed by a did:cheqd Decentralized Identifier (DID), using Credo.

    Verifiable Credentials signed by a did:cheqd can be securely presented using the AnonCreds proof format and the Present Proof Protocol v2 within the Credo framework. This enables trust-minimised, selective disclosure of credential data between a Holder and a Verifier.

    hashtag
    Prerequisites

    Before presenting a credential:

    Create an AnonCreds Credential Definition

    Create an Credential Definition as a DID-Linked Resource on cheqd for issuing AnonCreds.

    hashtag
    Register a Credential Definition

    After registering a schema, a credential definition can be registered based on the schema. The credential definition, amongst more things, binds the schema to a specific issuer. Schemas can be reused between issuers, but a credential definition is specific to an issuer. In a credential definition revocation can also be specified. This section will not go in-depth about revocation.

    The request body must contain the credential_definition object with the Issuer DID and the Schema ID created in the previous steps.

    *
  • didDocument

  • Create DID-Linked Resource

    Associate DID-Linked Resources to your DID using Credo.

    Issue Verifiable Credentials

    Issue Credentials using your cheqd DID using Credo.

    ​arrow-up-right
    ​arrow-up-right
    here
    To enable revocation, the options must contain the revocation flag, and the size of the revocation registry. Also, ensure that a Tails Serverarrow-up-right is configured for the issuer.

    hashtag
    Parameters

    chevron-rightcredential_definition (mandatory)hashtag

    "issuerId": DID of the Issuer.

    "schemaId": The AnonCreds Schema ID created by the Issuer (must be of the same DID).

    "tag": Additional Name to identify the Credential Definition.

    chevron-rightoptions (optional)hashtag

    "support_revocation": Boolean value to indicate that the Credential can be revoked or not. (Tails server must be configured if this is set to `true`)

    "revocation_registry_size": Maximum size of the Revocation Registry.

  • A Verifiable Credential must have been issued and accepted by the Holder

  • A Credo Agent is running for both the Verifier and the Holder

  • A DIDComm connection exists between Holder and Verifier (via OOB or another method)

  • Both agents are configured with:

    • @credo-ts/anoncreds

    • @credo-ts/didcomm

  • hashtag
    Step 1: Create a Connection with Holder

    Use any supported method to create a connection with the Holder. Automated out-of-band protocolarrow-up-right is recommended. You can follow the same steps as described in Issue a Verifiable Credential.

    hashtag
    Step 2: Send Proof Request

    After connection is established, the Verifier can send a proof request to the Holder.

    hashtag
    Step 3: Holder sends Presentation Proof

    Holder can get the stored credentials from own wallet and format a proof to send to the Verifier.

    When we want to send a proof, we have to listen to incoming proof requests and handle accordingly. In this example we do not have any user interaction, but is likely that your application would have a user-interface which would display the request.

    hashtag
    What’s Happening Behind the Scenes?

    • The Verifier sends a proof request with specific attributes and credential requirements

    • The Holder uses locally stored credentials to generate a selective disclosure proof

    • The proof is signed using AnonCreds and returned to the Verifier over DIDComm

    • The Verifier cryptographically validates the proof and the issuing DID (did:cheqd)

    hashtag
    Next Steps

    For more tutorials and examples, visit Credo Docsarrow-up-right.

    const key = await agent.wallet.createKey({
      keyType: KeyType.Ed25519,
    })
    
    const ed25519PublicKeyBase58 = key.publicKeyBase58
    import { DidDocument } from '@credo-ts/core'
    
    await agent.dids.create<CheqdDidCreateOptions>({
      method: 'cheqd',
      secret: {}, // No secret needed if key is already in wallet
      options: {
        network: 'testnet',
      },
      didDocument: new DidDocument({
        id: 'did:cheqd:testnet:92874297-d824-40ea-8ae5-364a1ec9237d',
        controller: ['did:cheqd:testnet:92874297-d824-40ea-8ae5-364a1ec9237d'],
        verificationMethod: [
          {
            id: 'did:cheqd:testnet:92874297-d824-40ea-8ae5-364a1ec9237d#key-1',
            type: 'Ed25519VerificationKey2018',
            controller: 'did:cheqd:testnet:92874297-d824-40ea-8ae5-364a1ec9237d',
            publicKeyBase58: ed25519PublicKeyBase58,
          },
        ],
        authentication: [
          'did:cheqd:testnet:92874297-d824-40ea-8ae5-364a1ec9237d#key-1',
        ],
      }),
    })
    await agent.dids.create({
      method: 'cheqd',
      secret: {
        verificationMethod: {
          id: 'key-1', // Logical key name
          type: 'Ed25519VerificationKey2020', // Or another supported type
        },
      },
      options: {
        network: 'testnet',
        methodSpecificIdAlgo: 'uuid', // Optional: 'uuid' (default) or 'base58'
      },
    })
    Issuer
    const seed = TypedArrayEncoder.fromString(`<seed>`) // Secret seed. Should be kept secure in production!
    const cheqdDid = 'did:cheqd:testnet:d37eba59-513d-42d3-8f9f-d1df0548b675' // Cheqd DID to be imported
    
    await agent.dids.import({
      did: cheqdDid,
      overwrite: true,
      privateKeys: [
        {
          privateKey: seed,
          keyType: KeyType.Ed25519,
        },
      ],
    })
    Issuer
    const schemaResult = await agent.modules.anoncreds.registerSchema({
      schema: {
        attrNames: ['name', 'degree', 'date'],
        issuerId: '<did>',
        name: 'Example Schema to register',
        version: '1.0.0',
      },
      options: {},
    })
    
    if (schemaResult.schemaState.state === 'failed') {
      throw new Error(`Error creating schema: ${schemaResult.schemaState.reason}`)
    }
    Issuer
    const credentialDefinitionResult = await agent.modules.anoncreds.registerCredentialDefinition({
      credentialDefinition: {
        tag: 'default',
        issuerId: '<did>',
        schemaId: schemaResult.schemaState.schemaId,
      },
      options: {
        supportRevocation: false,
      },
    })
    
    if (credentialDefinitionResult.credentialDefinitionState.state === 'failed') {
      throw new Error(
        `Error creating credential definition: ${credentialDefinitionResult.credentialDefinitionState.reason}`
      )
    }
    Verifier/Issuer
    await this.agent.modules.proofs.requestProof({
      protocolVersion: 'v2',
      connectionId: connectionRecord.id,
      proofFormats: {
        anoncreds: {
          name: 'proof-request',
          version: '1.0',
          requested_attributes: {
            name: {
              name: 'name',
              restrictions: [
                {
                  cred_def_id: this.credentialDefinition?.credentialDefinitionId,
                },
              ],
            },
          },
        },
      },
    })
    Holder
    this.agent.events.on(ProofEventTypes.ProofStateChanged
      async ({ payload }: ProofStateChangedEvent) => {
        if (payload.proofRecord.state === ProofState.RequestReceived) {
          const requestedCredentials = await this.agent.modules.proofs.selectCredentialsForRequest({
            proofRecordId: payload.proofRecord.id,
          })
          await this.agent.modules.proofs.acceptRequest({
            proofRecordId: proofRecord.id,
            proofFormats: requestedCredentials.proofFormats,
          })
        }
    })
    Suitable for web-native and mobile-first use cases

    Setup Credo Agent

    Before you get started, you need to setup your Credo Agent.

    AnonCreds

    Issue and present AnonCreds signed by cheqd DIDs using Credo.

    JSON-LD

    Issue and present JSON-LD Verifiable Credentials signed by cheqd DIDs using Credo.

    SD-JWT VC

    Issue and present SD-JWT Verifiable Credentials signed by cheqd DIDs using Credo.

    Cover

    cheqd Studio

    Our API product enables users to use cheqd's functionality with minimal complexity and easily integrate APIs into existing apps.

    Cover

    ACA-Py

    ACA-Py plugin supports full cheqd support for DIDs as well as Verifiable Credentials.

    Cover

    Veramo

    The Veramo SDK plugin supports JSON, JSON-LD credentials as well as cheqd Credential Payments in an SDK.

    Cover

    Walt.id Community Stack

    Walt.id Community Stack is an SDK that supports the standards for identity, with full cheqd support.

  • Credential Schemas and Definitions

  • Create DID-Linked Resource

    Create a generic DID-Linked Resource (DLR) tied to a cheqd DID using Credo.

    Understanding DLRs

    More details about DID-Linked Resources on Cheqd.

    Understanding AnonCreds

    More details about AnonCreds Object Method.

    Create Schema

    Create an AnonCreds Schema using Credo.

    Create Credential Definition

    Create an AnonCreds Credential Definition using Credo.

    Cover

    cheqd Studio

    Our API product enables users to use cheqd's functionality with minimal complexity and easily integrate APIs into existing apps.

    Cover

    DID Registrar

    Simple setup for building cheqd DIDs into existing applications using REST APIs, building into the .

    Cover

    Veramo

    The Veramo SDK plugin supports JSON, JSON-LD credentials as well as cheqd Credential Payments in an SDK.

    Cover

    cheqd Cosmos CLI

    Cosmos-native CLI for creating transactions on cheqd. Should be used for testing purposes only.

    Cover

    ACA-Py

    ACA-Py (Aries Cloud Agent Python) is an SDK that can issue AnonCreds, JSON-LD and SD-JWT credentials with full cheqd support.

    ✅ Your Credo agent is already configured with cheqd support
  • ✅ You have write access to the DID (i.e., the key used for DID creation or a controller key)

  • ✅ You know the DID you want to update and its current document state

  • hashtag
    How DID Updates Work

    To update a did:cheqd, you must:

    1. Fetch the current DID Document

    2. Modify the relevant fields (e.g., add keys, update service endpoints)

    3. Submit the updated DID Document using agent.dids.update(...)

    hashtag
    Example: Add a New Service and Verification Method


    hashtag
    Parameters Reference

    Parameter
    Required
    Description

    did

    ✅

    The DID you want to update

    didDocument

    ✅

    The complete updated DID Document

    secret

    ⚠️

    Required only if you're referencing new keys not yet in the agent’s wallet

    options

    ❌

    Get started

    Issue, present and revoke AnonCreds signed by cheqd Decentralized Identifiers (DIDs), using the tutorials below:


    hashtag
    Why Use AnonCreds?

    AnonCreds credentials are purpose-built for high-assurance, privacy-respecting use cases that require:

    • Selective disclosure of individual claims

    • Zero-knowledge proofs

    • Revocation support via AnonCreds Status Lists

    • Credential definitions and revocation registries anchored on-ledger


    hashtag
    AnonCreds + cheqd Support

    Credo now supports did:cheqd for issuing AnonCreds credentials using the cheqd AnonCreds Object Method. This removes dependency on Indy-based networks while retaining the proven AnonCreds credential exchange flow.

    hashtag
    With Credo + cheqd, you can:

    • ✅ Register did:cheqd identifiers for use in credential issuance

    • ✅ Publish Schemas and Credential Definitions as DID-Linked Resources

    • ✅ Issue and verify AnonCreds credentials signed with did:cheqd

    • ✅ Enable revocation using AnonCreds Status List Definitions and Entries, also stored on-ledger as DID-Linked Resources

    ✅ This allows developers to migrate off the deprecated Sovrin network or other Indy networks without changing their existing flows or protocols.


    hashtag
    Features Supported in Credo

    Feature
    Supported in ACA-Py

    Issue AnonCreds credentials

    ✅ Yes

    Present and verify credentials

    ✅ Yes

    Revocation support via cheqd AnonCreds Status Lists

    ✅ Yes

    DID-Linked Resource anchoring (schemas, defs, status)

    ✅ Yes

    Use did:cheqd for AnonCreds

    ✅ Yes

    Use other Indy-compatible ledgers

    ✅ Yes

    hashtag
    AnonCreds Object Method

    Take a deep dive into the cheqd AnonCreds Object Method below:

    Create a DID-Linked Resource

    Create a DID-Linked Resource (DLR) on cheqd using the Credo Agent.

    The createResource method from the Credo cheqd module allows you to create a DID-Linked Resource and publish it to the cheqd network under a did:cheqd identifier.

    DID-Linked Resources are uniquely identified by a UUIDv4 resource ID and persistently attached to the DID on-chain. These resources can later be resolved by ID or query parameters (e.g., resourceName, resourceType).

    hashtag
    Required Parameters

    Field
    Required
    Description

    hashtag
    Example

    💡 The id field must be a UUIDv4. You are responsible for generating and tracking this ID.

    hashtag
    Recommendations

    • Ensure the id is a UUIDv4, generated using a reliable UUID library (e.g., uuid in Node.js)

    • Keep resourceName and resourceType descriptive but concise — they are used for resolution

    Issue a Verifiable Credential

    Issue a Verifiable Credential (AnonCreds), signed with a did:cheqd Decentralized Identifier (DID).

    Using AnonCreds and the Issue Credential v2 Protocol, you can issue Verifiable Credentials signed by a did:cheqd identifier with just a few lines of code. This guide walks through the full flow using the Credo Agent.

    hashtag
    Prerequisites

    Before you begin, make sure you have:

    • A registered did:cheqd for the Issuer.

    • A and already created and published as DID-Linked Resources.

    • A configured with:

      • @credo-ts/cheqd for DID operations and resource publishing.

      • @credo-ts/anoncreds for AnonCreds credential handling.

    • Two agents: an Issuer and a Holder (can be separate apps or run locally)

    • Secure connectivity between agents using Out-of-Band (OOB) or a supported connection method.

    hashtag
    Step 1: Create a Connection with Holder

    Use any supported method to create a connection with the Holder of the credential. Automated is recommended.

    hashtag
    1a: Issuer Creates Connection Invite

    The Issuer agent will create a new connection invite for the Holder. This is needed to securely communicate between the Issuer and the Holder agents.

    hashtag
    1b: Holder receives invitation

    The above request will have an invitation in the response. Holder will have to copy that invitation and pass URL as invitationUrl in the following code:

    hashtag
    Step 2: Prepare and Send Credential Offer

    Generate a credential offer and send to the holder, informing them about the available credential and its attributes.

    hashtag
    Step 3: Holder accepts credential offer

    When we want to accept a credential, we have to listen to incoming credentials and handle accordingly. In this example we do not have any user interaction, but is likely that your application would have a user-interface which would display the credential. When receiving a credential offer you can get the values from credentialExchangeRecord.credentialAttributes.

    hashtag
    Summary

    Step
    Action

    SD-JWT VC

    Issue and present SD-JWT VC credentials signed by cheqd Decentralized Identifiers (DIDs), using Credo.

    SD-JWT Verifiable Credentials enable selective disclosure and unlinkability, combining the compact JWT format with privacy-preserving cryptographic techniques. They are optimized for privacy, mobile compatibility, and integration with modern identity standards.

    In Credo, SD-JWT credentials are issued using OpenID for Verifiable Credential Issuance (OID4VCI) and presented using OpenID for Verifiable Presentations (OID4VP)—ensuring secure, interoperable credential exchange based on open standards.

    ✅ This approach is fully aligned with the EU Digital Identity Wallet standards and protocols, and the developing EU Architecture and Reference Framework (ARF).


    JSON-LD

    Issue and present JSON-LD credentials signed by cheqd Decentralized Identifiers (DIDs), using Credo.

    JSON-LD Verifiable Credentials follow the , enabling decentralized, semantically rich, and interoperable credential exchange.

    In Credo, you can issue, verify, and present JSON-LD credentials over DIDComm v2, using the Issue Credential v2 and Present Proof v2 protocols—alongside support for Linked Data Proofs and various signature suites.

    hashtag
    Get Started

    import { DidDocumentService } from '@credo-ts/core'
    
    await agent.dids.update({
      did: 'did:cheqd:testnet:b84817b8-43ee-4483-98c5-f03760816411',
    
      // Used to authorize and derive additional keys, if needed
      secret: {
        verificationMethod: {
          id: 'key-2',
          type: 'JsonWebKey2020', // Can also be Ed25519VerificationKey2020, etc.
        },
      },
    
      didDocument: {
        id: 'did:cheqd:testnet:b84817b8-43ee-4483-98c5-f03760816411',
        controller: ['did:cheqd:testnet:b84817b8-43ee-4483-98c5-f03760816411'],
    
        verificationMethod: [
          {
            id: 'did:cheqd:testnet:b84817b8-43ee-4483-98c5-f03760816411#key-1',
            type: 'Ed25519VerificationKey2020',
            controller: 'did:cheqd:testnet:b84817b8-43ee-4483-98c5-f03760816411',
            publicKeyMultibase: 'z6MknkzLUEP5cxqqsaysNMWoh8NJRb3YsowTCj2D6yhwyEdj',
          },
        ],
    
        authentication: [
          'did:cheqd:testnet:b84817b8-43ee-4483-98c5-f03760816411#key-1',
        ],
    
        service: [
          new DidDocumentService({
            id: 'did:cheqd:testnet:b84817b8-43ee-4483-98c5-f03760816411#website',
            type: 'linkedDomains',
            serviceEndpoint: 'https://rand.in',
          }),
        ],
      },
    })

    Optional advanced settings (e.g., method-specific configurations)

    Issue AnonCreds Credentials

    Issue AnonCreds Credentials signed by cheqd DIDs using Credo.

    Present AnonCreds Credentials

    Present AnonCreds Credentials using Credo.

    cheqd AnonCreds Object Method

    Understand how cheqd supports AnonCreds Objects using DID-Linked Resources.

    European Architecture and Reference Framework (ARF)arrow-up-right
    Universal Registrararrow-up-right
    cheqd and Animo AnonCreds demo

    data

    ✅

    The content to store (object, Base64-encoded string, or plain text)

    version

    ❌

    Optional semantic version string (e.g. "1.0")

    alsoKnownAs

    ❌

    Optional array of aliases for the resource

    If the resource data is sensitive or large, consider encoding as a base64 string
  • Use version to manage changes to the resource over time

  • did

    ✅

    The did:cheqd identifier under which the resource is created

    id

    ✅

    A UUID string that acts as the permanent resource ID

    name

    ✅

    A human-readable resource name

    resourceType

    ✅

    A short type identifier (e.g. TrustRegistry, Logo, StatusList)

    @credo-ts/didcomm for DIDComm messaging

    1

    Create and accept a secure connection between Issuer and Holder

    2

    Issuer prepares and sends a credential offer (via AnonCreds v2)

    3

    Holder accepts and stores the credential automatically or via UI

    Decentralized Identifier (DID)
    Credential Schema
    Credential Definition
    Credo Agent
    out-of-band protocolarrow-up-right
    await agent.modules.cheqd.createResource(
      'did:cheqd:testnet:92874297-d824-40ea-8ae5-364a1ec9237d',
      {
        id: '6de33634-6439-4e46-aa3f-bfe03606b000',
        name: 'exampleTrustRegistry',
        resourceType: 'TrustRegistry',
        version: '1.0',
        data: {
          name: 'Example Org',
          jurisdiction: 'EU',
        },
      }
    )
    Issuer
    const createNewInvitation = async (agent: Agent) => {
      const outOfBandRecord = await agent.modules.oob.createInvitation()
    
      return {
        invitationUrl: outOfBandRecord.outOfBandInvitation.toUrl({ domain: 'http://localhost:3001' }),
        outOfBandRecord,
      }
    }
    Holder
    const receiveInvitation = async (agent: Agent, invitationUrl: string) => {
      const { outOfBandRecord } = await agent.modules.oob.receiveInvitationFromUrl(invitationUrl)
      if (!outOfBandRecord) {
          throw new Error(redText(Output.NoConnectionRecordFromOutOfBand))
      }
      return outOfBandRecord
    }
    Issuer
    await this.agent.modules.credentials.offerCredential({
          connectionId: connectionRecord.id,
          protocolVersion: 'v2',
          credentialFormats: {
            anoncreds: {
              attributes: [
                {
                  name: 'name',
                  value: 'Alice Smith',
                },
                {
                  name: 'degree',
                  value: 'Computer Science',
                },
                {
                  name: 'date',
                  value: '01/01/2022',
                },
              ],
              credentialDefinitionId: credentialDefinition.credentialDefinitionId,
            },
          },
        })
    Holder
    this.agent.events.on(CredentialEventTypes.CredentialStateChanged,
      async ({ payload }: CredentialStateChangedEvent) => {
      switch (payload.credentialRecord.state) {
        case CredentialState.OfferReceived:
          console.log('received a credential')
          // custom logic here
          await this.agent.modules.credentials.acceptOffer({ credentialRecordId: payload.credentialRecord.id })
          break
        case CredentialState.Done:
          console.log(`Credential for credential id ${payload.credentialRecord.id} is accepted`)
          // For demo purposes we exit the program here.
          process.exit(0)
      }
    })
    hashtag
    Get Started

    Issue and present SD-JWT credentials in Credo using the tutorials below:


    hashtag
    Why Use SD-JWT?

    SD-JWT credentials are ideal for ecosystems that require:

    • Privacy-preserving credential exchange Reveal only the claims you choose—no more, no less

    • Unlinkability across presentations Each presentation is cryptographically unique, preventing correlation by relying parties

    • Optimized for mobile and constrained environments Compact format, ideal for mobile wallets and low-bandwidth scenarios

    • Standards-compliant Built on IETF SD-JWT, OID4VCI, and OID4VP specifications

    • Interoperability with identity wallets Enables seamless interaction with both SSI wallets and federated identity providers supporting OpenID standards

    • EU Wallet-ready Fully compatible with the EU Digital Identity Wallet and ARF requirements


    hashtag
    Extensibility for cheqd Functionality

    While SD-JWT focuses on minimal disclosure, it can integrate cheqd-native functionality through linked references:

    🔗 DID-Linked Resources Reference schemas, legal terms, or trust frameworks hosted on the cheqd ledger via out-of-band metadata Use credential_metadata or presentation definitions to point to these resources

    🏛️ Trust Registries Issuers can prove authorisation by referencing cheqd-based trust registries—linked via DID-Linked Resources or OpenID Federation metadata

    🚫 Status Lists (Revocation) Revocation is supported via cheqd-compatible Bitstring Status Lists These can be referenced externally without compromising SD-JWT’s privacy guarantees

    📦 These integrations maintain SD-JWT’s compact, privacy-first design while adding verifiability and governance via cheqd infrastructure.


    hashtag
    Features Supported in Credo

    hashtag
    Supported DID Methods

    Credo supports SD-JWT credential issuance and key binding using:

    • did:key

    • did:web

    • did:cheqd

    hashtag
    Supported Protocols

    • Issuance: OID4VCI (OpenID for Verifiable Credential Issuance)

    • Presentation: OID4VP (OpenID for Verifiable Presentations)

    Issue and present JSON-LD credentials with Credo using the tutorials below:

    hashtag
    Why Use JSON-LD?

    JSON-LD credentials are ideal for ecosystems that require:

    • Standards-aligned, interoperable credentials

    • Richly structured, linked data

    • Flexible signature types (e.g. Ed25519, BBS+)

    • Integration with W3C-conformant wallets and identity providers

    • Compatibility with OpenID for Verifiable Credentials (OID4VC) and EU Digital Identity Wallet initiatives


    hashtag
    Extensibility for cheqd Functionality

    Thanks to the flexibility of the W3C VC data model, JSON-LD credentials can be extended to support advanced cheqd-native features, including:

    hashtag
    🔗 DID-Linked Resources

    • Credentials can reference schemas, governance frameworks, or legal documents published as DID-Linked Resources on the cheqd ledger

    • Use the termsOfUse or credentialSchema fields to point to cheqd-hosted resources

    hashtag
    🏛️ Trust Registries

    • A credential can reference a trust registry (also a DID-Linked Resource) that defines the rules, requirements, or authorisations under which the credential was issued

    hashtag
    🚫 Status Lists (Revocation)

    • cheqd-compatible Bistring Status List entries can be referenced using the credentialStatus field, enabling scalable and private revocation

    • These status lists can also be stored as DID-Linked Resources on cheqd

    🔧 These extensions follow W3C patterns and require no change to the base data model—making them portable, verifiable, and standards-compliant.


    hashtag
    Features Supported in Credo

    Feature
    Supported in ACA-Py

    Issue JSON-LD credentials

    ✅ Yes

    Present and verify JSON-LD credentials

    ✅ Yes

    Support for multiple proof types

    ✅ Yes (Ed25519Signature2018, Ed25519Signature2020, etc.)

    Use of external or on-chain contexts

    ✅ Yes

    Support for DID-Linked Resources

    ⚠️ Via extensions and custom fields


    hashtag
    Supported DID Methods

    Credo natively supports signing JSON-LD credentials using DIDs such as:

    • did:key

    • did:web

    • did:cheqd

    W3C Verifiable Credentials Data Modelarrow-up-right

    Present a Verifiable Credential

    Receive and present a SD-JWT Verifiable Credential, signed by a did:cheqd Decentralized Identifier (DID), using Credo and OpenID4VP.

    Using the OpenID4VC module, you can receive and present a OpenID compatible SD-JWT Credentials signed by a did:cheqd identifier. This guide walks through the flow of presentation using the Credo Agent.

    hashtag
    Prerequisites

    Before you being, ensure you have:

    • Basic knowledge of .

    • .

    hashtag
    Step 1: Configure the Holder Agent

    Configure the holder with cheqd and OpenID4VC Modules

    hashtag
    Step 2: Resolve and Accept Credential Offer

    This method:

    1. Resolves the offer.

    2. Accepts it via pre-authorized code flow.

    3. Selects a binding method—did:key (preferred) or JWK—for the SD-JWT, depending on issuer capabilities

    hashtag
    Step 3: Store the Credentials

    hashtag
    Step 4: Resolve and Accept Authorization Request

    Once you have a credential in your wallet, you can present it by responding to a receive authorization request, which includes an OpenID4VP presentation request. This request can be generated either by the verifier module or an external OpenID4VC verifier. First, resolve the authorization request, then accept it to present the credential in your wallet.

    Verify a Verifiable Credential

    Verify a SD-JWT Verifiable Credential, signed by a did:cheqd Decentralized Identifier (DID), using Credo and OpenID4VP.

    Using the OpenID4VC module, you can verify OpenID compatible SD-JWT Credentials signed by a did:cheqd identifier. This guide walks through the flow of verification using the Credo Agent.

    hashtag
    Prerequisites

    Before you being, ensure you have:

    • Basic knowledge of .

    • .

    hashtag
    Step 1: Configure the Verifier Agent

    After setting the verifier as , configure the verifier as follows

    hashtag
    Step 2: Create an Authorization Request

    Once you have configured the verifier, you can create an authorization request including an OpenID4VP presentation request based on . The authorization request method will generate an authorization request URI that you can share with a holder.

    hashtag
    Step 3: Add Event Listener to check Verification State Change

    Add an event listener that listens for state changed events, this allows Verifier to know when the authorization session is complete.

    Present a Verifiable Credential

    Present a JSON-LD Verifiable Credential, signed by a did:cheqd Decentralized Identifier (DID), using Credo.

    Verifiable Credentials signed by a did:cheqd can be securely presented using the Dif proof format and the Present Proof Protocol v2 within the Credo framework. This enables trust-minimised, selective disclosure of credential data between a Holder and a Verifier.

    hashtag
    Prerequisites

    Before presenting a credential:

    • A Verifiable Credential must have been issued and accepted by the Holder

    • A Credo Agent is running for both the Verifier and the Holder

    • A DIDComm connection exists between Holder and Verifier (via OOB or another method)

    hashtag
    Step 1: Create a Connection with Holder

    Use any supported method to create a connection with the Holder. Automated is recommended. You can follow the same steps as described in .

    hashtag
    Step 2: Register Proof Event Listeners

    Both agents need event listeners to handle the proof exchange protocol automatically.

    Step 3: Send Proof Request

    After the connection is established and event handlers registered, the Verifier can send a proof request to the Holder.

    The Proof Acceptance and Presentation is handled automatically by the event listeners registered for both Verifier and Holder.

    hashtag
    Next Steps

    For more tutorials and examples, visit .

    Issue a Verifiable Credential

    Issue SD-JWT Credentials signed by cheqd DIDs using Credo and OpenID4VCI.

    Present a Verifiable Credential

    Present SD-JWT Credentials signed by cheqd DIDs using Credo and OpenID4VP.

    Verify a Verifiable Credential

    Verify SD-JWT Credentials signed by cheqd DIDs using Credo and OpenID4VCI.

    Issue JSON-LD Credentials

    Issue JSON-LD Credentials signed by cheqd DIDs using Credo.

    Present JSON-LD Credentials

    Present JSON-LD Credentials using Credo.

    Credo Agent configurationarrow-up-right
    Issued a SD-JWT Verifiable Credential
    Credo Agent configurationarrow-up-right
    Issued a SD-JWT Verifiable Credential
    Presented a SD-JWT Verifiable Credential
    per these steps
    DIF Presentation Exchange V2arrow-up-right
    import { Agent, DidsModule } from '@credo-ts/core';
    import { agentDependencies } from '@credo-ts/node';
    import { CheqdModule } from '@credo-ts/cheqd';
    import { OpenId4VcHolderModule } from '@credo-ts/openid4vc';
    
    const holder = new Agent({
      config,
      dependencies: agentDependencies,
      modules: {
        dids: new DidsModule({
          resolvers: [new CheqdDidResolver()],
        }),
        openId4VcHolderModule: new OpenId4VcHolderModule(),
      },
    });
    import { DidKey, KeyDidCreateOptions, getJwkFromKey } from '@credo-ts/core';
    
    const resolved = await holder.modules.openId4VcHolder.resolveCredentialOffer(credentialOffer);
    
    const credentials = await holder.modules.openId4VcHolder.acceptCredentialOfferUsingPreAuthorizedCode(
      resolved,
      {
        credentialBindingResolver: async ({
          supportedDidMethods,
          keyType,
          supportsAllDidMethods,
          supportsJwk,
          credentialFormat,
        }) => {
          if (supportsAllDidMethods || supportedDidMethods?.includes('did:key')) {
            const didResult = await holder.dids.create<KeyDidCreateOptions>({
              method: 'key',
              options: { keyType },
            });
            const didKey = DidKey.fromDid(didResult.didState.did);
            return {
              method: 'did',
              didUrl: `${didKey.did}#${didKey.key.fingerprint}`,
            };
          }
          if (supportsJwk && credentialFormat === OpenId4VciCredentialFormatProfile.SdJwtVc) {
            const key = await holder.wallet.createKey({ keyType });
            return { method: 'jwk', getJwkFromKey(key) };
          }
          throw new Error('No binding method supported.');
        },
      }
    );
    
    // Store the received credentials
    const records: Array<W3cCredentialRecord | SdJwtVcRecord> = []
    for (const credential of credentials) {
      if ('compact' in credential) {
        const record = await holder.sdJwtVc.store(credential.compact)
        records.push(record)
      } else {
        const record = await holder.w3cCredentials.storeCredential({
          credential,
        })
        records.push(record)
      }
    }
    // resolved credential offer contains the offer, metadata, etc..
    const resolvedRequest = await holder.modules.openId4VcHolderModule.resolveSiopAuthorizationRequest(authorizationRequest)
    console.log(
      'Resolved credentials for request', JSON.stringify(resolvedRequest.presentationExchange.credentialsForRequest, null, 2)
    )
    
    const presExchangeService = holder.dependencyManager.resolve(DifPresentationExchangeService)
    // Automatically select credentials. In a wallet you could manually choose which credentials to return based on the "resolvedAuthorizationRequest.presentationExchange.credentialsForRequest" value
    const selectedCredentials = presExchangeService.selectCredentialsForRequest(
      resolvedRequest.presentationExchange.credentialsForRequest
    )
    
    // issuer only supports pre-authorized flow for now
    const authorizationResponse = await holder.modules.openId4VcHolderModule.acceptSiopAuthorizationRequest({
      authorizationRequest: resolvedRequest.authorizationRequest,
      presentationExchange: {
        credentials: selectedCredentials,
      },
    })
    console.log('Submitted authorization response', JSON.stringify(authorizationResponse.submittedResponse, null, 2))
    // Create a verifier, assuming the agent is called 'verifier'
    const openId4VcVerifier = await verifier.modules.openId4VcVerifier.createVerifier({})
    
    // Create a did:key that we will use for signing OpenID4VP authorization requests
    const verifierDidResult = await issuer.dids.create<KeyDidCreateOptions>({
      method: 'key',
      options: {
        keyType: KeyType.Ed25519,
      },
    })
    
    if (verifierDidResult.didState.state !== 'finished') {
      throw new Error('DID creation failed.')
    }
    
    const verifierDidKey = DidKey.fromDid(verifierDidResult.didState.did)
    const { authorizationRequest, verificationSession } =
      await verifier.modules.openId4VcVerifier.createAuthorizationRequest({
        verifierId: openId4VcVerifier.verifierId,
        requestSigner: {
          didUrl: `${verifierDidKey.did}#${verifierDidKey.key.fingerprint}`,
          method: 'did',
        },
        // Add DIF presentation exchange data
        presentationExchange: {
          definition: {
            id: '9ed05140-b33b-445e-a0f0-9a23aa501868',
            name: 'Employee Verification',
            purpose: 'We need to verify your employee status to grant access to the employee portal',
            input_descriptors: [
              {
                id: '9c98fb43-6fd5-49b1-8dcc-69bd2a378f23',
                constraints: {
                  // Require limit disclosure
                  limit_disclosure: 'required',
                  fields: [
                    {
                      filter: {
                        type: 'string',
                        const: 'AcmeCorpEmployee',
                      },
                      path: ['$.vct'],
                    },
                  ],
                },
              },
            ],
          },
        },
      })
    // Listen and react to changes in the verification session
    verifier.events.on<OpenId4VcVerificationSessionStateChangedEvent>(
      OpenId4VcVerifierEvents.VerificationSessionStateChanged,
      async (event) => {
        if (event.payload.verificationSession.id === verificationSession.id) {
          console.log('Verification session state changed to ', event.payload.verificationSession.state)
        }
    
        if (event.payload.verificationSession.state === OpenId4VcVerificationSessionState.ResponseVerified) {
          const verifiedAuthorizationResponse = await verifier.modules.openId4VcVerifier.getVerifiedAuthorizationResponse(
            verificationSession.id
          )
          console.log('Successfully verified presentation.', JSON.stringify(verifiedAuthorizationResponse, null, 2))
    
          console.log('Exiting...')
          process.exit()
        }
      }
    )
    Both agents are configured with needed for JSON-LD Credential Proof.
    out-of-band protocolarrow-up-right
    Issue a Verifiable Credential
    Credo Docsarrow-up-right
    Verifier and Holder
    import { ProofEventTypes, ProofState } from '@credo-ts/core'
    
    const setupProofListener = (agent: Agent) => {
      agent.events.on(ProofEventTypes.ProofStateChanged, async ({ payload }) => {
        const { proofRecord } = payload
        
        switch (proofRecord.state) {
          case ProofState.RequestReceived:
            console.log('Holder: Proof request received, creating presentation...')
            const requestedCredentials = await agent.proofs.selectCredentialsForRequest({
              proofRecordId: proofRecord.id,
            })
            
            await agent.proofs.acceptRequest({
              proofRecordId: proofRecord.id,
              proofFormats: {
                presentationExchange: {
                  credentials: requestedCredentials.proofFormats['presentation-exchange']?.credentials || {},
                },
              },
            })
            break
            
          case ProofState.PresentationReceived:
            console.log('Issuer: Presentation received, verifying...')
            await agent.proofs.acceptPresentation({
              proofRecordId: proofRecord.id,
            })
            break
            
          case ProofState.Done:
            console.log('Proof verification completed!')
            const proof = await agent.proofs.getById(proofRecord.id)
            console.log('Proof is valid:', proof.isVerified)
            break
        }
      })
    }
    Verifier/Issuer
    // Define what we want to verify
    const presentationDefinition = {
      id: 'permanent-resident-verification',
      input_descriptors: [
        {
          id: 'permanent-resident-card',
          name: 'Permanent Resident Card',
          purpose: 'Verify permanent resident status',
          constraints: {
            fields: [
              {
                path: ['$.type'],
                filter: {
                  type: 'array',
                  contains: { const: 'PermanentResidentCard' },
                },
              },
              {
                path: ['$.credentialSubject.givenName'],
                filter: { type: 'string' },
              },
              {
                path: ['$.credentialSubject.familyName'],
                filter: { type: 'string' },
              },
              {
                path: ['$.credentialSubject.lprNumber'],
                filter: { type: 'string' },
              },
            ],
          },
        },
      ],
    }
    // Get Connection
    const {invitationUrl, issuerConnection} = await createInvitation(issuerAgent)
    
    console.log('Requesting proof verification...')
    const proofExchange = await issuerAgent.proofs.requestProof({
      connectionId: issuerConnection.id,
      protocolVersion: 'v2',
      proofFormats: {
        presentationExchange: { presentationDefinition },
      },
      comment: 'Please present your Permanent Resident Card for verification',
    })
    console.log('Proof request sent:', proofExchange.id)
    // The rest of the flow is handled automatically by event listeners
    Credo packages

    Issue a Verifiable Credential

    Issue a JSON-LD Verifiable Credential, signed by a did:cheqd Decentralized Identifier (DID), using Credo.

    Using the Issue Credential v2 Protocol, you can issue JSON-LD Verifiable Credentials signed by a did:cheqd identifier with just a few lines of code. This guide walks through the full flow using the Credo Agent.

    hashtag
    Prerequisites

    Before you begin, ensure you have:

    • Basic knowledge of .

    • A cheqd testnet or mainnet account with sufficient tokens for DID operations

    hashtag
    Step 1: Install dependencies

    hashtag
    Step 2: Set up the Issuer Agent

    The issuer agent requires the cheqd module for DID operations and additional modules for W3C JSON-LD credential processing.

    hashtag
    Step 3: Set up the Holder Agent

    The holder agent needs to resolve cheqd DIDs and handle JSON-LD credentials.

    hashtag
    Step 4: Create Issuer DID

    hashtag
    Step 5: Create connection between Issuer and Holder

    Use any supported method to create a connection with the Holder of the credential. Automated is recommended.

    hashtag
    5a: Issuer creates Connection Invite

    The Issuer agent will create a new connection invite for the Holder. This is needed to securely communicate between the Issuer and the Holder agents.

    hashtag
    5b: Holder receives invitation

    The above request will have an invitation in the response. Holder will have to copy that invitation and pass URL as invitationUrl in the following code:

    hashtag
    Step 6: Set up Credential Event Listeners

    Both agents need event listeners to handle the credential exchange protocol automatically.

    hashtag
    Step 7: Holder proposes the Credential

    In this example, we will initiate the credential issuance process by having the holder propose a credential.

    The Credential Acceptance and storage is handled automatically by the event listeners registered for both Issuer and Holder.

    Credo Agent configuration
    out-of-band protocolarrow-up-right
    npm install @credo-ts/core @credo-ts/node @credo-ts/askar @credo-ts/cheqd
    npm install @hyperledger/aries-askar-nodejs
    Issuer
    import type { InitConfig } from '@credo-ts/core'
    import { AskarModule } from '@credo-ts/askar'
    import {
      Agent,
      CredentialsModule,
      V2CredentialProtocol,
      JsonLdCredentialFormatService,
      DidsModule,
      HttpOutboundTransport,
      WsOutboundTransport,
      ProofsModule,
      V2ProofProtocol,
      DifPresentationExchangeProofFormatService,
      CacheModule,
      InMemoryLruCache,
      W3cCredentialsModule,
      KeyType,
      DidDocumentBuilder,
      utils,
      getEd25519VerificationKey2018,
    } from '@credo-ts/core'
    import { agentDependencies, HttpInboundTransport } from '@credo-ts/node'
    import { ariesAskar } from '@hyperledger/aries-askar-nodejs'
    import { CheqdModule, CheqdModuleConfig, CheqdDidRegistrar, CheqdDidResolver } from '@credo-ts/cheqd'
    
    let issuerDid: string
    
    const issuerConfig: InitConfig = {
      label: 'cheqd-jsonld-issuer',
      walletConfig: {
        id: 'cheqd-issuer-wallet',
        key: 'testkey0000000000000000000000000',
      },
    }
    
    const initializeIssuerAgent = async () => {
      const issuer = new Agent({
        config: issuerConfig,
        dependencies: agentDependencies,
        modules: {
          askar: new AskarModule({ ariesAskar }),
          dids: new DidsModule({
            registrars: [new CheqdDidRegistrar()],
            resolvers: [new CheqdDidResolver()],
          }),
          cheqd: new CheqdModule(
            new CheqdModuleConfig({
              networks: [
                {
                  network: 'testnet', // or 'mainnet'
                  cosmosPayerSeed: 'your-cosmos-payer-seed-here',
                },
              ],
            })
          ),
          credentials: new CredentialsModule({
            credentialProtocols: [
              new V2CredentialProtocol({
                credentialFormats: [new JsonLdCredentialFormatService()],
              }),
            ],
          }),
          proofs: new ProofsModule({
            proofProtocols: [
              new V2ProofProtocol({
                proofFormats: [new DifPresentationExchangeProofFormatService()],
              }),
            ],
          }),
          cache: new CacheModule({
            cache: new InMemoryLruCache({ limit: 100 }),
          }),
          w3cCredentials: new W3cCredentialsModule({}),
        },
      })
    
      // Register transports
      issuer.registerOutboundTransport(new WsOutboundTransport())
      issuer.registerOutboundTransport(new HttpOutboundTransport())
      issuer.registerInboundTransport(new HttpInboundTransport({ port: 3001 }))
    
      await issuer.initialize()
      return issuer
    }
    Holder
    const holderConfig: InitConfig = {
      label: 'cheqd-jsonld-holder',
      walletConfig: {
        id: 'cheqd-holder-wallet',
        key: 'testkey0000000000000000000000000',
      },
    }
    
    const initializeHolderAgent = async () => {
      const holder = new Agent({
        config: holderConfig,
        dependencies: agentDependencies,
        modules: {
          askar: new AskarModule({ ariesAskar }),
          dids: new DidsModule({
            resolvers: [new CheqdDidResolver()],
          }),
          credentials: new CredentialsModule({
            credentialProtocols: [
              new V2CredentialProtocol({
                credentialFormats: [new JsonLdCredentialFormatService()],
              }),
            ],
          }),
          proofs: new ProofsModule({
            proofProtocols: [
              new V2ProofProtocol({
                proofFormats: [new DifPresentationExchangeProofFormatService()],
              }),
            ],
          }),
          cache: new CacheModule({
            cache: new InMemoryLruCache({ limit: 100 }),
          }),
          w3cCredentials: new W3cCredentialsModule({}),
        },
      })
    
      // Register transports
      holder.registerOutboundTransport(new WsOutboundTransport())
      holder.registerOutboundTransport(new HttpOutboundTransport())
      holder.registerInboundTransport(new HttpInboundTransport({ port: 3002 }))
    
      await holder.initialize()
      return holder
    }
    Issuer
    // Create a cheqd DID with Ed25519 verification method
    const did = `did:cheqd:testnet:${utils.uuid()}`
    const ed25519Key = await issuer.wallet.createKey({ keyType: KeyType.Ed25519 })
    
    const createResult = await issuer.dids.create({
      method: 'cheqd',
      didDocument: new DidDocumentBuilder(did)
        .addController(did)
        .addVerificationMethod(
          getEd25519VerificationKey2018({
            key: ed25519Key,
            controller: did,
            id: `${did}#${ed25519Key.fingerprint}`,
          })
        )
        .addAssertionMethod(`${did}#${ed25519Key.fingerprint}`)
        .addAuthentication(`${did}#${ed25519Key.fingerprint}`)
        .build(),
    })
    
    if (!createResult.didState.did) {
      throw new Error('cheqd DID creation failed')
    }
    
    issuerDid = createResult.didState.did
    console.log('Issuer DID created:', issuerDid)
    Issuer
    const createNewInvitation = async (agent: Agent) => {
      const outOfBandRecord = await agent.modules.oob.createInvitation()
    
      return {
        invitationUrl: outOfBandRecord.outOfBandInvitation.toUrl({ domain: 'http://localhost:3001' }),
        outOfBandRecord,
      }
    }
    Holder
    const receiveInvitation = async (agent: Agent, invitationUrl: string) => {
      const { outOfBandRecord } = await agent.modules.oob.receiveInvitationFromUrl(invitationUrl)
      if (!outOfBandRecord) {
          throw new Error(redText(Output.NoConnectionRecordFromOutOfBand))
      }
      return outOfBandRecord
    }
    Issuer and Holder
    import { CredentialEventTypes, CredentialState } from '@credo-ts/core'
    
    const setupCredentialListener = (agent: Agent) => {
      agent.events.on(CredentialEventTypes.CredentialStateChanged, async ({ payload }) => {
        const { credentialRecord } = payload
        
        switch (credentialRecord.state) {
          case CredentialState.ProposalReceived:
            console.log('Issuer: Credential proposal received')
            await agent.credentials.acceptProposal({
              credentialRecordId: credentialRecord.id,
              comment: 'JSON-LD Credential Offer',
            })
            break
            
          case CredentialState.OfferReceived:
            console.log('Holder: Credential offer received, accepting...')
            await agent.credentials.acceptOffer({
              credentialRecordId: credentialRecord.id,
              credentialFormats: { jsonld: {} },
            })
            break
            
          case CredentialState.RequestReceived:
            console.log('Issuer: Credential request received, issuing credential...')
            await agent.credentials.acceptRequest({
              credentialRecordId: credentialRecord.id,
              comment: 'JSON-LD Credential',
            })
            break
            
          case CredentialState.CredentialReceived:
            console.log('Holder: Credential received, accepting...')
            await agent.credentials.acceptCredential({
              credentialRecordId: credentialRecord.id,
            })
            break
            
          case CredentialState.Done:
            console.log('Done: Credential exchange completed!')
            break
        }
      })
    }
    Holder
    // Define the credential to be proposed
    const credentialOptions = {
      credential: {
        '@context': [
          'https://www.w3.org/2018/credentials/v1',
          'https://w3id.org/citizenship/v1',
          'https://w3id.org/security/bbs/v1',
        ],
        id: 'https://cheqd.io/credentials/permanent-resident-card',
        type: ['VerifiableCredential', 'PermanentResidentCard'],
        issuer: issuerDid,
        issuanceDate: new Date().toISOString(),
        expirationDate: new Date(Date.now() + 10 * 365 * 24 * 60 * 60 * 1000).toISOString(), // 10 years
        credentialSubject: {
          id: 'did:example:holder123',
          type: ['PermanentResident', 'Person'],
          givenName: 'John',
          familyName: 'Doe',
          gender: 'Male',
          birthDate: '1990-01-01',
          birthCountry: 'United States',
          residentSince: '2020-01-01',
          lprNumber: '123-456-789',
          lprCategory: 'IR1',
        },
      },
      options: {
        proofType: 'Ed25519Signature2018',
        proofPurpose: 'assertionMethod',
      },
    }
    // Get Connection
    const holderConnection = await receiveInvitation(holderAgent, invitationUrl)
    // Holder proposes credential
    console.log('Starting credential proposal...')
    const credentialExchange = await holderAgent.credentials.proposeCredential({
      connectionId: holderConnection.id,
      protocolVersion: 'v2',
      credentialFormats: { jsonld: credentialOptions },
      comment: 'Requesting Permanent Resident Card',
    })
    
    console.log('Credential exchange initiated:', credentialExchange.id)
    
    // The rest of the flow is handled automatically by event listeners
    // Wait for completion...

    Issue a Verifiable Credential

    Issue a SD-JWT Verifiable Credential, signed by a did:cheqd Decentralized Identifier (DID), using Credo and OpenID4VCI.

    Using the OpenID4VC module, you can issue OpenID compatible SD-JWT Credentials signed by a did:cheqd identifier. This guide walks through the flow of issuance using the Credo Agent.

    hashtag
    Prerequisites

    Before you being, ensure you have:

    • Basic knowledge of .

    • A cheqd testnet or mainnet account with sufficient tokens for DID operations.

    hashtag
    Step 1: Install dependencies

    hashtag
    Step 2: Configure the Issuer Agent

    Configure the issuer with cheqd and OpenID4VC Modules

    This registers:

    • cheqd for did:cheqd DID creation and ledger integration

    • openId4VcIssuer and openId4VcVerifier for OID4VC flows

    hashtag
    Step 3: Create a cheqd DID and link it to SD‑JWT issuance

    hashtag
    Step 4: Define the Issuer with SD‑JWT + did:cheqd support

    This declares support for:

    • vc+sd-jwt format

    • cryptographic binding with wasm-bound did:cheqd keys

    If you want to change the display metadata or the credentials supported by the issuer, you can use the issuer.modules.openId4VcIssuer.updateIssuer method.

    hashtag
    Step 5: Map credential requests to SD‑JWT payloads

    hashtag
    Credential Issuance Mapper

    The core of the issuance process is the credentialRequestToCredentialMapper function. This function constructs the credential to be signed and issued to the Holder. It includes:

    • Domain-specific claims: e.g., employee ID, role.

    • Cryptographic bindings: Associations with the Holder.

    • Optional selective disclosure: Customisable elements for privacy control.

    Utilising payload and disclosureFrame, you have full flexibility over the credential's structure and visibility. This allows for privacy-preserving credentials that adhere to the SD-JWT specification and the OpenID for Verifiable Credential Issuance (OID4VCI) standard.

    This constructs a standard SD‑JWT payload—structural claims ready for selective disclosure.

    circle-check

    hashtag
    Notes on Mapping VC Fields to SD-JWT Format

    VC Model Field
    SD-JWT Equivalent and Custom
    Comment
    circle-info

    hashtag
    Selective Disclosure (Optional)

    With this expanded payload, you can also enhance your disclosureFrame:

    This configuration means:

    • The lastName, role, evidence, and termsOfUse claims are blinded in the signed JWT.

    • The Holder can choose to reveal these fields when presenting the credential.


    circle-exclamation

    hashtag
    ⚠️ Important Considerations

    While SD-JWT allows for simple flat key-value claims, some fields from the full VC model (like @context, type, and deeply nested credentialSubject) are not directly represented due to JWT limitations. However, you can convey semantics through:

    • vct (type semantics)

    • credentialSchema (structure enforcement)

    • custom claim naming (e.g., credentialSubjectId instead of nesting)

    hashtag
    Step 6: Create Credential Offer

    Once you have configured the issuer, you can create a credential offer. The credential offer method will generate an offer URI that you can share with a holder.

    We have also added an event listener that listens for state changed events, this allows Issuer to know when the issuance session is complete.

    issuanceDate

    iat

    ISO 8601 format

    expirationDate

    exp

    Optional

    credentialSubject

    Flattened into individual keys

    SD-JWT doesn’t nest claims

    evidence

    evidence

    Optional, can be array of structured info

    credentialSchema

    credentialSchema

    Helps verifier interpret structure

    termsOfUse

    termsOfUse

    Optional. Encodes policy using ODRL or similar—can include rights, duties, and prohibitions.

    Use termsOfUse to express legal/policy frameworks around credential usage, such as prohibitions on sharing.

    @context

    Omitted in SD-JWT

    Context is not typically included in JWT payloads

    id

    id

    Use urn:uuid:... or full URL

    type

    vct

    Set via vct (Verifiable Credential Type)

    issuer

    issuer

    Credo Agent configurationarrow-up-right

    Must be a valid DID

    npm install @credo-ts/core @credo-ts/node @credo-ts/cheqd
    npm install @credo-ts/openid4vc
    import { Agent, DidsModule, KeyType } from '@credo-ts/core';
    import { agentDependencies } from '@credo-ts/node';
    import { CheqdModule } from '@credo-ts/cheqd';
    import express, { Router } from 'express'
    import { OpenId4VcIssuerModule, OpenId4VcVerifierModule } from '@credo-ts/openid4vc';
    
    // Create two express routers, all endpoints for the
    // issuer and verifier will be added to these routers
    const verifierRouter = Router()
    const issuerRouter = Router()
    
    // Register the routers on the express server. The path should match
    // with the baseUrl you configure in the modules below.
    const app = express()
    app.use('/oid4vci', issuerRouter)
    app.use('/siop', verifierRouter)
    
    const issuer = new Agent({
      config,
      dependencies: agentDependencies,
      modules: {
        dids: new DidsModule({
          registrars: [new CheqdDidRegistrar()],
          resolvers: [new CheqdDidResolver()],
        }),
        cheqd: new CheqdModule(
          new CheqdModuleConfig({
            networks: [
              {
                network: '<mainnet or testnet>',
                cosmosPayerSeed: '<cosmos payer seed or mnemonic>',
              },
            ],
          })
        ),
        openId4VcIssuer: new OpenId4VcIssuerModule({
          baseUrl: 'https://your-issuer-host/oid4vci',
          router: issuerRouter,
          endpoints: {
            // The credentialRequestToCredentialMapper is the only required endpoint
            // configuration that must be provided. This method is called whenever a
            // credential request has been received for an offer we created. The callback should
            // return the issued credential to return in the credential response to the holder.
            credential: {
              // you'll map credential once requests come in
              credentialRequestToCredentialMapper: async ({ credentialRequest }) => {
                // See step 5.
              },
            },
          },
        }),
        // openId4VcVerifier module can only be used in Node.js
        openId4VcVerifier: new OpenId4VcVerifierModule({
          baseUrl: 'https://your-issuer-host/siop',
          router: verifierRouter,
        }),
      },
    });
    // listen on port 3000 for the openid4vc app.
    app.listen(3000)
    // Create a did:cheqd that we will use for issuance
    const issuerDidResult = await issuer.dids.create({
      method: 'cheqd',
      options: {
        network: 'testnet',
        methodSpecificIdAlgo: 'uuid',
      },
    })
    
    if (issuerDidResult.didState.state !== 'finished') {
      throw new Error('DID creation failed.')
    }
    const issuerDid = issuerDidResult.did;
    import { JwaSignatureAlgorithm } from '@credo-ts/core'
    
    // Create an issuer with one supported credential: AcmeCorpEmployee
    const openid4vcIssuer = await issuer.modules.openId4VcIssuer.createIssuer({
      display: [
        {
          name: 'ACME Corp.',
          description: 'ACME Corp. is a company that provides the best services.',
          text_color: '#000000',
          background_color: '#FFFFFF',
          logo: {
            url: 'https://acme.com/logo.png',
            alt_text: 'ACME Corp. logo',
          },
        },
      ],
      credentialsSupported: [
        {
          format: 'vc+sd-jwt',
          vct: 'AcmeCorpEmployee',
          id: 'AcmeCorpEmployee',
          cryptographic_binding_methods_supported: ['did:cheqd'],
          cryptographic_suites_supported: [JwaSignatureAlgorithm.ES256],
        },
      ],
    })
    const credentialRequestToCredentialMapper: OpenId4VciCredentialRequestToCredentialMapper = async ({
      // agent context for the current wallet / tenant
      agentContext,
      // the credential offer related to the credential request
      credentialOffer,
      // the received credential request
      credentialRequest,
      // the list of credentialsSupported entries
      credentialsSupported,
      // the cryptographic binding provided by the holder in the credential request proof
      holderBinding,
      // the issuance session associated with the credential request and offer
      issuanceSession,
    }) => {
      const firstSupported = credentialsSupported[0]
      const { sub } = credentialRequest.claims;
      const payload = {
        vct: firstSupported.vct, // Verifiable Credential Type identifier
        // Credential subject fields (flattened)
        credentialSubjectId: sub,   // Represents subject's DID (e.g., Holder DID)
        firstName: 'John',
        lastName: 'Doe',
        employeeId: 'EMP-1234',
        role: 'engineer',
        // Optional: evidence and schema
        evidence: [{
          type: 'EmployeeRecord',
          verifier: issuerDid,
          evidenceDocument: 'HR Database Entry 2024-Q1',
          subjectPresence: 'Physical',
          documentPresence: 'Digital',
        }],
      
        credentialSchema: {
          id: 'https://example.org/schemas/employee-passport.json',
          type: 'JsonSchemaValidator2018',
        },
        // Credential Status
        credentialStatus: {
          id: 'https://status.cheqd.net/vc/123456',
          type: 'StatusList2021Entry',
          statusPurpose: 'revocation',
          statusListIndex: '123456',
          statusListCredential: 'https://status.cheqd.net/list/employee-vc.json',
        },
        // Timestamps in numeric format
        notBefore: Math.floor(Date.now() / 1000),
        expiry: Math.floor((Date.now() + 31536000000) / 1000),
        // Terms of Use
        termsOfUse: [
          {
            type: 'OdrlPolicy2017',
            profile: 'https://cheqd.net/policies/employee-vc-policy.json',
            prohibition: [
              {
                assigner: issuerDid,
                target: 'credential',
                action: 'share',
              },
            ],
          },
        ],
      };
      return {
        credentialSupportedId: firstSupported.id,
        format: 'vc+sd-jwt',
        // We can provide the holderBinding as is, if we don't want to make changes
        holder: holderBinding,
        payload: payload,
        disclosureFrame: {
          _sd: ['lastName', 'credentialStatus', 'termsOfUse'],
        },
        issuer: {
          method: 'cheqd',
          issuerDid,
        },
      };
    }
    tsCopyEditdisclosureFrame: {
      _sd: ['lastName', 'role', 'evidence', 'termsOfUse'],
    }
    const { credentialOffer, issuanceSession } = 
      await issuer.modules.openId4VcIssuer.createCredentialOffer({
        issuerId: openid4vcIssuer.issuerId,
        // values must match the `id` of the credential supported by the issuer
        offeredCredentials: ['AcmeCorpEmployee'],
      
        // Only pre-authorized code flow is supported
        preAuthorizedCodeFlowConfig: {
          userPinRequired: false,
        },
      
        // You can store any metadata about the issuance here
        issuanceMetadata: {
          someKey: 'someValue',
        },
    })
    
    // Listen and react to changes in the issuance session
    issuer.events.on<OpenId4VcIssuanceSessionStateChangedEvent>(
      OpenId4VcIssuerEvents.IssuanceSessionStateChanged,
      (event) => {
        if (event.payload.issuanceSession.id === issuanceSession.id) {
          console.log('Issuance session state changed to ', 
            event.payload.issuanceSession.state)
        }
      }
    )