Product Docs
Product DocsTechnical DocsLearning & GovernanceUseful Links
  • Product Docs
  • Node Docs
  • Learning Docs
  • â„šī¸Getting Started
    • Product Overview
    • âžĄī¸Get Started with cheqd Studio
      • 👉Set Up Your Account
      • đŸ—ī¸Create API Keys
      • đŸĒ™Token Top Up
      • 🔄Advanced Configuration Options
    • â˜‘ī¸Use Trust Registries for AI Agents
      • đŸ—ī¸Build an AI Agent Trust Registry
        • Setup AI Agent Trust Registry
          • Issue Verifiable Credentials to AI Agent
        • Setup and Configure MCP Server
          • Create AI Agent DID
          • Import Credential to AI Agent
          • Advanced functionality
            • Issue a Verifiable Credential
            • Verify a Credential
      • 🤝Validate AI Agent Trust Chain
  • đŸŸĸStart using cheqd
    • 🆔Create DIDs and Identity Keys
      • Create a DID
      • Create Identity Keys
      • Create a Subject DID
      • Resolve a DID
      • Update a DID
      • Deactivate a DID
    • ✅Issue Credentials and Presentations
      • Issue a Verifiable Credential
      • Setup Verida Wallet
      • Verify a Verifiable Credential
      • Verify a Verifiable Presentation
      • Revoke a Verifiable Credential
      • Suspend or Unsuspend a Verifiable Credential
    • â™ģī¸Charge for Verifiable Credentials
      • Understanding Credential Payments
        • Access Control Conditions
        • Privacy Considerations
      • Charge for Status List
      • Issue Credential with Encrypted Status List
      • Create Verifier Pays Issuer flow
      • Bulk Update or Rotate Encryption Keys
    • 🤝Build Trust Registries
      • Decentralized Trust Chains (DTCs)
        • Root Authorisations
        • RTAO -> TAO
        • TAO -> SubTAO
        • TAO -> Trusted Issuer (TI)
        • Referencing Trust Registry within a Verifiable Credential
      • Set up Trust Chain
        • Issue a Verifiable Accreditation
        • Verify a Verifiable Accreditation
      • Get Started with TRAIN
        • Deploy TRAIN and Anchor rTAO in DNS
        • Validate Trust Chain
    • 🎋Create Status Lists
      • Bitstring Status List
        • Create Bitstring Status List
        • Update Bitstring Status List
        • Check Bitstring Status List
        • Search Bitstring Status List
      • Token Status List
        • Create Token Status List
        • Update Token Status List
    • â†•ī¸Create DID-Linked Resources
      • Understanding DID-Linked Resources
        • Context for developing DID-Linked Resources
        • Technical composition of DID-Linked Resources
        • Referencing DID-Linked Resources in VCs
      • Create a DID-Linked Resource
      • Search for DID-Linked Resources
  • đŸ› ī¸Integrate an SDK
    • Choosing the right SDK
    • 🍏Credo
      • Setup Credo Agent
      • Decentralized Identifiers (DIDs)
        • Create a DID
        • Update a DID
        • Deactivate a DID
      • DID-Linked Resources
        • Create a DID-Linked Resource
        • Resolve a DID-Linked Resource
        • Create an AnonCreds Schema
        • Create an AnonCreds Credential Definition
      • Verifiable Credentials and Presentations
        • AnonCreds
          • Issue a Verifiable Credential
          • Present a Verifiable Credential
        • JSON-LD
          • Issue a Verifiable Credential
          • Present a Verifiable Credential
        • SD-JWT VC
          • Issue a Verifiable Credential
          • Present a Verifiable Credential
    • 🍊ACA-Py
      • Setup ACA-Py Agent
      • Decentralized Identifiers (DIDs)
        • Create a DID
        • Update a DID
        • Deactivate a DID
      • DID-Linked Resources
        • Create AnonCreds Schema
        • Create AnonCreds Credential Definition
      • Verifiable Credentials and Presentations
        • AnonCreds
          • Issue a Verifiable Credential
          • Present a Verifiable Credential
          • Revoke a Verifiable Credential
        • JSON-LD
          • Issue a Verifiable Credential
          • Present a Verifiable Credential
    • 🍈Veramo
      • Setup Veramo CLI for cheqd
        • Troubleshooting Veramo CLI Setup
      • Decentralized Identifiers (DIDs)
        • Create a DID
        • Querying a DID
        • Update an existing DID
        • Deactivate a DID
        • Create an off-ledger holder DID
        • Managing Identity Keys
        • Troubleshooting
      • Verifiable Credentials and Presentations
        • Issue a Verifiable Credential
        • Verify a Verifiable Credential
        • Create a Verifiable Presentation
        • Verify a Verifiable Presentation
      • Credential Payments
        • Charge for Status List
        • Issue Credential with Encrypted Status List
        • Verifier pays Issuer
      • Bitstring Status List
        • Create Status List
        • Issuing a Verifiable Credential referencing Status List
      • DID-Linked Resources
        • Create a DID-Linked Resource
        • Create a new Resource version within existing Collection
    • đŸĢWalt.id Community Stack
  • đŸ—ī¸Architecture
    • Architecture Decision Record (ADR) Process
    • List of ADRs
      • đŸ”ĩADR 001: cheqd DID Method
      • đŸŸĸADR 002: DID-Linked Resources
      • 🟡ADR 003: DID Resolver
      • 🟠ADR 004: DID Registrar
      • đŸŸŖADR 005: DID Resolution & DID URL Dereferencing
  • đŸ’ĢAdvanced features and alternatives
    • âžĄī¸DID Registrar
      • Setup DID Registrar
      • Create a DID
      • Create a DID-Linked Resource
    • âŦ…ī¸DID Resolver
      • Setup DID Resolver
    • ⚡AnonCreds Object Method
      • Schemas
      • Credential Definitions
      • Revocation Registry Definitions
      • Revocation Status Lists
    • 🌠Advanced Tooling
      • cheqd Cosmos CLI for identity
        • Create a DID
        • Update a DID
        • Deactivate a DID
        • Query a DID
        • Create a DID-Linked Resource
        • Update a DID-Linked Resource
      • Direct interaction with ledger code
      • VDR Tools CLI with cheqd (deprecated)
      • Demo Wallet for Identity Setup
  • âš›ī¸Network
    • Get started with cheqd Network
      • Identity Write Pricing
      • Comparison to Hyperledger Indy
    • ⏊Setup your Wallet
      • Setup Leap Wallet
        • Congifure cheqd Testnet for Leap
      • Setup Keplr Wallet
      • Migrate from Keplr to Leap Wallet
    • â†Ēī¸Useful Tools and APIs
      • Block Explorer
      • Testnet Faucet
      • Validator Status API
      • Cheqd x Cosmos Data APIs
      • Cosmos Airdrop Helpers
      • Cosmos Address Convertor
      • Ethereum Bridge
    • âŦ†ī¸Network Upgrades
      • 2021
        • 0.1.x
        • 0.2.x
        • 0.3.x
      • 2022
        • 0.4.x
        • 0.5.x
        • 0.6.x
      • 2023
        • 1.x
      • 2024
        • 2.x
        • 3.x
      • 2025
        • 3.1.x
        • 4.x
      • Root Cause Analysis of Outages
        • v1.x upgrade RCA
  • âš–ī¸Legal
    • License
    • Code of Conduct
    • Security Policy
  • 🆘Support
    • System Status
    • Discord
    • Bugs & Feature Requests
