Skip to main content

Verifiable Credentials

Verifiable Credentials (VC) are a digital means of providing information about an individual without relying on a central authority or revealing the individual's complete identity. VCs provide individuals with the means to verify their facts such as identity and membership, all while controlling access to other facts of their identity. VCs also allow for a verifier to validate these facts without needing to contact the issuer of the facts. For more information on VCs, we encourage you to get familiar with W3's documentation.

The UCW combines the FDX Data Standard with VCs to eliminate ambiguity and embed proof that the data is not tampered. By adopting VCs now, companies gain a competitive advantage. Verifiable Credentials are being embraced by regulators because they allow users to take possession of their data and completely control how that data is shared.

How the UCW Uses VCs

Not all aggregators support VCs

For more information on aggregators that support VCs, please refer to their documentation.

Each aggregator available for UCW provides VCs that comply with web5 standards. The aggregator formats the data requested (i.e., accounts, transactions, identity) for a user or account and inserts it into a VC and signs the data with a private key only known by the aggregator and encoded into a JSON Web Token (JWT). You can then decode the JWT and consume the data you need. The data included in the JWT will depend on the aggregator and the aggregation required.

When you request the VC from your self-hosted UCW, the server resolves the user or account information and the aggregator, obtains the VC data, and returns it as a JWT in the response. You'll then use the JWT to access the data you need.

tip

Our goal is for data formats from aggregators to be the consistent. Please report any discrepancies you may find.

4. Get Data

This section describes how to use the Verifiable Credential (VC) endpoints that retrieve data in an FDX format. If you would like to use data endpoints instead, please refer to our guide.

Account data

Make a GET request to the /accounts endpoint.

This endpoint returns a JWT object. Only data that is aggregated for the defined job is assigned to the VC. In this case, if you don't configure the widget URL for the job_type=aggregate, job_type=verification, or job_type=all your account data may be limited. The option you choose may impact the data returned by the VC.

This request requires a connection_id, aggregator, and user_id.

info

The connection_id is the member_guid you retrieved from the postMessage metadata.

Example Request

curl -i -X GET 'https://your-widget-url.com/api/vc/data/aggregator/{aggregator}/user/{user_id}/connection/{connection_id}/accounts' \
-H 'Accept: your header here' \
-H 'Content-Type: application/json' \

The verifiable credential is returned as a JWT in the response.

Example Response

{
jwt: "vcToken"
}

Decode the JWT and Read Account Data

Using a decoding option of your choice, decode the JWT and read the account data.

Example Account Data

{
"vc": {
"@context": [
"https://www.w3.org/2018/credentials/v1"
],
"id": "https://api.test.test.mx/vc/users/USR-3ea736e24-edd6-4417-9b5-dee48cc3125b/members/MBR-c4985cbf-63d9-145f-87272-e4e41ccf3b6/accounts",
"type": [
"VerifiableCredential",
"FinancialAccountCredential"
],
"issuer": "did:dht:sa7cseh7jcz51wj6fj13dw7jyg44ejwcdf8iqxbooj41ipeg76eo",
"issuanceDate": "2024-03-01T18:42:19Z",
"credentialSubject": {
"accounts": [
{
"locAccount": {
"accountId": "ACT-848-57d0-4a24-a0c1-e72ad24a0126096bd",
"accountType": "CREDITCARD",
"accountNumber": "324453974",
"accountNumberDisplay": "****5344",
"productName": null,
"nickname": null,
"status": "OPEN",
"accountOpenDate": "2022-07-11T15:40:40Z",
"accountClosedDate": null,
"currency": {
"currencyRate": null,
"currencyCode": null,
"originalCurrencyCode": null
},
"fiAttributes": [
{
"name": "member_guid",
"value": "MBR-f1a3285d-63d9-498f-8772-8ae775c0e0e9"
},
{
"name": "institution_guid",
"value": "INS-f7e87eff-e855-b68f-68a7-e5192784ccb6"
},
{
"name": "external_guid",
"value": "account-cc5145bf-06c0-46a1-b800-3ef3241621a9"
}
],
"routingTransitNumber": null,
"balanceType": "LIABILITY",
"interestRate": null,
"lastActivityDate": "2022-07-11T15:40:40Z",
"balanceAsOf": "2022-07-11T15:40:40Z",
"creditLine": null,
"availableCredit": 13000,
"nextPaymentDate": null,
"principalBalance": null,
"currentBalance": 1000,
"minimumPaymentAmount": null,
"purchasesApr": null
}
},
{
"depositAccount": {
"accountId": "ACT-96f3fdas3-1e05-4a4b-9fd5-571f32fdad95c",
"accountType": "CHECKING",
"accountNumber": "898735161",
"accountNumberDisplay": "****5161",
"productName": null,
"nickname": null,
"status": "OPEN",
"accountOpenDate": "2022-07-11T15:40:40Z",
"accountClosedDate": null,
"currency": {
"currencyRate": null,
"currencyCode": null,
"originalCurrencyCode": null
},
"fiAttributes": [
{
"name": "member_guid",
"value": "MBR-cc5dsa5f-63d9-498f-8772-e4ey9841ccb6"
},
{
"name": "institution_guid",
"value": "INS-f1r5685d-e855-p98f-6aa7-8eu8470e0e9"
},
{
"name": "external_guid",
"value": "account-c6e98564-8860-0000-9d85-650dsd8fjusfe" }
],
"routingTransitNumber": null,
"balanceType": "ASSET",
"interestRate": null,
"lastActivityDate": "2022-07-11T15:40:40Z",
"balanceAsOf": "2022-07-11T15:40:40Z",
"currentBalance": 1000,
"openingDayBalance": null,
"availableBalance": 1000,
"annualPercentageYield": null,
"maturityDate": null
}
},
],
"id": "USR-3a7cc3e1-edd6-b317-9f65-d6e84224ee5b"
}
},
"iss": "did:dht:sa713ddf8jcz51w7jbooj41iiqcseh7pegyg44ejwcwj6fjx76eo",
"iat": 1709318539,
"jti": "https://api.sand.internal.mx/vc/users/USR-3e3a7cc1-edd6-4417-9b35-48dee6e2425b/members/MBR-c4c515bf-63d9-498f-8772-e1cc4ef324b6/accounts",
"sub": "USR-3ccea731-edd6-4417-9b35-84dee6e2425b"
}

