Skip to main content

Verifiable Credentials - Process Flow

About this chapter

This chapter covers the main user action initiated flows related to the use of Verifiable Credentials with the Wallet fulfilling the role of a Holder. The information presented here is intended primarily for a technical audience and explains the back-end logic and request flows related to verifiable credentials.

User Flows

There are three main user flows involved in managing verifiable credentials:

  • Establishing a connection with an issuer/verifier
  • Receiving a credential
  • Responding to a proof request

These flows primarily use the endpoints outlined on the openapi specification for VCs, and require the user to be logged in at the Wallet Server.

Establishing a Connection with Issuer/Verifier

This flow starts with the user receiving an invitation to connect from an issuer or verifier. This invitation is typically received either from a QR code in the real world, or as part of a link with the invitation payload as a query parameter.

When the user either scans the QR code or clicks the link, the Wallet Server receives a request to its connection establishment endpoint with the base 64 encoded information.

The Wallet Server manages an account at the cloud agent which shares a 1-1 relationship with the Wallet account at the Wallet Server. If this account is missing on attempting to create a connection, the Wallet Server automatically attempts to create a new one for the user before proceeding. The Wallet Server will then automatically forward the invitation to the cloud agent, which will use the information to create a channel between the account and the other party.

On a successful connection establishment, the Wallet Server will then be ready to receive communications for the user from the cloud agent, whenever the other party has information requiring the user's attention.

Receiving a credential

This flow requires the connection establishment flow to have successfully completed beforehand, as the communication channel is used by the issuer to issue a credential to the user.

Credential issuance is initiated by the credential issuer, which propagates as a credential offer event that our cloud agent instance receives. The cloud agent then forwards the credential offer to the Wallet Server as a webhook event, using the webhook URL that was provided by the Wallet Server automatically when it created a cloud agent account for the user.

Upon receiving the credential offer, a record of it is created in the Wallet Server's database with its state set to "pending". The offer then becomes visible to users of the Wallet API, requiring them to accept or decline it.

On acceptance, the Wallet Server notifies the cloud agent that the user has accepted the offer. The acceptance is propagated back to the issuer, which can then proceed to issuing the actual credential if they choose to.

After some amount of time after acceptance, the issuer sends the credential over the connection, which is then forwarded as a second webhook event through the cloud agent to the Wallet Server. The Wallet Server then proceeds with storing the user's credential at the cloud agent, as well as a cached copy in its own database. This is portion is done in the background, using the user's offer acceptance as permission to store their credential in their Wallet on their behalf.

Proof Requests

This flow requires the connection establishment flow to have successfully completed beforehand, as the communication channel is used by the verifier to send over the request for proof fulfillment.

The proof request flow is initiated by the verifier usually when the user requests to access a service provided by them. The verifier then propagates the request as a proof request event that our cloud agent instance receives and forwards to the Wallet Server using a webhook request.

Similar to the credential offer, a record of the proof request is stored in the Wallet Server's database in a "pending" state and becomes visible the next time a user's Wallet client checks the list of proof requests.

The user must then choose the credential(s) they wish to use to fulfill the proof request, or decline the request altogether. To let the user know what credentials they are allowed to use, the cloud agent provides a separate endpoint which specifically returns a list of credentials stored by the user which are applicable to the proof, which the Wallet Server exposes an API on its end to access.

On acceptance, the chosen credentials are validated by the cloud agent for basic conditions, such as whether the credentials provided actually exist, whether the credentials fit any restrictions imposed as part of the proof request, etc. Upon passing the check by the cloud agent, the Wallet Server responds to the user to notify that their credentials are acceptable. Then, the relevant information from the credentials are sent over to the verifier for further validation on their end to determine whether the user can access their services or not.

Open API Specifications for Verifiable Credentials

Sample Open API Specifications for Verifiable Credentials


# To view this API spec in a UI, navigate to editor.swagger.io, or a local instance of swagger running, or the openapi widget in confluence, and paste this file into the text editor section.
openapi: 3.0.0
info:
title: "Wallet Server VC API"
description: "Wallet Server Verifiable Credentials API"
termsOfService: "https://identos.ca/terms/"
contact:
name: "API Support"
url: "https://identos.ca"
version: 1.0.4
servers:
- url: https://127.0.0.1:8084/

