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

Channels (L4)

L4 is the channel-rendering layer. A product-backed channel binds exactly one Data Product (L3) to a concrete output:

  • a form with fields and widgets;
  • a PDF document with placements and repeated sections;
  • an API payload with an external naming convention and path mappings; or
  • a verifiable credential with credential-format metadata and claim mappings.

An OID4VCI issuance channel is different. It composes one or more verifiable-credential channels through independent credential configurations and does not bind a Data Product directly.

Every product-backed channel carries the same setRef wire field. Its value is a DataProductRef with dataProductId and dataProductVersion. The field name is shared by the channel contract; the referenced resource is the L3 Data Product.

These layers are authored over REST. VDX presents a guided UI for the same channel model.

Two verifiable-credential channels derived from the same Data Product and branding. Each maps Data Product role and path selections to its own credential format.

Channel Routes and Lifecycle

The API is rooted at /api/model/v1:

ChannelCreateReplace
FormsPOST /channels/formsPUT /channels/forms/{channelId}
PDF documentsPOST /channels/pdfPUT /channels/pdf/{channelId}
Verifiable credentialsPOST /channels/credentialsPUT /channels/credentials/{channelId}
OID4VCI issuancePOST /channels/oid4vciPUT /channels/oid4vci/{channelId}
API payloadsPOST /channels/apiPUT /channels/api/{channelId}

Channel-neutral reads use GET /api/model/v1/channels/{channelId} and listing uses GET /api/model/v1/channels. The channel list returns a paged response with data and pagination.

The model lifecycle values are DRAFT and PUBLISHED. Channel responses include status in their concrete payload. Form channels also expose PUT /channels/forms/{channelId}/publication with { "published": true | false }. Branding has its own status endpoint, described below.

The Channel Envelope

Channel create, update, get, and list operations return a ChannelEnvelope. Its wire shape is a type discriminator plus the concrete payload:

{
"channel": "VERIFIABLE_CREDENTIAL",
"payload": {
"id": "9f10...sdJwtChannelId",
"tenantId": "acme",
"name": "Acme Employee - SD-JWT",
"version": 1,
"channel": "VERIFIABLE_CREDENTIAL",
"setRef": {
"dataProductId": "1b2c...dataProductId",
"dataProductVersion": 1
},
"fieldMappings": [],
"credentialFormat": "dc+sd-jwt",
"vct": "https://issuer.acme.example/employee",
"vctHostingMode": "HOSTED",
"claimMappings": [
{
"set": { "roleRef": "employee", "path": ["given_name"] },
"disclosure": "ALWAYS"
},
{
"set": { "roleRef": "employee", "path": ["residentialAddress", "country"] },
"claimPath": ["address", "country"],
"disclosure": "SELECTIVE"
}
],
"brandingRef": {
"brandingId": "b1f2...brandingId",
"version": 1
},
"status": "DRAFT"
}
}

Read channel to determine the concrete channel type, then decode payload as the matching channel. The concrete payload repeats the channel discriminator inline.

The Product-Backed VC Channel

A verifiable-credential channel binds exactly one Data Product to one credential format. It carries the credential wire identity and maps credential claims to Data Product selections. The same channel can supply the shape used by issuer design generation and verifier DCQL generation.

The channel is role-neutral:

  • an issuer can create a credential design from it;
  • a verifier can derive a DCQL query from it.

The key fields are:

FieldMeaning
setRef{ dataProductId, dataProductVersion }, pinning the Data Product snapshot.
credentialFormatThe wire format, such as dc+sd-jwt, vc+sd-jwt, mso_mdoc, or jwt_vc_json.
vctThe SD-JWT VC type identifier when the format uses a VCT.
vctHostingModeOptional hosting intent: HOSTED, EXTERNAL, or NONE.
doctypeThe mdoc doctype when credentialFormat is mso_mdoc.
namespaceChannel-level mdoc namespace. It is an identifier, not a claim-path segment.
jsonLdContextsJSON-LD @context URIs for a JSON-LD VC.
claimMappingsClaim-to-Data-Product mappings with an optional wire claimPath, optional mdoc namespace, and disclosure.
brandingRef{ brandingId, version }, pinning a credential-branding snapshot.

