Skip to main content
Version: v0.25.0 (Latest)

Working with Designs

The IDK manages three types of design entities: credential designs for claim presentation, issuer designs for provider branding, and verifier designs for relying party display. This guide covers creating, querying, and updating these entities, along with their displays, claim definitions, and render variants.

Credential Designs

A credential design describes how a credential type should be presented to users. It combines localized display names, claim presentation rules, bindings that connect it to specific credential types, and render variants for different visual contexts.

Creating a Credential Design

val design = designService.createCredentialDesign(
tenantId = tenantId,
input = CreateCredentialDesignInput(
alias = "identity-credential",
bindings = listOf(
DesignBinding(
key = DesignBindingKey.VCT,
value = "https://issuer.example.com/identity"
),
DesignBinding(
key = DesignBindingKey.CREDENTIAL_CONFIGURATION_ID,
value = "IdentityCredential"
)
),
displays = listOf(
LocalizedCredentialDisplay(
locale = "en",
name = "Identity Credential",
description = "Government-issued digital identity"
),
LocalizedCredentialDisplay(
locale = "nl",
name = "Identiteitsbewijs",
description = "Door de overheid uitgegeven digitale identiteit"
)
),
claims = listOf(
ClaimPresentation(
path = DesignClaimPath(listOf(ClaimPathSegment.Property("given_name"))),
labels = listOf(
ClaimLabel(locale = "en", label = "First Name"),
ClaimLabel(locale = "nl", label = "Voornaam")
),
mandatory = true,
order = 1,
valueKind = ClaimValueKind.STRING,
widgetHint = ClaimWidgetHint.TEXT
),
ClaimPresentation(
path = DesignClaimPath(listOf(ClaimPathSegment.Property("family_name"))),
labels = listOf(
ClaimLabel(locale = "en", label = "Last Name"),
ClaimLabel(locale = "nl", label = "Achternaam")
),
mandatory = true,
order = 2,
valueKind = ClaimValueKind.STRING,
widgetHint = ClaimWidgetHint.TEXT
),
ClaimPresentation(
path = DesignClaimPath(listOf(ClaimPathSegment.Property("birth_date"))),
labels = listOf(
ClaimLabel(locale = "en", label = "Date of Birth"),
ClaimLabel(locale = "nl", label = "Geboortedatum")
),
mandatory = true,
order = 3,
valueKind = ClaimValueKind.DATE,
widgetHint = ClaimWidgetHint.DATE
),
ClaimPresentation(
path = DesignClaimPath(listOf(ClaimPathSegment.Property("portrait"))),
labels = listOf(ClaimLabel(locale = "en", label = "Photo")),
mandatory = false,
order = 4,
valueKind = ClaimValueKind.IMAGE,
widgetHint = ClaimWidgetHint.IMAGE,
sdPolicy = SdPolicy.ALLOWED
)
)
)
)

The bindings list is key: it determines which credentials this design applies to. A design with both a VCT and CREDENTIAL_CONFIGURATION_ID binding will match credentials identified by either property.

Querying Designs

Designs can be retrieved by ID, binding, or listed with filters.

// By ID
val design = designService.getCredentialDesign(tenantId, designId)

// By exact binding
val result = designService.findCredentialDesignByBinding(
tenantId = tenantId,
binding = DesignBinding(
key = DesignBindingKey.VCT,
value = "https://issuer.example.com/identity"
)
)
val designs = result.value // IdkResult<List<CredentialDesignRecord>, IdkError>

// By binding key and value (returns first match)
val result = designService.findCredentialDesignByBindingKey(
tenantId = tenantId,
bindingKey = DesignBindingKey.CREDENTIAL_CONFIGURATION_ID,
bindingValue = "IdentityCredential"
)
val designs = result.value // IdkResult<List<CredentialDesignRecord>, IdkError>

// List all designs
val designs = designService.listCredentialDesigns(tenantId)