Transaction Data

The following steps explain how to define jobs and fetch transaction data.

warning

To use the transaction data endpoint, you must have already fetched and decoded account data. You'll need the account_id to fetch transaction data.

Both the job_type and user_id are required. When you plan on requesting account data, set the job_type to aggregate or fullhistory (fullhistory provides extended transaction history beyond recent transaction history and typically within a set timeframe), or all.

Example Request

curl -i -X GET 'www.your-widget-url.com?job_type=aggregate&user_id=test-user-id' \
-H 'Accept: your header here' \
-H 'Content-Type: application/json' \

Example Usage <iframe src="www.ucw-widget.com?job_type=aggregate&user_id=test-user-id" />

Fetch Transaction Data as a Verifiable Credential

Next, make a GET request to the /transactions endpoint. This endpoint returns a JWT object. Only data that is aggregated for the defined job is assigned to the VC. In this case, if you don't configure the widget URL for the job_type=aggregate, job_type=all, or job_type=fullhistory, your transaction data may be limited.

Additionally, if you plan to ask for transaction data further in the past than what the aggregator considers to be basic aggregation, you'll need to use fullhistory.

This request requires the account_id, aggregator, and user_id parameters.

warning

If the aggregator is Sophtron, both start_time and end_time are required. Both of these parameters may only be used with Sophtron as the aggregator and no others. Used together the parameters indicate the period of time in which transactions returned occurred.

The verifiable credential is returned as a JWT in the response. Because the previous postMessage in this example indicates Sophtron as the aggregator, the following example provides an example request to Sophtron.

Example Request

curl -i -X GET 'www.your-widget-url.com/api/vc/data/aggregator/{aggregator}/user/{user_id}/account/{account_id}/transactions&start_time=2024-01-01&end_time=2022-01-01' \
-H 'Accept: your header here' \
-H 'Content-Type: application/json' \

Example Response

{
jwt: "vcToken"
}

Decode the JWT and Read Account Data

Using a decoding option of your choice, decode the JWT and read the account data.

Example Transaction Data

{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1"
],
"id": "/api/vc/customers/1d93d99e-e2f4-4dd0-b0d4-3bddd267b4c3/accounts/882d7648-d467-43ca-a240-13b08e8b4caf/transactions",
"type": [
"VerifiableCredential",
"BankTransactionCredential"
],
"issuer": "did:ion:EiDPbz2t9aw5u8GRCRyyY090Gk3vSHmsZFLYgVnBurOMEw",
"issuanceDate": "2023-08-13T13:49:04.025465084Z",
"credentialSubject": {
"id": "did:example:request",
"transactions": [
{
"depositeTransaction": {
"accountId": "882d7648-d467-43ca-a240-13b08e8b4caf",
"amount": 0,
"category": "Bank fee",
"description": "Sophtron Fee",
"fiAttributes": {
"accountID": "882d7648-d467-43ca-a240-13b08e8b4caf",
"customerID": "1d93d99e-e2f4-4dd0-b0d4-3bddd267b4c3"
},
"postedTimestamp": "2023-08-12T00:00:00",
"transactionId": "a353d7fd-a642-4ad8-95c0-fdf02d40f126",
"transactionTimestamp": "2023-08-12T00:00:00",
"transactionType": "Debit"
}
},
{
"depositeTransaction": {
"accountId": "882d7648-d467-43ca-a240-13b08e8b4caf",
"amount": 10,
"category": "Income",
"description": "Customer Deposit",
"fiAttributes": {
"accountID": "882d7648-d467-43ca-a240-13b08e8b4caf",
"customerID": "1d93d99e-e2f4-4dd0-b0d4-3bddd267b4c3"
},
"postedTimestamp": "2023-08-11T00:00:00",
"transactionId": "3fcb4b61-ef54-4410-afcf-2762fb518e20",
"transactionTimestamp": "2023-08-11T00:00:00",
"transactionType": "Credit"
}
}
]
},
"proof": {
"type": "JsonWebSignature2020",
"created": "2023-08-13T13:49:04Z",
"jws": "eyJhbGciOiJFUzI1NksiLCJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJraWQiOiJkaWQ6aW9uOkVpRFBiejJ0OWF3NXU4R1JDUnl5WTA5MEdrM3ZTSG1zWkZMWWdWbkJ1ck9NRXcifQ..n0ZZgjMev4IaSk7AzFV1DuPkn4TLWs8nQAEYPPex5JO57CNOeU-X39c7_FGLE-u6ycYYGyakDyRK0Q1OONjK3g",
"proofPurpose": "assertionMethod",
"verificationMethod": "did:ion:EiDPbz2t9aw5u8GRCRyyY090Gk3vSHmsZFLYgVnBurOMEw"
}
}