Each claimMappings entry uses set: { roleRef, path } to identify a selected Data Product path. disclosure is ALWAYS or SELECTIVE. When claimPath is omitted, it defaults to the Data Product path. When it is present, it is a flat array of clean segments. An mdoc namespace is supplied by namespace, never by adding the namespace to claimPath.

Credential Branding

Credential branding is a reusable L4 presentation overlay. It carries the localized credential display name, description, logo, colors, optional background image, and optional layout hint. It is authored once per credential type and referenced by each format-specific VC channel through brandingRef.

Create branding

POST /api/model/v1/branding
{
"name": "Acme Employee Credential branding",
"description": "Presentation overlay for the Acme Employee credential.",
"layout": "card-default",
"locales": [
{
"language": "en",
"name": "Employee Credential",
"description": "Acme Corp employee identity credential",
"logoUri": "https://acme.example/logo.png",
"backgroundColor": "#1A237E",
"textColor": "#FFFFFF",
"backgroundImageUri": null
},
{
"language": "nl",
"name": "Medewerkerscredential",
"description": "Acme Corp medewerkersidentiteitscredential",
"logoUri": "https://acme.example/logo.png",
"backgroundColor": "#1A237E",
"textColor": "#FFFFFF",
"backgroundImageUri": null
}
]
}

Use PATCH /api/model/v1/branding/{brandingId} to update draft metadata, locales, or layout. Use PUT /api/model/v1/branding/{brandingId}/status to set DRAFT or PUBLISHED:

{ "status": "PUBLISHED" }

The version can be incremented with POST /api/model/v1/branding/{brandingId}/version, which has no request body. A VC channel references the exact published branding snapshot with brandingRef.

Create the Employee SD-JWT VC Channel

This channel binds the published Data Product to dc+sd-jwt. It maps eight selected paths to credential claims. The relationship traversal residentialAddress.country is placed under address.country on the wire.

POST /api/model/v1/channels/credentials
{
"name": "Acme Employee - SD-JWT",
"setRef": {
"dataProductId": "1b2c...dataProductId",
"dataProductVersion": 1
},
"credentialFormat": "dc+sd-jwt",
"vct": "https://issuer.acme.example/employee",
"vctHostingMode": "HOSTED",
"brandingRef": {
"brandingId": "b1f2...employeeBrandingId",
"version": 1
},
"claimMappings": [
{ "set": { "roleRef": "employee", "path": ["given_name"] }, "disclosure": "ALWAYS" },
{ "set": { "roleRef": "employee", "path": ["family_name"] }, "disclosure": "ALWAYS" },
{ "set": { "roleRef": "employee", "path": ["employee_id"] }, "disclosure": "SELECTIVE" },
{ "set": { "roleRef": "employee", "path": ["job_title"] }, "disclosure": "ALWAYS" },
{ "set": { "roleRef": "employee", "path": ["employment_status"] }, "disclosure": "SELECTIVE" },
{ "set": { "roleRef": "employee", "path": ["email"] }, "disclosure": "SELECTIVE" },
{
"set": { "roleRef": "employee", "path": ["residentialAddress", "country"] },
"claimPath": ["address", "country"],
"disclosure": "SELECTIVE"
},
{
"set": { "roleRef": "employer", "path": ["legal_name"] },
"claimPath": ["employer"],
"disclosure": "ALWAYS"
}
]
}

PUT /api/model/v1/channels/credentials/{channelId} replaces the VC channel content and returns the same envelope shape.

Create the Business Card mdoc VC Channel

This channel binds the same Data Product to mso_mdoc. The channel-level namespace is combined with each claim element by the mdoc converter. doctype and namespace are separate fields.

