API and Dashboard
Nested Attributes

Nested Attributes

⚠️

Nested attributes can only be configured through the API.
For the moment, because of the complexity involved with defining templates with nested attributes in the dashboard, nested attribute in SD-JWT credential and presentation templates can only be configured through the SD-JWT VC Credential Templates (opens in a new tab) and the Presentation Templates (opens in a new tab) APIs. This page covers example payloads of how to create templates with nested attributes through the API.

Nested attributes in credentials allow you to group certain attributes under a key. Instead of e.g. defining a person's first name, last name and address all top-level you can define a property person and address. The person would contain the person's name, and the address would contain the address details of the person.

Nested attributes are only available for credentials with the SD-JWT VC (sd-jwt-vc) format, and are not supported for Anoncreds (anoncreds) credentials.

This guide assumes you're familiar with creating credential and presentation templates through the API. If you're unfamiliar with this process, the guides on Issuing Credentials and Verifying Credentials can help you get started with a simple template.

Credential Template

There's two options for adding nested attributes to an SD-JWT VC credential template:

  1. Create the basis of the template through the dashboard, and afterwards update it through the API to add the nested attributes.
  2. Create the full template through the API.

Both methods are similar, the difference is that for the first approach you'd first have to fetch the existing template using the Retrieve sd-jwt-vc credential template (opens in a new tab) endpoint, after which you can call the Update sd-jwt-vc credential template (opens in a new tab).

For the second approach, you can directly call the Create sd-jwt-vc credential template (opens in a new tab) endpoint.

Let's expand on the example given above of a credential with a person's name and address details. The "Flat" tab shows the credential with the flat structure (before), and the "Nested" tab shows the credential with the nested structure (after).

{
  "name": "Person Identity",
  "description": "A personal identity card",
  "background": {
    "color": "#FFFFFF",
    "url": "https://example.com/image.png"
  },
  "text": {
    "color": "#000000"
  },
  "type": "PersonIdentity",
  "attributes": {
    "first_name": {
      "type": "string",
      "name": "First Name",
      "description": "First name of the person",
      "required": true,
      "alwaysDisclosed": false
    },
    "last_name": {
      "type": "string",
      "name": "Last Name",
      "description": "Last name of the person",
      "required": false,
      "alwaysDisclosed": false
    },
    "street_name": {
      "type": "string",
      "name": "Street Name",
      "description": "Street name where the person resides",
      "required": true,
      "alwaysDisclosed": false
    },
    "country": {
      "type": "string",
      "name": "Country",
      "description": "Country where the person resides",
      "required": true,
      "alwaysDisclosed": true
    }
  }
}

The full payload of a credential issued with this template will have the following structure:

{
  "person": {
    "first_name": "John",
    "last_name": "Doe"
  },
  "address": {
    "country": "NL",
    "street_name": "Cow Street"
  },
  "vct": "https://metadata.paradym.id/types/skxEAByH1N-PersonIdentity",
  "iss": "did:web:metadata.paradym.id:3f1a3c34-0a10-4c1e-999a-5da290b8491f",
  "iat": 1720521140
}

However the disclosed payload by default looks as follows, and even though person is always disclosed, the nested attributes within person are not.

{
  "person": {
    "_sd": ["Fi-2K6ApoLYdEeCt7kNoTtL-hXLzGHI9sgs7f5_vLiQ", "ncZW0KGY1Bov9y1uJEsBOrMji8Nr0g3oYPNEWaXeMEY"]
  },
  "_sd": ["DjGs-I0-nHLyUO6YmolGba_zapIlOdIHU0B_GaNqPII"],
  "_sd_alg": "sha-256",
  "vct": "https://metadata.paradym.id/types/skxEAByH1N-PersonIdentity",
  "iss": "did:web:metadata.paradym.id:3f1a3c34-0a10-4c1e-999a-5da290b8491f",
  "iat": 1720521140
}

Once you have constructed your credential template and are happy with the structure of the issued credential, you can start defining your presentation template.

Presentation Template

