Verifiable Credentials Documentation

Handling a VC API Exchange

A VC API workflow defines a particular set of steps for exchanging verifiable credentials between two parties across a trust boundary. Each step can involve the issuance, verification, transmission, or presentation of verifiable credentials.

from VC API Workflows & Exchanges

A VC API workflow interaction begins with either a CHAPI event or an interaction URL via a QR code (or similar user initiated transfer experience).

The CHAPI event will provide a complete protocols object similar to the following:

{
  "protocols": {
    "vcapi": "https://vcapi.example.com/workflows/abc/exchanges/123",
    "OID4VCI": "openid-credential-offer://?..."
  }
}

Alternatively, a QR code can be used to provide a URL which when dereferenced will result in either a protocols (object as above) or an HTML fallback page to allow the user to continue otherwise.

To retrieve a protocols object from an interaction URL, the Wallet must send an HTTP GET request including an explicit Accept: application/json request header--which results in the same JSON object as above:

GET /workflows/abc/exchanges/123/protocols
Host: vcapi.example.com
Accept: application/json

{
  "protocols": {
    "vcapi": "https://vcapi.example.com/workflows/abc/exchanges/123",
    "OID4VCI": "openid-credential-offer://?..."
  }
}

Once a VC API exchange URL is acquisitioned from protocols.vcapi, a POST request is sent with a configuration object (which may be empty) to begin the exchange:

POST /workflows/abc/exchanges/123
Host: vcapi.example.com

{}

A response will be returned by the exchanger...

If the response object is empty (as above), the exchange is complete and nothing is requested from nor offered to the exchange client.

If, however, the object includes verifiablePresentationRequest, then the exchange is not yet complete and some additional information is requested, as specified by the contents of the associated verifiable presentation request.

Read more about possible values in the Verifiable Presentation Requests specification.

For example:

{
  "verifiablePresentationRequest": {
    "query": [{
      "type": "QueryByExample",
      "credentialQuery": [{
        "reason": "We require proof of residency to onboard you.",
        "example": {
          "@context": [
            "https://www.w3.org/ns/credentials/v2",
            "https://w3id.org/citizenship/v1"
          ],
          "type": "PermanentResidentCard"
        }
      }]
    }]
  }
}

If the object includes verifiablePresentation, then some information is offered, such as verifiable credentials issued to the holder operating the exchange client (i.e. the Wallet) or verifiable credentials with information about the exchange server's operator based on the exchange client's request.

For example:

{
  "verifiablePresentation": {
    "@context": [
      "https://www.w3.org/ns/credentials/v2"
    ],
    "type": ["VerifiablePresentation"],
    "verifiableCredential": [{
      "@context": [
        "https://www.w3.org/ns/credentials/v2",
        "https://w3id.org/citizenship/v1"
      ],
      "type": ["VerifiableCredential", "PermanentResidentCard"],
      // additional properties...
    }]
  }
}

If the object includes redirectUrl, the exchange is complete and the workflow service recommends that the client sent the user to another place to continue the interaction.

For example:

{
  "redirectUrl": "https://vcapi.example.com/go-here-next"
}

Many Verifiable Credential use cases can be implemented using these basic primitives. Either party to an exchange is capable of requesting Verifiable Presentations and of providing one or more Verifiable Credentials that might be necessary to establish trust and/or gain authorization capabilities, and either party is capable of presenting credentials that they hold or that they have issued. Specific workflows can be configured to expect specific presentations and credentials and to reject deviations from the expected flow of information.

When a workflow service determines that a particular message is not acceptable, it raises an error by responding with a 4xx HTTP status message and a JSON object that expresses information about the error.

Below is an example of a typical exchange:

sequenceDiagram
    participant H as Holder
    participant W as Holder Coordinator (Wallet)
    participant I as Issuer/Verifier Coordinator
    autonumber
    Note right of H: Start exchange
    W->>I: Initiate
    Note right of W: POST /workflows/abc/exchanges/123 — HTTP request to start exchange (e.g., send credentials, get credentials)
    I->>W: Verifiable Presentation Request (VPR)
    Note left of I: VPR includes method of interaction, for purposes of exchange
    W->>I: Verifiable Presentation (VP)
    Note right of W: POST /workflows/abc/exchanges/123 — sent via interaction mechanism to meet requirements of exchange
    I->>W: Verifiable Presentation
    Note left of I: VP includes result of exchange (e.g., VCs), or VPR with new interaction request, or error description

The exchange client (Wallet) code for a flow like the above may look similar to the following:

const receivedExchangeUrl = 'https://vcapi.example.com/workflows/abc/exchanges/123';

const response = await fetch(receivedExchangeUrl, {
  method: 'POST'
  body: JSON.stringify({})
});
const body = await response.json();

function checkResponse(body) {
  if('verifiablePresentationRequest' in body) {
    // use the information in the Verifiable Presentation Request to find a
    // credential that fulfills the request, then send a Verifiable
    // Presentation a message back to the exchange endpoint
    // Verifiable Presentation Request
    const verifiablePresentation = findCredentialAndCreatePresentation();
    const response = await fetch(receivedExchangeUrl, {
      method: 'POST',
      body: JSON.stringify({verifiablePresentation})
    });
    checkResponse(response.json());
  }
  if('verifiablePresentation' in body) {
    // the Wallet has received a Verifiable Presentation containing one or more
    // Verifiable Credentials--use them per your use case
  }
  if('redirectUrl' in body) {
    // take the user to the new location
  }
}