Build an issuer

You run the brand side: start an exchange and deliver a signed authenticity credential into the product's wallet — over the same VCALM exchange loop the wallet speaks.

Under the hood: the raw VCALM exchange

Want to build your own coordinator, or just see exactly what the server does on the wire? It's a short HTTP loop — three things:

  1. Expose an interaction URL (on a tag, QR, or link) that advertises vcapi.
  2. When the wallet POSTs to your exchange URL, respond with the authenticity credential in a verifiablePresentation.
  3. Optionally bind the credential to the current owner first, then deliver; finish the exchange.

1. Advertise the exchange

The wallet dereferences your interaction URL (iuv=1) and you return the protocols you support. Offer vcapi for VCALM:

{
  "protocols": {
    "vcapi": "https://issuer.brand.example/exchanges/bag-456"
  }
}

2. Deliver the credential

The wallet POSTs an empty body to start. You respond with the signed authenticity credential wrapped in a verifiablePresentation:

{
  "verifiablePresentation": {
    "@context": ["https://www.w3.org/ns/credentials/v2"],
    "type": ["VerifiablePresentation"],
    "verifiableCredential": [{
      "@context": [
        "https://www.w3.org/ns/credentials/v2",
        "https://w3id.org/traceability/v1"
      ],
      "type": ["VerifiableCredential", "ProductPassportCredential"],
      "issuer": "did:web:brand.example",
      "credentialSubject": {
        "id": "did:example:bag-456",
        "product": {"sku": "HB-2026-0042", "model": "Atelier Tote"},
        "origin": {"country": "FR", "facility": "Paris Atelier"},
        "manufacturedOn": "2026-03-14"
      },
      "proof": { "...": "brand signature" }
    }]
  }
}

The exchange is complete when you return no verifiablePresentationRequest.

3. (Optional) Bind to the current owner first

To tie the credential to whoever holds the product, respond to the wallet's first POST with a verifiablePresentationRequest for DID Authentication, then deliver on the next turn:

{
  "verifiablePresentationRequest": {
    "query": [{
      "type": "DIDAuthentication",
      "acceptedMethods": [{"method": "example"}]
    }],
    "challenge": "99612b24-63d9-11ea-b99f-4f66f3e4f81a",
    "domain": "issuer.brand.example"
  }
}

That's it

A conformant provenance issuer is this exchange: advertise, deliver, confirm. Signing the credential is your issuer instance's job — the coordinator just runs the exchange.

On the wallet end, a standalone client-side VCALM library is still planned; until it ships, the raw HTTP flow is the supported path there. For the issuer/coordinator side shown here, use @bedrock/vc-delivery.

Go deeper

← Back to roles