POST /api/model/v1/channels/credentials
{
"name": "Acme Business Card - mdoc",
"setRef": {
"dataProductId": "1b2c...dataProductId",
"dataProductVersion": 1
},
"credentialFormat": "mso_mdoc",
"doctype": "org.acme.businesscard.1",
"namespace": "org.acme.businesscard.1",
"brandingRef": {
"brandingId": "b3c4...businessCardBrandingId",
"version": 1
},
"claimMappings": [
{ "set": { "roleRef": "employee", "path": ["given_name"] }, "disclosure": "ALWAYS" },
{ "set": { "roleRef": "employee", "path": ["family_name"] }, "disclosure": "ALWAYS" },
{ "set": { "roleRef": "employee", "path": ["job_title"] }, "disclosure": "ALWAYS" },
{ "set": { "roleRef": "employee", "path": ["email"] }, "disclosure": "SELECTIVE" },
{
"set": { "roleRef": "employee", "path": ["residentialAddress", "postal_code"] },
"claimPath": ["postal_code"],
"namespace": "org.acme.businesscard.1",
"disclosure": "SELECTIVE"
}
]
}

Create the OID4VCI Issuance Channel

An OID4VCI issuance channel composes the VC channels through credentialConfigurations. It has no setRef. Each configuration pins one VC channel and declares its own credential-configuration id, signing setup, and offer defaults.

POST /api/model/v1/channels/oid4vci
{
"name": "Acme Employee and Business Card issuance",
"credentialConfigurations": [
{
"credentialConfigurationId": "EmployeeCredential",
"channelRef": {
"channelId": "9f10...sdJwtChannelId",
"channelVersion": 1
},
"signing": {
"algorithm": "ES256",
"keyBinding": "did:jwk"
},
"offer": {
"grants": ["AUTHORIZATION_CODE"],
"txCodeRequired": false,
"validitySeconds": 600
}
},
{
"credentialConfigurationId": "BusinessCardMdoc",
"channelRef": {
"channelId": "a3b4...mdocChannelId",
"channelVersion": 1
},
"signing": {
"algorithm": "EdDSA",
"keyBinding": "x5c"
},
"offer": {
"grants": ["PRE_AUTHORIZED_CODE"],
"txCodeRequired": true,
"validitySeconds": 300
}
}
]
}

PUT /api/model/v1/channels/oid4vci/{channelId} replaces the issuance channel content and returns the same envelope shape. The credentialConfigurationId values are the identifiers advertised by the issuer and referenced by its offers.

List Channels and Branding

Use the channel list filters nameContains, channel, and dataProductId together with the shared pagination parameters:

GET /api/model/v1/channels?channel=VERIFIABLE_CREDENTIAL&dataProductId=1b2c...dataProductId&limit=10&offset=0

The response is a { data, pagination } envelope. Each item in data is a ChannelEnvelope; read the concrete channel at .data[].payload.

{
"data": [
{
"channel": "VERIFIABLE_CREDENTIAL",
"payload": {
"id": "9f10...sdJwtChannelId",
"name": "Acme Employee - SD-JWT",
"version": 1,
"channel": "VERIFIABLE_CREDENTIAL",
"credentialFormat": "dc+sd-jwt",
"vct": "https://issuer.acme.example/employee",
"status": "DRAFT"
}
},
{
"channel": "OID4VCI",
"payload": {
"id": "c5d6...oid4vciChannelId",
"name": "Acme Employee and Business Card issuance",
"version": 1,
"channel": "OID4VCI",
"credentialConfigurations": [],
"status": "DRAFT"
}
}
],
"pagination": {
"limit": 10,
"offset": 0,
"page": 0,
"size": 2,
"total": 2,
"totalPages": 1,
"hasMore": false
}
}

Branding uses the corresponding /api/model/v1/branding resource. Its list response also uses { data, pagination }, and status accepts DRAFT or PUBLISHED.

License

The semantic-model authoring API is gated by the semantic-modeling.v1 enterprise feature. Every request in this walkthrough uses an operator bearer token, and the tenant context is resolved from that token.

What You Have Now

The example now has one Data Product, two VC channels in different credential formats, shared credential branding, and one OID4VCI issuance channel that composes both VC channels. Issuer design and verifier DCQL generation can consume the same VC channel, keeping the credential shape and requested shape aligned.

Next Steps

  • Issuing: create issuer designs and offers from the VC channels.
  • Verifying: derive a DCQL query from the same VC channels.
  • Data Product (L3): review the role-rooted paths consumed by these channels.
  • Modeling Your World: review the layer rules and governance model.