Claim Presentation

Claims are the most detailed part of a credential design. Each ClaimPresentation controls how a single claim is displayed to the user.

Claim Paths

Claims are identified by their path within the credential structure. Paths use segments that can be property names, array indices, or wildcard array elements:

// Simple property: "family_name"
DesignClaimPath(listOf(ClaimPathSegment.Property("family_name")))

// Nested property: "address.city"
DesignClaimPath(listOf(
ClaimPathSegment.Property("address"),
ClaimPathSegment.Property("city")
))

// mDoc namespace: "org.iso.18013.5.1.family_name"
DesignClaimPath(listOf(
ClaimPathSegment.Property("org.iso.18013.5.1"),
ClaimPathSegment.Property("family_name")
))

// Array element: "addresses[0].city"
DesignClaimPath(listOf(
ClaimPathSegment.Property("addresses"),
ClaimPathSegment.Index(0),
ClaimPathSegment.Property("city")
))

Value Kinds and Widget Hints

The valueKind tells the wallet what type of data the claim contains, while widgetHint suggests how to render it. These work together to produce appropriate UI:

Value KindWidget HintsTypical Rendering
STRINGTEXT, MULTILINE_TEXT, BADGEText field, text area, or colored badge
DATEDATEFormatted date with locale-aware formatting
DATE_TIMEDATE_TIMEFormatted timestamp
BOOLEANCHECKBOXToggle or checkmark
IMAGEIMAGERendered image (base64 or URI)
URIURIClickable link
NUMBERTEXTFormatted number with optional unit
ARRAYLIST, TABLEList items or table rows
OBJECTGROUPNested group of sub-claims
MARKDOWNMARKDOWNRendered markdown content

Selective Disclosure Policy

For SD-JWT credentials, the sdPolicy controls whether a claim can be selectively disclosed:

PolicyMeaning
ALWAYSThis claim is always selectively disclosable and must be individually consented
ALLOWEDThe claim may be disclosed selectively if the wallet and verifier support it
NEVERThis claim is always included in presentations (e.g., credential type identifiers)

Claim Groups and Ordering

Claims can be organized into groups and ordered within those groups. The order field controls display sequence, and the group field allows visual grouping in wallet UIs:

ClaimPresentation(
path = DesignClaimPath(listOf(ClaimPathSegment.Property("given_name"))),
labels = listOf(ClaimLabel(locale = "en", label = "First Name")),
order = 1,
group = "personal-info"
)

Issuer Designs

Issuer designs store branding information for credential issuers: display names, descriptions, and logos. They are linked to issuers through bindings or a partyId reference.

val issuerDesign = designService.createIssuerDesign(
tenantId = tenantId,
input = CreateIssuerDesignInput(
alias = "example-issuer",
bindings = listOf(
DesignBinding(
key = DesignBindingKey.ISSUER_ID,
value = "https://issuer.example.com"
)
),
displays = listOf(
EntityLocaleDesign(
locale = "en",
displayName = "Example Government Agency",
description = "Official credential issuer"
)
)
)
)

A credential design can reference an issuer design through its issuerDesignId field, linking the credential's display metadata to the issuer's branding.

Verifier Designs

Verifier designs follow the same structure as issuer designs but are used when displaying information about relying parties in consent screens:

val verifierDesign = designService.createVerifierDesign(
tenantId = tenantId,
input = CreateVerifierDesignInput(
alias = "airport-verifier",
bindings = listOf(
DesignBinding(key = DesignBindingKey.CUSTOM, value = "airport-border-control")
),
displays = listOf(
EntityLocaleDesign(
locale = "en",
displayName = "Airport Border Control",
description = "Identity verification at border crossing"
)
)
)
)

Render Variants

Render variants define how a credential is visually represented. A single design can have multiple variants for different contexts: a simple card for list views, an SVG template for detail views, and a PDF template for printing.

Simple Card