Powered by GitBook
LogoLogo

General

  • Website
  • Blog
  • Get $CHEQ

Product Docs

  • Product Docs
  • cheqd Studio
  • Creds.xyz
  • Bug/Feature Requests

Technical Docs

  • Node Docs
  • GitHub
  • Block Explorer

Learning Docs

  • Learning Docs
  • Governance Docs
  • Governance Forum
  • Governance Explorer
On this page
  • Prerequisites
  • Step 1: Install dependencies
  • Step 2: Set up the Issuer Agent
  • Step 3: Set up the Holder Agent
  • Step 4: Create Issuer DID
  • Step 5: Create connection between Issuer and Holder
  • 5a: Issuer creates Connection Invite
  • 5b: Holder receives invitation
  • Step 6: Set up Credential Event Listeners
  • Step 7: Holder proposes the Credential

Was this helpful?

Edit on GitHub
Export as PDF
  1. Integrate an SDK
  2. Credo
  3. Verifiable Credentials and Presentations
  4. JSON-LD

Issue a Verifiable Credential

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

Last updated 2 days ago

Was this helpful?

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.

Prerequisites

Before you begin, ensure you have:

  • Basic knowledge of .

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

Step 1: Install dependencies

npm install @credo-ts/core @credo-ts/node @credo-ts/askar @credo-ts/cheqd
npm install @hyperledger/aries-askar-nodejs

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.

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
}

Step 3: Set up the Holder Agent

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

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
}

Step 4: Create Issuer DID

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)

Step 5: Create connection between Issuer and Holder

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.

Issuer
const createNewInvitation = async (agent: Agent) => {
  const outOfBandRecord = await agent.modules.oob.createInvitation()

  return {
    invitationUrl: outOfBandRecord.outOfBandInvitation.toUrl({ domain: 'http://localhost:3001' }),
    outOfBandRecord,
  }
}

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:

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
}

Step 6: Set up Credential Event Listeners

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

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
    }
  })
}

Step 7: Holder proposes the Credential

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

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...

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

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

đŸ› ī¸
🍏
Credo Agent configuration
out-of-band protocol