Build an issuer

You run the agency side — a DMV, a licensing board, or a state agency. As a coordinator you speak VCALM — just the coordinator side of it. You don't run VCALM delivery, the on-the-wire exchange with the resident's wallet; a workflow service does that. Your whole job is three calls: create an exchange, share an interaction URL that points the resident's wallet at it, then poll the exchange until the wallet has been issued the credential.

What the coordinator does

  1. Create an exchange by POSTing the workflow's variables to its exchanges endpoint.
  2. Host and share an interaction URL that points the resident's wallet at that exchange.
  3. Poll the exchange until it completes, then read the result.

The workflow service does everything in between — protocol negotiation, DID authentication, signing the credential, talking to whatever wallet shows up — and hands you back just the result.

1. Create the exchange

POST to your workflow's exchanges endpoint. The body carries a ttl and the variables the workflow expects — here, who the license is for and its details:

POST https://workflows.example/workflows/ambulance-license-issuance/exchanges
{
  "ttl": 900,
  "variables": {
    "subject": "did:example:resident456",
    "licenseClass": "Ambulance Driver",
    "licenseNumber": "ADL-2026-00123",
    "validFrom": "2026-01-15T00:00:00Z",
    "validUntil": "2028-01-14T23:59:59Z"
  }
}

This call is authenticated (your service uses a capability or OAuth2 token). On success the service returns 204 No Content with a Location header — that's your exchange URL:

HTTP/1.1 204 No Content
Location: https://workflows.example/workflows/ambulance-license-issuance/exchanges/z19xQ...

2. Host and share the interaction URL

The exchange URL is the vcapi interaction endpoint. Hosting and sharing it is the coordinator's responsibility — put it in a QR code on a portal confirmation page, typically wrapped in a presentation request the wallet understands:

{
  "VerifiablePresentation": {
    "interact": {
      "service": [{
        "type": "VerifiableCredentialApiExchangeService",
        "serviceEndpoint": "https://workflows.example/workflows/ambulance-license-issuance/exchanges/z19xQ..."
      }]
    }
  }
}

The resident's wallet POSTs to that endpoint and runs the exchange with the workflow service directly. DID authentication and credential delivery happen there — you are not in that loop.

3. Poll for the result

While the wallet works, GET the exchange URL (authenticated) until its state is complete:

GET https://workflows.example/workflows/ambulance-license-issuance/exchanges/z19xQ...
{
  "exchange": {
    "id": "z19xQ...",
    "state": "complete",
    "variables": {
      "results": {
        "didAuthn": {
          "did": "did:example:resident456",
          "verifiablePresentation": { "...": "the wallet's authenticated VP" }
        }
      }
    }
  }
}

Verified per-step results land under exchange.variables.results, keyed by the workflow's step names. A complete state means the credential was issued into the resident's wallet.

That's it

A government issuer, as a coordinator, is these three calls: create an exchange, host and share the interaction URL, poll for the result. Signing the credential and speaking every wallet's protocol is the workflow service's job — you just create exchanges and read what comes back.

On the wallet end, a standalone client-side VCALM library is still planned; until it ships, wallets run the exchange over the raw HTTP flow. The coordinator side shown here works today against any VCALM workflow service, such as one running @bedrock/vc-delivery.

Go deeper

← Back to roles