paths:
/webhooks/vc/topic/issue_credential_v2_0:
post:
tags:
- Webhook Handling
description: "Captures a credential exchange webhook corresponding to hyperledger aries webhook specifications. This endpoint should only be used by the cloud agent."
summary: "Credential Exchange Event Capture"
parameters:
- name: x-wallet-id
in: header
required: true
schema:
type: string
requestBody:
description: A JSON containing the details of the credential issuance event provided by the aries cloud agent
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/issued-credential-webhook"
responses:
200:
description: Webhook Handled Successfully
400:
description: Input Error
404:
description: Wallet ID not found
500:
description: Internal Server Error
/webhooks/vc/webhook/topic/present_proof_v2_0:
post:
tags:
- Webhook Handling
description: "Captures a proof exchange webhook corresponding to hyperledger aries webhook specifications. This endpoint should only be used by the cloud agent."
summary: "Proof Exchange Event Capture"
parameters:
- name: x-wallet-id
in: header
required: true
schema:
type: string
requestBody:
description: A JSON containing the details of the proof exchange event provided by the aries cloud agent
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/present-proof-webhook"
responses:
200:
description: Webhook Handled Successfully
400:
description: Input Error
404:
description: Wallet ID not found
500:
description: Internal Server Error


/me/verifiable-credentials/credential-offers:
get:
tags:
- Credential Issuance
description: "Retrieves a list of credential offers"
summary: "List Offers"
parameters:
- name: state
in: query
required: true
schema:
type: string
example: "PENDING, EXPIRED, ACCEPTED, DECLINED"
security:
- bearerAuth: []
responses:
200:
description: List of offers with the state given, or all offers if no state is given
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/credential-offers-endpoint-response"
/me/verifiable-credentials/credential-offer/{credOfferId}:
put:
tags:
- Credential Issuance
description: "Takes the user's response to a credential offer - accept or decline"
summary: "User Response to Offer"
parameters:
- name: credOfferId
in: path
required: true
schema:
type: string
- name: accept
in: query
required: true
schema:
type: boolean
security:
- bearerAuth: []
responses:
200:
description: Successfully received acceptance or denial
400:
description: Credential offer has expired
content:
application/json:
schema:
$ref: "#/components/schemas/wallet-standard-error"
/me/verifiable-credentials/credential-offer/{credOfferId}/ready:
get:
tags:
- Credential Issuance
description: "Checks the status of a credential exchange. Intended for use after accepting a credential offer, as the user will need to wait while the issuer prepares the credential to send over, which is received via webhook asynchronously. The wallet will automatically store the credential when it is received via a webhook."
summary: "Check Credential Exchange Status"
parameters:
- name: credOfferId
in: path
required: true
schema:
type: string
security:
- bearerAuth: []
responses:
200:
description: Credential exchange record - state only
content:
application/json:
schema:
type: object
properties:
status:
type: boolean
/verifiable-credentials/stakeholders:
get:
tags:
- Verifiable Credentials
description: "Retrieves a list of verifiable credential stakeholders (issuers and verifiers known to our system)"
summary: "Get Issuers and Verifiers"
responses:
200:
description: List of verifiable credential stakeholders
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/VerifiableCredentialStakeholder"
/me/verifiable-credentials/credentials:
get:
tags:
- Verifiable Credentials
description: "Retrieves a list of verifiable credentials"
summary: "Get VCs"
security:
- bearerAuth: []
parameters:
- name: vc-stakeholder-id
in: query
required: false
schema:
type: string
default: ""
- name: expand-credentials
in: query
required: false
description: If true, credentials will also include their attributes, and the server will attempt to fetch attributes for any credentials which previously failed to fetch. (Defaults to false)
schema:
type: boolean
default: false
responses:
200:
description: List of verifiable credentials
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/SimplifiedVerifiableCredentialData"
/me/verifiable-credentials/credentials/{verifiableCredentialId}:
get:
tags:
- Verifiable Credentials
description: "Retrieves a single verifiable credential"
summary: "Get VC"
parameters:
- name: verifiableCredentialId
in: path
required: true
schema:
type: string
security:
- bearerAuth: []
responses:
200:
description: List of verifiable credentials
content:
application/json:
schema:
$ref: "#/components/schemas/VerifiableCredentialData"

