Credential Offers
The Open Credential Handoff (O4CH) protocol lets issuers
deliver Verifiable Credentials to holders via URL. The credential travels
in the URL fragment (#vc=) and is never transmitted to a server.
How It Works
- Issuer serializes the credential into a compact URL using
serializeCredentialOffer() - The URL is delivered to the holder (QR code, email, link)
- The holder opens the URL — the receiving page reads the fragment
parseCredentialOffer()extracts and validates the credential- The holder's wallet stores the credential locally
Because the credential is in the URL fragment (after
#), it is never sent to the server in an HTTP request.
The credential stays entirely client-side.
CredentialOffer
TypeScript
interface CredentialOffer { credential: VerifiableCredential // The full W3C VC issuerDid: string // Issuer's DID issuedAt: string // ISO 8601 timestamp expiresAt?: string // Optional expiry }
CredentialOfferPreview
TypeScript
interface CredentialOfferPreview { type: string // Credential type name issuerName: string // Human-readable issuer summary: string // One-line description issuedAt: string // When issued expiresAt?: string // When it expires }
Creating an Offer URL
TypeScript
import { serializeCredentialOffer } from '@attestto/id-wallet-adapter' const url = serializeCredentialOffer({ baseUrl: 'https://verify.attestto.com/offer/', credential: myVC, issuerDid: 'did:sns:attestto.sol', issuedAt: new Date().toISOString(), }) // Result: https://verify.attestto.com/offer/#vc=eyJ0eXAi... console.log(url)
Consuming an Offer
TypeScript
import { parseCredentialOffer } from '@attestto/id-wallet-adapter' // On the receiving page const offer = parseCredentialOffer(window.location.href) if (offer) { console.log('Received credential from:', offer.issuerDid) console.log('Type:', offer.credential.type) // Store in wallet const wallet = await pickWallet({ render: 'modal' }) await wallet.storeCredential(offer.credential) }
verify.attestto.com Implementation
The page at verify.attestto.com/offer/ implements O4CH as a
reference. When a user opens an offer URL, the page reads the fragment,
displays a preview card using CredentialOfferPreview, and
prompts the user to store the credential in their wallet.