Resolve DIDs & Verify Credentials
Every Vouch agent gets a W3C Decentralized Identifier (DID) and can attach Verifiable Credentials. This tutorial covers resolving DIDs, generating DID Documents, and working with credentials using both the SDK and the REST API.
The DID Format
Vouch uses the did:sol method. The DID is derived directly from the owner's Solana public key:
did:sol:<owner_pubkey>
# Example:
did:sol:7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU
Resolve DIDs with the SDK
Derive a DID
import { RegistryClient } from '@vouch-protocol/sdk'; // Vouch SDK for DID derivation import { PublicKey } from '@solana/web3.js'; // Solana public key type const owner = new PublicKey('7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU'); // Agent owner's Solana pubkey // Derive the DID string const did = RegistryClient.deriveAgentDid(owner); // Deterministic: always the same for a given pubkey console.log(did); // "did:sol:7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU"
Generate a DID Document
The DID Document follows the W3C DID Core specification and includes verification methods, authentication, and service endpoints. First, set up the Anchor provider and program (same pattern as the registration tutorial):
import { Connection, Keypair } from '@solana/web3.js'; import { AnchorProvider, Program, Wallet } from '@coral-xyz/anchor'; import { RegistryClient, IDL } from '@vouch-protocol/sdk'; import { readFileSync } from 'fs'; import { homedir } from 'os'; import { join } from 'path'; // Set up connection, wallet, and Anchor const connection = new Connection('https://api.devnet.solana.com'); const keypair = Keypair.fromSecretKey(new Uint8Array(JSON.parse( readFileSync(join(homedir(), '.config/solana/id.json'), 'utf-8') ))); const wallet = new Wallet(keypair); const provider = new AnchorProvider(connection, wallet, { commitment: 'confirmed' }); const program = new Program(IDL, provider); // Initialize RegistryClient and generate the DID Document const registry = new RegistryClient(program, provider); const didDoc = await registry.generateDidDocument(owner); // Build a W3C DID Document from on-chain data console.log(JSON.stringify(didDoc, null, 2)); // Pretty-print the DID Document
The returned DID Document structure:
{
"@context": [
"https://www.w3.org/ns/did/v1",
"https://w3id.org/security/suites/ed25519-2020/v1"
],
"id": "did:sol:7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
"controller": "did:sol:7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
"verificationMethod": [{
"id": "did:sol:7xKXtg...#key-1",
"type": "Ed25519VerificationKey2020",
"controller": "did:sol:7xKXtg...",
"publicKeyBase58": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU"
}],
"authentication": ["did:sol:7xKXtg...#key-1"],
"assertionMethod": ["did:sol:7xKXtg...#key-1"],
"service": [{
"id": "did:sol:7xKXtg...#agent-card",
"type": "A2AAgentCard",
"serviceEndpoint": "https://example.com/agent-card.json"
}, {
"id": "did:sol:7xKXtg...#vouch-registry",
"type": "VouchRegistry",
"serviceEndpoint": "solana:5i73FN7Ycgnh9dr2Yzf3oFiTCKhu85JPpTUbZ84VPtu4:<agentPda>"
}]
}
Resolve DIDs with the API
Resolve Agent + DID Document
Returns the agent data plus a full DID Document.
# Resolve a DID to get agent data + full DID Document
curl https://api.vouchprotocol.xyz/did/did:sol:7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU
{
"did": "did:sol:7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
"agent": {
"pda": "BKq8rN4EwJQG3R9FnLhSGqJ2tNkh8cVRxvNApj7hbfQM",
"name": "CommerceBot",
"tier": "standard",
"reputation_score": 720
},
"didDocument": { "@context": ["..."], "id": "did:sol:...", "...": "..." }
}
Resolve DID Document Only
Returns only the W3C DID Document. Content-Type: application/did+ld+json.
# Fetch only the W3C DID Document (Content-Type: application/did+ld+json)
curl https://api.vouchprotocol.xyz/did/did:sol:7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU/document
Verifiable Credentials
Vouch supports four types of Verifiable Credentials that can be attached to agents:
| Type | Purpose |
|---|---|
| SecurityAudit | Proves the agent passed a security audit |
| Identity | Verifies the agent operator's identity |
| Capability | Certifies specific skills or capabilities |
| Compliance | Proves compliance with a regulatory framework |
Submit a Credential
Submit a Verifiable Credential for an agent. Requires wallet authentication.
Submit a VC for verification and storage. Requires wallet auth headers.
# Submit a Verifiable Credential for an agent (requires wallet auth)
curl -X POST https://api.vouchprotocol.xyz/agents/AgentPDA.../credentials/verify \
-H "Content-Type: application/json" \
-H "X-Wallet-Pubkey: 7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU" \
-H "X-Wallet-Signature: <signed-message>" \
-H "X-Wallet-Timestamp: 1710000000" \
-d '{
"credential": {
"@context": ["https://www.w3.org/2018/credentials/v1"],
"type": ["VerifiableCredential", "SecurityAuditCredential"],
"issuer": "did:sol:AuditorPubkey...",
"credentialSubject": {
"id": "did:sol:7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
"auditResult": "pass",
"auditDate": "2026-03-15"
}
}
}'
List Agent Credentials
List all Verifiable Credentials attached to an agent. Public, no auth required.
# List all Verifiable Credentials for an agent (public, no auth needed)
curl https://api.vouchprotocol.xyz/agents/AgentPDA.../credentials
{
"pda": "BKq8rN4EwJQG3R9FnLhSGqJ2tNkh8cVRxvNApj7hbfQM",
"total": 1,
"credentials": [
{
"type": "SecurityAuditCredential",
"issuer": "did:sol:AuditorPubkey123...",
"credential_url": "https://auditor.example/vc/123.json",
"structurally_valid": true,
"valid_from": "2026-01-01T00:00:00Z",
"valid_until": "2027-01-01T00:00:00Z"
}
]
}
SDK Credential Helpers
The SDK provides utility functions for working with credentials:
import { isCredentialActive, // Checks expiry + revocation status isCredentialForAgent, // Verifies credential subject matches agent DID buildCredentialsExtension, // Formats VCs as a DID Document extension } from '@vouch-protocol/sdk'; // Check if a credential is still valid (not expired/revoked) const active = isCredentialActive(credential); // Returns false if expired or revoked console.log('Active:', active); // true // Check if a credential belongs to a specific agent (second param is the owner PublicKey) const agentOwner = new PublicKey('7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU'); const belongs = isCredentialForAgent(credential, agentOwner); // Matches credentialSubject.id to the owner's PublicKey console.log('Belongs to agent:', belongs); // true // Build a credentials extension for the DID Document const extension = buildCredentialsExtension(credentials); // Attach VCs to a DID Document console.log(extension); // { credentials: [...] }
Complete SDK Example
import { Connection, Keypair } from '@solana/web3.js'; import { AnchorProvider, Program, Wallet } from '@coral-xyz/anchor'; import { RegistryClient, IDL, isCredentialActive, isCredentialForAgent, } from '@vouch-protocol/sdk'; import { readFileSync } from 'fs'; import { homedir } from 'os'; import { join } from 'path'; async function main() { // 0. Set up connection, wallet, and Anchor const connection = new Connection('https://api.mainnet-beta.solana.com'); const keypair = Keypair.fromSecretKey(new Uint8Array(JSON.parse( readFileSync(join(homedir(), '.config/solana/id.json'), 'utf-8') ))); const wallet = new Wallet(keypair); const provider = new AnchorProvider(connection, wallet, { commitment: 'confirmed' }); const program = new Program(IDL, provider); // 1. Derive the DID const did = RegistryClient.deriveAgentDid(wallet.publicKey); // Pure function, no RPC call needed console.log('DID:', did); // 2. Generate the full DID Document const registry = new RegistryClient(program, provider); const didDoc = await registry.generateDidDocument(wallet.publicKey); // Fetches agent PDA and builds the W3C document console.log('DID Document:', JSON.stringify(didDoc, null, 2)); // 3. Fetch credentials via API const [agentPda] = RegistryClient.findAgentPda(wallet.publicKey); // Returns [PublicKey, bump] tuple const res = await fetch( // GET credentials from the REST API `https://api.vouchprotocol.xyz/agents/${agentPda.toBase58()}/credentials` ); const { credentials } = await res.json(); // 4. Map API response (snake_case) to AgentCredentialRef (camelCase) // isCredentialActive expects { credentialUrl, validFrom, validUntil, ... } const refs = credentials.map((c: any) => ({ type: c.type, issuer: c.issuer, credentialUrl: c.credential_url, validFrom: c.valid_from, validUntil: c.valid_until, structurallyValid: c.structurally_valid, })); // 5. Filter active credentials for this agent const valid = refs.filter( // Keep only non-expired VCs that match this agent (c: any) => isCredentialActive(c) && isCredentialForAgent(c, wallet.publicKey) ); console.log('Valid credentials:', valid.length); } main().catch(console.error);
API response mapping. The REST API returns credentials with snake_case fields (credential_url, valid_from, valid_until, structurally_valid), but the SDK's isCredentialActive function expects an AgentCredentialRef object with camelCase fields (credentialUrl, validFrom, validUntil, structurallyValid). Make sure to map the fields before passing API responses to SDK helpers.
Next Steps
- DID Utilities reference for all SDK DID methods
- Credentials SDK reference for the full helper API
- DID API reference for all REST endpoints
- Credentials API reference for submission and listing details