/me/verifiable-credentials/connections:
get:
tags:
- Cloud Agent Connections
description: "Retrieves a list of all connections for the user"
summary: "Get VC Stakeholder Connections"
parameters:
- name: include-credentials
in: query
description: If set to false, credentials will not be included in the response. (defaults to true)
required: false
schema:
type: boolean
default: true
- name: expand-credentials
in: query
description: If set to false and include, causes credentials within to not include attributes or detailed information about them (defaults to false)
required: false
schema:
type: boolean
default: false
security:
- bearerAuth: []
responses:
200:
description: List of connections
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/SimplifiedVerifiableCredentialConnection"
post:
tags:
- Cloud Agent Connections
description: "Establishes a DIDComm connection with an issuer or verifier of verifiable credentials (referred to as \"stakeholder\") using the connection data provided by that stakeholder. If the user has never connected to any VC issuer or verifier before, a cloud agent account will automatically be created internally for them to use."
summary: "Connect VC Stakeholder"
requestBody:
description: A JWT of a JSON containing information required to establish a didcomm connection with an issuer or verifier. This JSON is generated by the aries cloud agent, and received through some means directly from the issuer or verifier outside of the system (i.e. QR scan)
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/connection-invite-jwt"
security:
- bearerAuth: []
responses:
200:
description: "Successfully established connection (Note: The credentials array will always be empty here)"
content:
application/json:
schema:
$ref: "#/components/schemas/VerifiableCredentialConnection"
400:
description: Failed Request
content:
application/json:
schema:
$ref: "#/components/schemas/wallet-standard-error"

/me/verifiable-credentials/connections/{cloudAgentConnectionId}:
get:
tags:
- Cloud Agent Connections
description: "Retrieves a connection for the user"
summary: "Get VC Stakeholder Connection"
parameters:
- name: cloudAgentConnectionId
in: path
required: true
schema:
type: string
- name: include-credentials
in: query
description: If set to false, credentials will not be included in the response. (defaults to true)
required: false
schema:
type: boolean
- name: expand-credentials
in: query
description: If set to false and include, causes credentials within to not include attributes or detailed information about them (defaults to true)
required: false
schema:
type: boolean
security:
- bearerAuth: []
responses:
200:
description: Single Connection
content:
application/json:
schema:
$ref: "#/components/schemas/VerifiableCredentialConnection"
/me/verifiable-credentials/credentials/{verifiableCredentialId}/activity:
get:
tags:
- Verifiable Credentials
description: "Compiles a list of activity items involving the verifiable credential - including issuance of the credential, the credential's usage in proof requests, and the revocation of the credential"
summary: "VC Activity"
parameters:
- name: verifiableCredentialId
in: path
required: true
schema:
type: string
security:
- bearerAuth: []
responses:
200:
description: List of activities
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/vc-activity-record"


/me/verifiable-credentials/proof-requests/{id}:
get:
tags:
- Proof Requests
description: "Retrieves the proof request record associated with the given exchange ID"
summary: "Get Proof Request Record"
parameters:
- name: id
in: path
required: true
schema:
type: string
security:
- bearerAuth: []
responses:
200:
description: Proof Request
content:
application/json:
schema:
$ref: "#/components/schemas/proof-request-record"
put:
tags:
- Proof Requests
description: "Respond to a proof request associated with the given exchange ID using credentials and inputs specified by the user"
summary: "Respond to Proof Request"
parameters:
- name: id
in: path
required: true
schema:
type: string
- name: accept
in: query
required: true
schema:
type: boolean
requestBody:
description: A JSON specifying which VerifiableCredentialResource should be used to fulfill all requested attributes and predicates in the request. This is only considered if accept is set to true
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/proof-response"
security:
- bearerAuth: []
responses:
200:
description: Sent to verifier for validation and use

/me/verifiable-credentials/proof-requests/{id}/valid-verifiable-credentials:
get:
tags:
- Proof Requests
description: "Retrieves a list of verifiable credentials which may be used to filfill the proof request"
summary: "Get Verifiable Credentials For Proof"
parameters:
- name: id
in: path
required: true
schema:
type: string
security:
- bearerAuth: []
responses:
200:
description: List of verifiable credentials
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/SimplifiedVerifiableCredentialData"
/me/verifiable-credentials/proof-requests:
get:
tags:
- Proof Requests
description: "Retrieves records of all proof requests. If query parameters are provided, the results are filtered by that query."
summary: "Get History of All Proof Requests"
parameters:
- name: state
in: query
required: true
schema:
type: string
example: "PENDING, ACCEPTED, REJECTED"
security:
- bearerAuth: []
responses:
200:
description: List of Proof Requests
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/proof-request-records"