Similar to adding nested attributes to a credential template, adding nested attributes to a presentation template works similar and you have the choice to start in the dashboard, or directly leverage the API.

{
  "name": "Verify Person Identity",
  "description": "This information is requested to verify if you are residing in the Netherlands",
  "credentials": [
    {
      "format": "sd-jwt-vc",
      "type": "https://metadata.paradym.id/types/skxEAByH1N-PersonIdentity",
      "issuers": [
        "did:web:metadata.paradym.id:3f1a3c34-0a10-4c1e-999a-5da290b8491f"
      ],
      "attributes": {
        "first_name": {
          "type": "string"
        },
        "country": {
          "type": "string",
          "value": "NL"
        }
      }
    }
  ]
}

In both cases, the peron's first_name and country are requested. However, when the structure of the "Nested" tab is used, the nested properties will be requested from the credential.

Nested disclosures

When working with credentials with nested attributes, it is important to understand how nested attributes work in combination with selective disclosure.

The "Always disclosed" (alwaysDisclosed in the API) option on an attribute manages whether an attribute can be selectively disclosed. If "Always disclosed" is enabled, it means the attribute will always be presented to a verifier, even if it didn't request this parameter.

Let's take the person and address example from the credential template section. The following fields will be disclosed by default:

{
  // ✅ person is 'disclosed' as it has always disclosed set to true.
  "person": {
    // ❌ first_name is not disclosed as it has always disclosed set to false.
    "first_name": "John",
    // ❌ last_name is not disclosed as it has always disclosed set to false.
    "last_name": "Doe"
  },
  // ❌ address is not 'disclosed' as it has always disclosed set to false.
  "address": {
    // ❌ country is not disclosed even though it has always disclosed set to true. 
    // This is because the parent object (address) is not disclosed by default.
    "country": "NL",
    // ❌ street_name is not 'disclosed' as it has always disclosed set to false.
    "street_name": "Cow Street"
  },
 
  // metadata fields, these are always disclosed
  "vct": "https://metadata.paradym.id/types/skxEAByH1N-PersonIdentity",
  "iss": "did:web:metadata.paradym.id:3f1a3c34-0a10-4c1e-999a-5da290b8491f",
  "iat": 1720521140
}

This will result in the below payload being presented. person is present, because it is always disclosed. address is not present, because it is not disclosed by default.

{
  "person": {
 
  },
 
  // metadata fields, these are always disclosed
  "vct": "https://metadata.paradym.id/types/skxEAByH1N-PersonIdentity",
  "iss": "did:web:metadata.paradym.id:3f1a3c34-0a10-4c1e-999a-5da290b8491f",
  "iat": 1720521140
}

Now let's say we request the disclosure of person.first_name and address.street_name. This results in the following payload being disclosed. In the below example the only attribute that has not been disclosed is person.last_name. This is because the attribute has not been requested to be disclosed, and the attribute has alwaysDisclosed disabled.

{
  // ✅ disclosed because `person` has `alwaysDisclosed` enabled
  "person": {
    //  ✅ disclosed because `address.first_name` was requested to be disclosed
    "first_name": "John",
  },
 
  // ✅ disclosed because nested property `street_name` was requested to be disclosed
  // if a nested property is disclosed, the parent property needs to be disclosed as well
  "address": {
 
    // ✅ disclosed because `address.country` has `alwaysDisclosed` enabled
    // and because the parent attribute `address` is now disclosed for `address.street_name`
    // this attribute is now also disclosed. To prevent this fro happening, you can disable
    // `alwaysDisclosed` on the `address.country` attribute.
    "country": "NL",
 
    // ✅ disclosed because `address.street_name` was requested to be disclosed
    "street_name": "Cow Street"
  },
 
  // metadata fields, these are always disclosed
  "vct": "https://metadata.paradym.id/types/skxEAByH1N-PersonIdentity",
  "iss": "did:web:metadata.paradym.id:3f1a3c34-0a10-4c1e-999a-5da290b8491f",
  "iat": 1720521140
}

It is up to the issuer of the credential to determine the right combination of nested attributes and required disclosure of attributes.