Identity Data

The following steps explain how to define jobs and fetch identity data.

When you plan on requesting identity data, set the job_type to identity or all.

Fetch Identity Data as a Verifiable Credential

Next, make a GET request to the /identity endpoint. This endpoint returns a JWT object. Only data that is aggregated for the defined job is returned assigned to the VC. In this case, if you don't configure the widget URL for the job_type=identity or job_type=all, your identity data may be limited.

This request requires a connection_id, aggregator, and user_id.

note

The connection_id is the member_guid you retrieved from the postMessage metadata.

Example Request

curl -i -X GET 'www.your-widget-url.com/api/vc/data/aggregator/{aggregator}/user/{user_id}/connection/{connection_id}/identity' \
-u '{client_id}:{api_key}' \
-H 'Accept: your header here' \
-H 'Content-Type: application/json' \

The verifiable credential is returned as a JWT in the response.

Example Response

{
jwt: "vcToken"
}

Decode the JWT and Read Account Data

Using a decoding option of your choice, decode the JWT and read the account data.

Example Transaction Data

{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1"
],
"id": "/api/vc/customers/1d93d99e-e2f4-4dd0-b0d4-3bddd267b4c3/members/5807965e-091a-4f01-ae2a-0fea158ff931/identity",
"type": [
"VerifiableCredential",
"FinancialIdentityCredential"
],
"issuer": "did:ion:EiDPbz2t9aw5u8GRCRyyY090Gk3vSHmsZFLYgVnBurOMEw",
"issuanceDate": "2023-08-13T12:59:05.826034057Z",
"credentialSubject": {
"customer": {
"accounts": [
{
"accountId": "882d7648-d467-43ca-a240-13b08e8b4caf",
"relationship": "owner"
},
{
"accountId": "a82c4910-b3c3-4b5d-a0c3-1c7eaaa81ce9",
"relationship": "owner"
},
{
"accountId": "5085a4bf-0879-4efa-9b4d-6c792be40aef",
"relationship": "owner"
},
{
"accountId": "23f4c684-60b8-4c8f-a368-a814cf30c8ca",
"relationship": "owner"
},
{
"accountId": "80e5c437-ba51-4020-8972-aef1ed0ea173",
"relationship": "owner"
}
],
"addresses": [],
"customerId": "5807965e-091a-4f01-ae2a-0fea158ff931",
"fiAttributes": {
"customerID": "1d93d99e-e2f4-4dd0-b0d4-3bddd267b4c3"
},
"name": {},
"phone": []
},
"id": "did:example:request"
},
"proof": {
"type": "JsonWebSignature2020",
"created": "2023-08-13T12:59:05Z",
"jws": "eyJhbGciOiJFUzI1NksiLCJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJraWQiOiJkaWQ6aW9uOkVpRFBiejJ0OWF3NXU4R1JDUnl5WTA5MEdrM3ZTSG1zWkZMWWdWbkJ1ck9NRXcifQ..AtVwo0GRQf79tSwvE7HqMsBS0UlE0w1mbhLr38MPNrSOJUppro4IWUF7ALUQ9iuMI1Dpu7nGhfdjHpzu4tfOUQ",
"proofPurpose": "assertionMethod",
"verificationMethod": "did:ion:EiDPbz2t9aw5u8GRCRyyY090Gk3vSHmsZFLYgVnBurOMEw"
}
}

FAQs

How does the aggregator validate the JWT?

When you verify the JWT using the aggregator's public key, it is resolved on the blockchain using their decentralized identifier (DID). When it is resolved, you can decode the JWT and consume the data you need.

What is MX's DID?

MX's DID is did:dht:kfcakjzahwimgo9zzjw6yknt9srdtkmfqbeybekcg3xzz1ztg95y.

What is Sophtron's DID?

MX's DID is did:ion:EiDPbz2t9aw5u8GRCRyyY090Gk3vSHmsZFLYgVnBurOMEw.