Build an issuer
You run the school side. 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 student's wallet; a workflow service does that. Your whole job is three calls: create an exchange, share an interaction URL that points the student's wallet at it, then poll the exchange until the wallet has been issued the student ID.
What the coordinator does
- Create an exchange by POSTing the workflow's variables
to its
exchangesendpoint. - Host and share an interaction URL that points the student's wallet at that exchange.
- Poll the exchange until it completes, then read the result.
The workflow service does everything in between — protocol negotiation, DID authentication, signing, 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 student ID is for:
POST https://workflows.example/workflows/student-id-issuance/exchanges
{
"ttl": 900,
"variables": {
"subject": "did:example:student123",
"student_number": "S1234567"
}
}
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/student-id-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 or a link, typically wrapped in a presentation request the wallet
understands:
{
"VerifiablePresentation": {
"interact": {
"service": [{
"type": "VerifiableCredentialApiExchangeService",
"serviceEndpoint": "https://workflows.example/workflows/student-id-issuance/exchanges/z19xQ..."
}]
}
}
}
The student'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/student-id-issuance/exchanges/z19xQ...
{
"exchange": {
"id": "z19xQ...",
"state": "complete",
"variables": {
"results": {
"didAuthn": {
"did": "did:example:student123",
"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 student ID was issued into the
student's wallet.
That's it
A school issuer, as a coordinator, is these three calls: create an exchange, host and share the interaction URL, poll for the result. Signing the student ID 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.