The Data Product (L3)
A Data Product is a role-rooted traversal subselection of exactly one published Object Model (L2). The Object Model defines the effective use-case graph. The Data Product selects the business terms needed by one issuance, verification, form, document, or API scenario and can narrow their governance further.
The Data Product is the consumable L3 contract for Channels (L4). It is tenant-owned, versioned, and uses the DRAFT | PUBLISHED lifecycle.
VDX presents a guided authoring UI for the Data Product layer. The concepts and resulting model are identical whether you use that UI or the REST API documented here.
Core Concepts
objectModelRef, pinning one Object Model snapshot
A Data Product references exactly one published Object Model with objectModelRef: { objectModelId, objectModelVersion }. The version pin keeps resolution stable when the Object Model later receives another snapshot.
selected, role-rooted traversal paths
selected is an array of { roleRef, path } entries. roleRef identifies an Object Model role and path is an array of string segments rooted at that role's object type. A path can walk a specialization, an inline compound, or a relationship into a related object type.
For example, the employee role can cross the residentialAddress relationship to select country on the related Address object type:
{ "roleRef": "employee", "path": ["residentialAddress", "country"] }
A direct business term uses a single segment:
{ "roleRef": "employee", "path": ["given_name"] }
The same path shape represents direct and relationship traversal selections. The resolver interprets each path against the pinned Object Model graph.
overrides, narrow-only tightening
overrides contains { roleRef, path, ... } entries keyed to selected paths. Each entry may tighten the effective Object Model value but cannot widen it.
| Field | Effect |
|---|---|
conformance | Narrows OPTIONAL to MANDATORY. |
cardinality | Narrows MULTIPLE to SINGLE. |
entryCodesSubset | Restricts a picklist to a non-empty subset of the effective codes. |
sensitive | Tightens sensitivity to true. |
The walkthrough narrows employment_status to entryCodesSubset: ["active", "suspended"]. An override must target a selected (roleRef, path) pair, and every code must already exist in the resolved Object Model term.
The Data Product contains selection and governance narrowing only. Rendering fields such as form widgets, document placement, claim paths, and credential branding belong to L4 channels.
From traversal to channel output
Channels consume the same (roleRef, path) selections. A form maps one to a field, a PDF channel maps one to a placement, an API channel maps one to an external payload path, and a verifiable-credential channel maps one to a claim. The Data Product declares how to reach a business term; the channel declares where that value appears in its output.
For example:
| Data Product path | Channel wire path |
|---|---|
["residentialAddress", "country"] | SD-JWT claimPath: ["address", "country"] |
["residentialAddress", "postal_code"] | mdoc claimPath: ["postal_code"], under the channel namespace |
See Channels (L4) for the channel-specific request and response shapes.
REST: Create, Select, Publish, and Resolve a Data Product
The API is rooted at /api/model/v1. Requests use an operator bearer token, with tenant context resolved from that token.
Authorization: Bearer <operator access token>
Content-Type: application/json
Create the Data Product
- Request
- Response
POST /api/model/v1/data-products
{
"name": "Acme Employee Badge",
"description": "Employee identity, residential country, and employer name.",
"objectModelRef": {
"objectModelId": "c4d5e6f7-8a9b-4c0d-8e1f-2a3b4c5d6e7f",
"objectModelVersion": 1
},
"selected": [
{ "roleRef": "employee", "path": ["given_name"] },
{ "roleRef": "employee", "path": ["family_name"] },
{ "roleRef": "employee", "path": ["employee_id"] },
{ "roleRef": "employee", "path": ["job_title"] },
{ "roleRef": "employee", "path": ["employment_status"] },
{ "roleRef": "employee", "path": ["email"] },
{ "roleRef": "employee", "path": ["residentialAddress", "postal_code"] },
{ "roleRef": "employee", "path": ["residentialAddress", "country"] },
{ "roleRef": "employer", "path": ["legal_name"] }
],
"overrides": [
{
"roleRef": "employee",
"path": ["employment_status"],
"entryCodesSubset": ["active", "suspended"]
}
]
}
201 Created returns a DataProduct. The server assigns the UUID, starts version at 1, and creates the product with status: "DRAFT".
{
"id": "1b2c3d4e-5f60-4a71-8b92-0c1d2e3f4a5b",
"tenantId": "acme",
"name": "Acme Employee Badge",
"version": 1,
"status": "DRAFT",
"objectModelRef": {
"objectModelId": "c4d5e6f7-8a9b-4c0d-8e1f-2a3b4c5d6e7f",
"objectModelVersion": 1
},
"selected": [
{ "roleRef": "employee", "path": ["given_name"] },
{ "roleRef": "employee", "path": ["employment_status"] }
],
"overrides": [
{
"roleRef": "employee",
"path": ["employment_status"],
"entryCodesSubset": ["active", "suspended"]
}
],
"description": "Employee identity, residential country, and employer name.",
"createdAt": "2026-01-15T09:30:00Z",
"updatedAt": "2026-01-15T09:30:00Z"
}
Replace the selection
The create request can include selected and overrides. A draft product's selection can also be replaced wholesale:
PUT /api/model/v1/data-products/{dataProductId}/selection
{
"selected": [
{ "roleRef": "employee", "path": ["given_name"] },
{ "roleRef": "employee", "path": ["family_name"] },
{ "roleRef": "employee", "path": ["residentialAddress", "country"] }
]
}
The replacement leaves overrides unchanged. An existing override whose (roleRef, path) is no longer selected fails validation, so remove or replace the matching override before reducing the selection.
Use PATCH /api/model/v1/data-products/{dataProductId} for draft name, description, selected, and overrides updates. Use GET and DELETE on the same resource path to read or remove a product. The objectModelRef, lifecycle status, and version are not changed by the metadata patch.
Publish
The Data Product lifecycle has two values: DRAFT and PUBLISHED.
- Request
- Response
PUT /api/model/v1/data-products/{dataProductId}/status
{ "status": "PUBLISHED" }
200 OK returns the DataProduct with the requested lifecycle status. A published version is the stable snapshot that channels pin with DataProductRef.
{
"id": "1b2c3d4e-5f60-4a71-8b92-0c1d2e3f4a5b",
"version": 1,
"status": "PUBLISHED"
}
Use POST /api/model/v1/data-products/{dataProductId}/version with no request body to increment the monotonic snapshot version.
Resolve
Resolve the published Data Product against its pinned Object Model:
GET /api/model/v1/data-products/{dataProductId}/resolved
200 OK returns a ResolvedDataProduct. Its attributes entries contain the selected paths, their source object type, their OCA value type, and effective overlays after Data Product narrowing. schemaRelationships carries the schema relationships exposed by the selected paths.
{
"dataProductId": "1b2c3d4e-5f60-4a71-8b92-0c1d2e3f4a5b",
"dataProductVersion": 1,
"objectModelId": "c4d5e6f7-8a9b-4c0d-8e1f-2a3b4c5d6e7f",
"objectModelVersion": 1,
"attributes": [
{
"roleRef": "employee",
"path": ["given_name"],
"sourceEntity": "Person",
"valueType": "Text",
"overlays": { "conformance": "MANDATORY" }
},
{
"roleRef": "employee",
"path": ["employment_status"],
"sourceEntity": "Employee",
"valueType": "Text",
"viaRelationship": null,
"overlays": {
"conformance": "OPTIONAL",
"entryCodes": { "codes": ["active", "suspended"] }
}
},
{
"roleRef": "employee",
"path": ["residentialAddress", "country"],
"sourceEntity": "Address",
"valueType": "Text",
"viaRelationship": "residentialAddress",
"overlays": { "widgetHint": "PICKLIST" }
}
],
"schemaRelationships": []
}
The employment_status overlay contains only active and suspended, because the Data Product override narrowed the effective code list. The residentialAddress.country entry demonstrates that resolution preserves the traversal path and identifies the relationship that reached the source object type.
Version Pinning and Narrowing Precedence
The effective value for a selected business term is determined in this order:
- Data Domain (L1) defines the canonical value type and governance overlays.
- Object Model (L2) binds the role and can narrow
conformance,sdPolicy,cardinality,entryCodesSubset, orsensitive. - Data Product (L3) selects the paths and can narrow
conformance,cardinality,entryCodesSubset, orsensitiveagain. - Channel (L4) maps a selected path to an output and does not change its governance.
No Data Product override can widen the effective Object Model value. objectModelRef fixes the Object Model snapshot, and the Data Product's own version fixes the L3 selection that channels consume.
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 the Published Data Product Delivers
With the Data Product published, you have a stable, versioned selection that:
- names the business terms in scope by role and traversal path;
- preserves source-object provenance in the resolved contract;
- carries effective governance overlays after narrow-only validation;
- can be consumed by multiple channels without copying the selection.
Next Steps
- Channels (L4): bind this Data Product to a verifiable-credential format or another output channel.
- Object Model (L2): review the roles, relationships, and governance this product narrows.
- Modeling Your World: review the four-layer model and version-pinning rules.
- Provenance & Operations: follow usage lineage when a channel consumes a Data Product path.