/me/verifiable-credentials/credentials/{verifiableCredentialId}/revocation-status:
get:
tags:
- Verifiable Credentials
description: "Retrieves the revocation status of a single credential."
summary: "VC Revocation Status"
parameters:
- name: verifiableCredentialId
in: path
required: true
schema:
type: string
security:
- bearerAuth: []
responses:
200:
description: Revoked - true or false
content:
application/json:
schema:
type: object
properties:
revoked:
type: boolean

/verifiable-credentials/connection-redirect:
get:
tags:
- Cloud Agent Connections
description: "Captures a redirect from the issuer or verifier containing a JWT parameter which contains the data needed to establish a connection. This is intended to be used in the web flow where instead of scanning a QR code, the user clicks a redirect. The endpoint will either redirect to the webui or a mobile client (app redirect scheme determined by application.yml config) depending on the user-agent calling this endpoint."
summary: "WebUI Redirect Capture"
parameters:
- name: c_i
in: query
required: true
schema:
type: string
responses:
302:
description: Redirect to the webui's /profile path, or mobile app /profile bundle id, relaying the JWT contained in the c_i parameter as a c_i parameter to the webui

components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: string
schemas:
issued-credential-webhook:
type: object
properties:
connection_id:
type: string
state:
type: string
updated_at:
type: string
initiator:
type: string
cred_offer:
$ref: "#/components/schemas/credentialExchangeOffer"
created_at:
type: string
thread_id:
type: string
cred_ex_id:
type: string
auto_remove:
type: boolean
auto_offer:
type: boolean
auto_issue:
type: boolean
role:
type: string
trace:
type: boolean
by_format:
type: string
additionalProperties:
type: string
cred_issue:
$ref: "#/components/schemas/credentialExchangeOffer"
error_msg:
type: string
credentialExchangeOffer:
type: object
properties:
"@id":
type: string
"@type":
type: string
"~thread":
type: string
offers~attach:
type: object
additionalProperties:
type: string
present-proof-webhook:
type: object
properties:
presExId:
type: string
threadId:
type: string
initiator:
type: string
presentationProposalDict:
type: object
presentationRequest:
type: object
presentation:
type: object
verified:
type: string
autoPresent:
type: boolean
errorMsg:
type: string
credential-offers-endpoint-response:
type: object
properties:
comment:
type: string
credential_offer_id:
type: string
connection_id:
type: string
state:
type: string
credential_preview:
$ref: "#/components/schemas/credentialOfferPreview"
credential_received:
type: boolean
vc_stakeholder:
$ref: "#/components/schemas/VerifiableCredentialStakeholder"
expires_at:
type: string
format: date-time
#filter:
#$ref: "#/components/schemas/credentialOfferFilter"
credentialOfferPreview:
type: object
properties:
display_name:
type: string
example: "digital identity"
attributes:
type: array
items:
$ref: "#/components/schemas/credentialOfferPreviewAttributes"
credentialOfferPreviewAttributes:
type: object
properties:
mime_type:
type: string
name:
type: string
value:
type: string
credentialOfferFilter:
type: object
properties:
indy:
type: object
ld_proof:
type: object
proof-request-records:
type: object
properties:
comment:
type: string
id:
type: string
state:
type: string
example: "PENDING"
acknowledged_at:
type: string
format: date-time
vc_stakeholder:
$ref: "#/components/schemas/VerifiableCredentialStakeholder"
proof-request-record:
type: object
properties:
name:
type: string
comment:
type: string
id:
type: string
connection_id:
type: string
attributes:
$ref: "#/components/schemas/proofRequestAttribute"
predicates:
$ref: "#/components/schemas/proofRequestAttribute"
state:
type: string
example: "PENDING"
acknowledged_at:
type: string
format: date-time
vc_stakeholder:
$ref: "#/components/schemas/VerifiableCredentialStakeholder"
proofRequestAttribute:
type: array
items:
type: string
presentationFormat:
type: object
properties:
attach_id:
type: string
format:
type: string
requestPresentationsAttach:
type: object
properties:
"@id":
type: string
byte_count:
type: integer
data:
$ref: "#/components/schemas/presentationData"
description:
type: string
filename:
type: string
lastmod_time:
type: string
mimeType:
type: string
presentationData:
type: object
properties:
base64:
type: string
json:
type: string
jws:
type: object
links:
type: array
items:
type: string
sha256:
type: string
proof-response:
type: object
properties:
credential_id:
type: string
example:
"291bfaf0-97fa-431f-8621-75d39b29a11a"

