Verifiable Credentials
Verifiable Credentials (VCs) are secure digital credentials that allow users to share their financial data while maintaining control over their privacy.
In UCW, VCs combine the FDX Data Standard with VCs to:
- Allow users to securely prove facts without revealing their entire identity
- Provide tamper-evident data that can be verified without contacting the original issuer
- Enable complete user control over data sharing
These endpoints are disabled by default. See Enable UCW Data Endpoints.
For technical details, see W3C's Verifiable Credentials Data Model.
Regulators increasingly favor VCs as they enhance user privacy and control. Implementing VCs now gives your application a competitive advantage in the evolving regulatory landscape.
Aggregator Support
Not all aggregators support VCs. Check the table below and refer to specific documentation:
Aggregator | Connection | Data Endpoints | VC Endpoints |
---|---|---|---|
Akoya | ✔ | ✘ | ✘ |
Finicity | ✔ | ✔ | ✘ |
MX | ✔ | ✔ | ✔ |
Sophtron | ✔ | ✔ | ✔ |
Supported functionality by aggregator
How UCW Uses VCs
- The aggregator formats user data (accounts, transactions, identity) according to FDX standards
- The aggregator signs this data with their private key and packages it as a Verifiable Credential
- Your application requests this data from UCW, which returns it as a JWT
- You verify the JWT using the aggregator's decentralized identifier (DID)
- After you decode the JWT, you use the standardized data in your application
We strive for consistency in data formats across aggregators. Please report any discrepancies you encounter.
Decentralized Identifiers (DIDs)
Each aggregator has a unique DID for verification:
- MX:
did:dht:kfcakjzahwimgo9zzjw6yknt9srdtkmfqbeybekcg3xzz1ztg95y
- Sophtron:
did:ion:EiDPbz2t9aw5u8GRCRyyY090Gk3vSHmsZFLYgVnBurOMEw
Using VC Endpoints
This section describes how to use the Verifiable Credential (VC) endpoints that retrieve data in an FDX format. If you'd like to use data endpoints instead, see UCW Data Endpoints.
Before making any VC requests, you must first listen for the postMessage when a user connects an account.
The userId
in your data requests must match the userId
configured in the widget.
Common Implementation Steps
- Configure appropriate
jobtypes
in your widget URL to ensure necessary data is collected - Extract the
member_guid
from the widget's postMessage event - Make API requests to the appropriate VC endpoint
- Verify and decode the returned JWT
- Process the standardized FDX data
Account data
To retrieve account data:
- Define your
jobtypes
in the widget URL. Only data that is aggregated for the defined job is assigned to the VC. - Capture the
member_guid
in the postmessage event and use it as theconnectionId
in the VC request. - In the VC request, also set the
aggregator
anduserId
. - Use a decoding option of your choice to decode the JWT and read the account data.
Example Request
curl -i -X GET 'https://your-widget-url.com/api/vc/data/aggregator/{aggregator}/user/{userId}/connection/{connectionId}/accounts' \
-H 'Accept: your header here' \
-H 'Content-Type: application/json' \
Example Response
{
jwt: "vcToken"
}
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
To retrieve transactions data:
- Define your
jobtypes
in the widget URL. Only data that is aggregated for the defined job is assigned to the VC.transactions
for basic, recent historytransactionHistory
for a set of transactions that cover a longer timeframe
- In the VC request, set the
accountId
,aggregator
, anduserId
.accountId
must have already been fetched in anaccount
call. - (Optional) Use these parameters on the VC data request:
startDate
- ISO 8601 format (YYYY-MM-DD). defaults to 120 days agoendDate
- ISO 8601 format (YYYY-MM-DD). defaults to 5 days in the future
- Use a decoding option of your choice to decode the JWT and read the account data.
The verifiable credential is returned as a JWT in the response. The following example uses the aggregator, Sophtron, with their required date parameters.
Example Request
curl -i -X GET 'www.your-widget-url.com/api/vc/data/aggregator/{aggregator}/user/{userId}/account/{accountId}/transactions&startDate=2024-01-01&endDate=2022-01-01' \
-H 'Accept: your header here' \
-H 'Content-Type: application/json' \
Example Response
{
jwt: "vcToken"
}
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
To retrieve identity data:
- Define your
jobtypes
asaccountOwner
in the widget URL. Only data that is aggregated for the defined job is assigned to the VC. - Capture the
member_guid
in the postmessage event and use it as theconnectionId
in the VC request. - In the VC request, also set the
aggregator
anduserId
. - Use a decoding option of your choice to decode the JWT and read the account data.
Example Request
curl -i -X GET 'www.your-widget-url.com/api/vc/data/aggregator/{aggregator}/user/{userId}/connection/{connectionId}/identity' \
-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"
}
Example Identity 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"
}
}