The simplest render variant defines colors and an optional logo:

val variant = designService.createRenderVariant(
tenantId = tenantId,
input = CreateRenderVariantInput(
kind = RenderVariantKind.SIMPLE_CARD,
alias = "identity-card-light",
localeApplicability = listOf("en", "nl"),
colors = CardColors(
background = "#1a365d",
text = "#ffffff",
accent = "#63b3ed"
),
logo = AssetReference(
uri = "https://issuer.example.com/logo-white.png",
altText = "Issuer Logo"
)
)
)

SVG Template

SVG templates provide rich credential visuals with placeholder substitution. The template can reference claim values using placeholder syntax, and the IDK handles substitution at render time:

val svgVariant = designService.createRenderVariant(
tenantId = tenantId,
input = CreateRenderVariantInput(
kind = RenderVariantKind.SVG_TEMPLATE,
alias = "identity-svg-portrait",
svgTemplate = SvgTemplate(
uri = "https://issuer.example.com/templates/identity.svg",
orientation = SvgOrientation.PORTRAIT,
colorScheme = SvgColorScheme.LIGHT,
contrast = SvgContrast.NORMAL
)
)
)

W3C Render Method

For credentials following the W3C VC Render Method specification, you can reference external render method definitions:

val w3cVariant = designService.createRenderVariant(
tenantId = tenantId,
input = CreateRenderVariantInput(
kind = RenderVariantKind.W3C_RENDER_METHOD,
alias = "w3c-html-render",
w3cRenderMethod = W3cRenderMethodReference(
type = "SvgRenderingTemplate2024",
uri = "https://issuer.example.com/renders/identity.svg",
mediaType = "image/svg+xml",
name = "Identity Credential Card",
digestMultibase = "zQmWvQ..."
)
)
)

Asset Management

Design assets (logos, backgrounds, SVG templates) can be uploaded and managed through the design service. Assets are stored in the IDK's blob store and referenced from designs via AssetReference.

// Upload a logo
val assetRef = designService.uploadDesignAsset(
tenantId = tenantId,
input = UploadDesignAssetInput(
designId = design.id,
assetType = DesignAssetType.LOGO,
content = logoPngBytes,
contentType = "image/png",
altText = "Issuer Logo"
)
)

// Retrieve the asset
val asset = designService.getDesignAsset(
tenantId = tenantId,
input = GetDesignAssetInput(
designId = design.id,
assetType = DesignAssetType.LOGO
)
)

Supported asset types are LOGO, BACKGROUND_IMAGE, SVG_TEMPLATE, and PDF_TEMPLATE.

Data Types

ClaimPresentation

data class ClaimPresentation(
val path: DesignClaimPath, // Claim path segments
val labels: List<ClaimLabel>, // Localized labels
val mandatory: Boolean = false, // Required claim
val sdPolicy: SdPolicy? = null, // Selective disclosure policy
val order: Int? = null, // Display ordering
val group: String? = null, // Visual grouping
val svgId: String? = null, // SVG template placeholder ID
val valueKind: ClaimValueKind? = null, // Data type hint
val widgetHint: ClaimWidgetHint? = null, // UI rendering hint
val markdownAllowed: Boolean = false, // Allow markdown rendering
val entryCodes: List<String>? = null, // Enumerated valid values
val unit: String? = null // Unit of measurement
)

RenderVariantRecord

data class RenderVariantRecord(
val id: String,
val tenantId: String,
val kind: RenderVariantKind, // SIMPLE_CARD, SVG_TEMPLATE, etc.
val alias: String? = null,
val localeApplicability: List<String>? = null,
val logo: AssetReference? = null,
val backgroundImage: AssetReference? = null,
val colors: CardColors? = null,
val svgTemplate: SvgTemplate? = null,
val w3cRenderMethod: W3cRenderMethodReference? = null,
val pdfTemplate: AssetReference? = null
)