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

  1. Issuer serializes the credential into a compact URL using serializeCredentialOffer()
  2. The URL is delivered to the holder (QR code, email, link)
  3. The holder opens the URL — the receiving page reads the fragment
  4. parseCredentialOffer() extracts and validates the credential
  5. 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.