# TODO update to use key value attribute for mapping requested predicate key to resource id
didcomm-connection-request:
type: object
properties:
"@id":
type: string
"@type":
type: string
"did":
type: string
"imageUrl":
type: string
"label":
type: string
"recipientKeys":
type: array
items:
type: string
"routingKeys":
type: array
items:
type: string
"serviceEndpoint":
type: string
connection-status:
type: object
properties:
state:
type: string
example: "active"
last_updated:
type: string
format: date-time
their_label:
type: string
example: "faber.agent"
vc_stakeholder_id:
type: string
connection-invite-jwt:
type: object
properties:
connection_jwt:
type: string
example: eyJAdHlwZSI6ICJkaWQ6c292OkJ6Q2JzTlloTXJqSGlxWkRUVUFTSGc7c3BlYy9jb25uZWN0aW9ucy8xLjAvaW52aXRhdGlvbiIsICJAaWQiOiAiZmViZDczNzAtMWQ3Ny00OTM0LWFlMjAtYTRhY2ZkMmU1YWJhIiwgImxhYmVsIjogImZhYmVyLmFnZW50IiwgInJlY2lwaWVudEtleXMiOiBbIjl0U1lQZFhRcmRUZmRwanhXaW1UVHlBVG83ZU0zd3g3UUpOOFM4Q3VoeVZWIl0sICJzZXJ2aWNlRW5kcG9pbnQiOiAiaHR0cDovLzdkYzktMjYwNy1mZWE4LTNlZGYtMWYxMC1mMWZlLWEyODYtYTczYS0zNjRhLm5ncm9rLmlvIn0=
VerifiableCredentialStakeholder:
type: object
properties:
vc_stakeholder_id:
type: string
required:
type: boolean
style:
$ref: "#/components/schemas/StyleResponse"
name:
type: string
issuer:
type: boolean
verifier:
type: boolean
StyleResponse:
type: object
properties:
style_color:
type: string
style_font_color:
type: string
logo_url:
type: string
vc-activity-record:
type: object
properties:
type:
type: string
example: "issued/shared/revoked"
value:
type: string
example: "Ontario Health"
time:
type: string
format: date-time
SimplifiedVerifiableCredentialData:
type: object
properties:
identifier:
type: string
type:
type: string
description:
type: string
name:
type: string
example: "digital identity"
connection:
type: string
format: uuid
received_at:
type: string
format: date
state:
type: string
example: OFFERED/ACCEPTED/READY
VerifiableCredentialData:
allOf:
- $ref: "#/components/schemas/SimplifiedVerifiableCredentialData"
- type: object
properties:
attributes:
type: array
items:
type: object
properties:
mime_type:
type: string
example: "text"
description: The mime type of this credential. If null, assume it is plain text.
name:
type: string
example: "First Name"
value:
type: string
example: "John Doe"

SimplifiedVerifiableCredentialConnection:
type: object
properties:
id:
type: string
format: uuid
alias:
type: string
last_active:
type: string
format: date-time
disabled_at:
type: string
format: date-time
vc_stakeholder_id:
type: string
description: "Note: This field will be deprecated in the next version, please use vc_stakeholder.vc_stakeholder_id instead"
vc_stakeholder:
$ref: "#/components/schemas/VerifiableCredentialStakeholder"
credentials:
type: array
items:
$ref: "#/components/schemas/SimplifiedVerifiableCredentialData"
VerifiableCredentialConnection:
type: object
properties:
id:
type: string
format: uuid
alias:
type: string
last_active:
type: string
format: date-time
disabled_at:
type: string
format: date-time
vc_stakeholder_id:
type: string
description: "Note: This field will be deprecated in the next version, please use vc_stakeholder.vc_stakeholder_id instead"
vc_stakeholder:
$ref: "#/components/schemas/VerifiableCredentialStakeholder"
credentials:
type: array
items:
$ref: "#/components/schemas/VerifiableCredentialData"
wallet-standard-error:
type: object
properties:
status:
type: number
example: 401
message:
type: string
timestamp:
type: string
format: date-time
alert_level:
type: string
example: "3"
lookup_code:
type: string
example: 70000