{
  "openapi": "3.0.0",
  "info": {
    "title": "UNTP Identity Resolver API",
    "description": "Implementation guidance for the UNTP Identity Resolver (IDR) specification. This API implements the Discover-Resolve-Verify workflow defined by the UNTP IDR specification, enabling supply chain actors to resolve product and facility identifiers from **any** registered identifier scheme to linked digital credentials such as Digital Product Passports (DPPs), Digital Conformity Credentials (DCCs), Digital Facility Records (DFRs), and Digital Traceability Events (DTEs).\n\nUNTP does not mandate GS1. Identifier key types, path shape, and optional multi-segment (\"qualifier\") paths are defined per scheme. GS1 Digital Link is one supported encoding; ISO/IEC 15459 with ASC MH10 Data Identifiers, opaque registry IDs, Identification Links, and other EN 18219-aligned families are equally valid when registered as namespaces.\n\n## Standards Compliance\n\n- **ISO/IEC 18975** — Encoding and resolving identifiers over HTTP\n- **IETF RFC 9264** — Linkset media type for typed link collections\n- **W3C DID Resolution** — Decentralised identifier resolution patterns\n- **IETF RFC 8615** — Well-known URI discovery mechanism\n\n## Functional Layers\n\nThe API is organized into three functional layers:\n\n1. **Resolution (public)** — Anonymous, unauthenticated resolution of identifiers to linksets or redirects. This is the core conformity surface that supply chain verifiers interact with.\n2. **Link Registration (management)** — Authenticated API for registering and managing link resolver entries that associate identifiers with typed link targets.\n3. **Identifier Scheme Management** — Authenticated API for configuring permitted identifier schemes (namespaces and application identifiers) before link registration can occur.\n\n## Normative Requirements\n\nThis API addresses UNTP IDR normative requirements IDR-01 through IDR-14. Key requirements include: IDR-01 (identifier scheme registration), IDR-02 (link registration), IDR-06/07/08 (resolution with link type, language, and context negotiation), IDR-09 (optional scheme-defined path grouping), IDR-10 (secure targets with encryption and access roles), and IDR-14 (resolver description discovery).",
    "version": "0.8.0"
  },
  "tags": [
    {
      "name": "Link Resolution",
      "description": "Public, anonymous resolution of identifiers to linksets or redirect targets. Implements IDR-06, IDR-07, and IDR-08. Resolution paths conform to ISO/IEC 18975 structured-path URI syntax when the scheme uses that encoding: /{namespace}/{identifierKeyType}/{identifierKey}/{optionalSchemeSegments}. identifierKeyType is a **scheme-defined** shortcode (e.g. gtin, 25S, product, facility) — not a mandatory GS1 Application Identifier. Optional trailing path segments (historically called \"qualifiers\" in GS1) are scheme-specific; leave them empty when the scheme uses an opaque single-segment ID. The same path applies to products, facilities, and other entity types; facility identifiers typically omit the trailing segments (empty secondaryIdentifiersPath)."
    },
    {
      "name": "Link Registration",
      "description": "Authenticated API for registering link resolver entries that associate an identifier with one or more typed link targets (the responses array). Each entry represents one identified item with multiple link variants differentiated by link type, language, MIME type, context, and access role. Implements IDR-02 (one carrier, many links), IDR-09 (qualifier path grouping), and IDR-10 (secure targets)."
    },
    {
      "name": "Link Management",
      "description": "CRUD operations on individual links within a registered resolver entry. Use these endpoints to update target URLs, rotate credentials, change access roles, toggle active status, or soft/hard delete individual link variants without affecting other links on the same identifier."
    },
    {
      "name": "Identifiers",
      "description": "Manage permitted identifier schemes (namespaces and their application identifiers). An identifier scheme must be registered before any link registration can reference it. Each scheme defines a namespace, a set of application identifiers (primary keys, qualifiers, data attributes) with regex validation patterns, and their relationships. Implements IDR-01 (scheme registration), IDR-03 (identifier validation), and IDR-04 (scheme configuration)."
    }
  ],
  "servers": [
    {
      "url": "https://{resolverDomain}/api/{version}",
      "description": "UNTP Identity Resolver API. Replace the variables with your resolver's domain and API version.",
      "variables": {
        "resolverDomain": {
          "default": "resolver.example.com",
          "description": "The domain name of the identity resolver service. Each registry operator deploys their own resolver instance."
        },
        "version": {
          "default": "0.8.0",
          "description": "API version. Follows the UNTP specification version that the resolver implements."
        }
      }
    }
  ],
  "paths": {
    "/health-check": {
      "get": {
        "operationId": "AppController_healthCheck",
        "summary": "Health check",
        "parameters": [],
        "responses": {
          "201": {
            "description": "Health check status",
            "content": {
              "application/json": {
                "schema": {
                  "example": {
                    "status": "OK"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/identifiers": {
      "post": {
        "operationId": "IdentifierManagementController_upsertIdentifier",
        "summary": "Create or update identifier",
        "description": "Registers or updates an identifier scheme configuration. Defines the namespace, application identifiers (primary keys, qualifiers, data attributes), their regex validation patterns, and relationships. An identifier scheme MUST be registered before any link registration can occur for that namespace. If the namespace already exists, the configuration is replaced (upsert semantics).",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IdentifierDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Identifier scheme created or updated successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "example": {
                    "message": "Application identifier upserted successfully"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error. The request body does not conform to the IdentifierDto schema — for example, missing required fields, invalid regex patterns, or duplicate shortcodes within the same namespace.",
            "content": {
              "application/json": {
                "schema": {
                  "example": {
                    "errors": [
                      {
                        "field": "applicationIdentifiers.0.shortcode",
                        "message": "shortcode must be a string"
                      },
                      {
                        "field": "applicationIdentifiers.0.regex",
                        "message": "regex must be a string"
                      }
                    ]
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "example": {
                    "message": "Internal server error"
                  }
                }
              }
            }
          }
        },
        "tags": ["Identifiers"],
        "security": [
          {
            "bearer": []
          }
        ]
      },
      "delete": {
        "operationId": "IdentifierManagementController_deleteIdentifier",
        "summary": "Delete identifier by namespace",
        "description": "Permanently removes an identifier scheme and all its application identifier configurations. Existing link resolver entries that reference this namespace will no longer resolve.",
        "parameters": [
          {
            "name": "namespace",
            "required": true,
            "in": "query",
            "description": "The namespace of the identifier scheme to delete (e.g. \"gs1\", \"nlis\"). Must match an existing registered scheme.",
            "schema": {
              "type": "string",
              "example": "example-identifier-scheme"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Identifier scheme deleted successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "example": {
                    "message": "Application identifier deleted successfully"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request - Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "example": {
                    "error": "Namespace is required"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "example": {
                    "message": "Internal server error"
                  }
                }
              }
            }
          }
        },
        "tags": ["Identifiers"],
        "security": [
          {
            "bearer": []
          }
        ]
      },
      "get": {
        "operationId": "IdentifierManagementController_getIdentifier",
        "summary": "Get identifier by namespace",
        "description": "Retrieves the configuration for one or all registered identifier schemes. When a namespace is specified, returns that scheme's configuration. When omitted, returns all registered schemes. Useful for inspecting which identifier types and validation patterns are currently configured.",
        "parameters": [
          {
            "name": "namespace",
            "required": false,
            "in": "query",
            "description": "The namespace of the identifier scheme to retrieve (e.g. \"gs1\", \"nlis\"). If not provided, all registered identifier schemes are returned.",
            "schema": {
              "type": "string",
              "example": "example-identifier-scheme"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Identifier scheme(s) retrieved successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/IdentifierDto"
                    },
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/IdentifierDto"
                      }
                    }
                  ]
                },
                "examples": {
                  "oneNamespace": {
                    "summary": "Single Namespace Response",
                    "value": {
                      "namespace": "example-identifier-scheme",
                      "applicationIdentifiers": [
                        {
                          "title": "Global Trade Item Number (GTIN)",
                          "label": "GTIN",
                          "shortcode": "gtin",
                          "ai": "01",
                          "type": "I",
                          "qualifiers": ["10"],
                          "regex": "(\\d{12,14}|\\d{8})"
                        },
                        {
                          "title": "Batch or Lot Number",
                          "label": "BATCH/LOT",
                          "shortcode": "lot",
                          "ai": "10",
                          "type": "Q",
                          "regex": "([\\x21-\\x22\\x25-\\x2F\\x30-\\x39\\x41-\\x5A\\x5F\\x61-\\x7A]{0,20})"
                        }
                      ]
                    }
                  },
                  "allNamespaces": {
                    "summary": "All Namespaces Response",
                    "value": [
                      {
                        "namespace": "example-identifier-scheme",
                        "applicationIdentifiers": [
                          {
                            "title": "Global Trade Item Number (GTIN)",
                            "label": "GTIN",
                            "shortcode": "gtin",
                            "ai": "01",
                            "type": "I",
                            "regex": "(\\d{12,14}|\\d{8})"
                          }
                        ]
                      },
                      {
                        "namespace": "integrity-systems",
                        "applicationIdentifiers": [
                          {
                            "title": "NLIS ID",
                            "label": "NLISID",
                            "shortcode": "nlisid",
                            "ai": "01",
                            "format": "N14",
                            "type": "I",
                            "regex": "(\\d{12,14}|\\d{8})"
                          }
                        ]
                      }
                    ]
                  },
                  "opaqueProductRegister": {
                    "summary": "Non-GS1 opaque product register (no qualifiers)",
                    "value": {
                      "namespace": "products.sample-register.example",
                      "applicationIdentifiers": [
                        {
                          "title": "Opaque product identifier",
                          "label": "PRODUCT",
                          "shortcode": "product",
                          "type": "I",
                          "regex": "[A-Za-z0-9_-]{1,64}"
                        }
                      ]
                    }
                  },
                  "ascMh10SerialisedProduct": {
                    "summary": "Non-GS1 ASC MH10 Data Identifier scheme",
                    "value": {
                      "namespace": "industrial-ids.example",
                      "applicationIdentifiers": [
                        {
                          "title": "Serialised product (ASC MH10 DI 25S)",
                          "label": "25S",
                          "shortcode": "25S",
                          "ai": "25S",
                          "type": "I",
                          "regex": "[A-Za-z0-9]{1,50}"
                        }
                      ]
                    }
                  },
                  "gs1DigitalLink": {
                    "summary": "GS1 Digital Link-style scheme (optional path continuations)",
                    "value": {
                      "namespace": "example-identifier-scheme",
                      "applicationIdentifiers": [
                        {
                          "title": "Global Trade Item Number (GTIN)",
                          "label": "GTIN",
                          "shortcode": "gtin",
                          "ai": "01",
                          "type": "I",
                          "qualifiers": ["10"],
                          "regex": "(\\d{12,14}|\\d{8})"
                        },
                        {
                          "title": "Batch or Lot Number",
                          "label": "BATCH/LOT",
                          "shortcode": "lot",
                          "ai": "10",
                          "type": "Q",
                          "regex": "([\\x21-\\x22\\x25-\\x2F\\x30-\\x39\\x41-\\x5A\\x5F\\x61-\\x7A]{0,20})"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "example": {
                    "message": "Internal server error"
                  }
                }
              }
            }
          }
        },
        "tags": ["Identifiers"],
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/resolver": {
      "post": {
        "operationId": "LinkRegistrationController_create",
        "summary": "Register a new link resolver",
        "description": "Registers a link resolver entry associating an identifier with one or more typed link targets. Each entry in the responses array represents a link variant — a combination of link type, language, MIME type, geographic context, and access role — per UNTP IDR-06. This implements the core IDR-02 requirement: one identified item (carrier) linked to many digital credentials and documents.\n\nUpsert semantics: if a resolver entry already exists for the same namespace + identificationKeyType + identificationKey + qualifierPath combination, new responses are added and existing responses with matching linkType + mimeType + context are updated. hreflang is metadata on the variant (not part of the matching key) — a single variant can serve multiple languages.",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateLinkRegistrationDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Link resolver entry registered successfully. The identifier is now resolvable via the resolution endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "example": {
                    "message": "Link resolver registered successfully"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error. Common causes: the namespace or identificationKeyType is not registered, the identificationKey does not match the registered regex pattern, or required response fields are missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FieldErrorsResponse"
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable Entity. The request is syntactically valid but semantically incorrect — for example, a qualifier path that references an AI not registered as a qualifier for the primary identifier type.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FieldErrorsResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FieldErrorsResponse"
                }
              }
            }
          }
        },
        "tags": ["Link Registration"],
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/resolver/links": {
      "get": {
        "operationId": "LinkManagementController_listLinks",
        "summary": "List all active links for an identifier",
        "description": "Retrieves all active links registered for a specific identifier, optionally filtered by linkType, mimeType, or language. Returns the full link variant details including target URLs, access roles, and encryption metadata. This is a management endpoint for inspecting registered links — for public resolution, use the /{namespace}/{identifierKeyType}/{identifierKey} path instead.",
        "parameters": [
          {
            "name": "namespace",
            "required": true,
            "in": "query",
            "description": "The namespace of the identifier scheme (e.g. \"gs1\", \"nlis\"). Must match a registered identifier scheme.",
            "example": "example-identifier-scheme",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "identificationKeyType",
            "required": true,
            "in": "query",
            "description": "The application identifier shortcode (e.g. \"gtin\", \"nlisid\"). Must be a registered shortcode within the specified namespace.",
            "example": "gtin",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "identificationKey",
            "required": true,
            "in": "query",
            "description": "The actual identifier value (e.g. GTIN digits \"09359502000010\"). Must match the regex pattern registered for this identifier type.",
            "example": "12345678901234",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "qualifierPath",
            "required": false,
            "in": "query",
            "description": "Optional qualifier path segment for sub-identification (e.g. \"/10/LOT123\" for batch). Use \"/\" for the base identifier without qualifiers. Implements IDR-09 logical grouping.",
            "example": "/10/12345678901234567890",
            "schema": {
              "default": "/",
              "type": "string"
            }
          },
          {
            "name": "linkType",
            "required": false,
            "in": "query",
            "description": "Filter results by link type: an IANA-registered token (e.g. \"dpp\") or the URI of an extension type (e.g. \"https://test.uncefact.org/vocabulary/link-type/dcc\", \"https://ref.gs1.org/voc/certificationInfo\"). For compatibility with UNTP 0.7 clients, resolvers SHOULD also accept the legacy aliases \"untp:dpp\", \"untp:dcc\", \"untp:dfr\", \"untp:dte\", \"untp:dia\" and \"untp:idr\", mapping them to their canonical values. New clients SHOULD NOT send the aliases.",
            "example": "https://test.uncefact.org/vocabulary/link-type/dcc",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "mimeType",
            "required": false,
            "in": "query",
            "description": "Filter results by MIME type of the target resource (e.g. \"application/json\", \"application/vc+ld+json\", \"application/pdf\").",
            "example": "application/json",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "hreflang",
            "required": false,
            "in": "query",
            "description": "Filter results by BCP 47 language tag (e.g. \"en\", \"de\", \"ja\"). Returns only link variants whose hreflang array contains the specified tag.",
            "example": "en",
            "schema": {
              "type": "string",
              "pattern": "(^\\w{2}$)|(^\\w{2}-\\w{2}$)"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Links retrieved successfully. Returns an array of link variants matching the query filters."
          },
          "400": {
            "description": "Validation error. The namespace or identificationKeyType is not registered, or required query parameters are missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FieldErrorsResponse"
                }
              }
            }
          },
          "404": {
            "description": "No link resolver entry found for this identifier. The namespace + identificationKeyType + identificationKey + qualifierPath combination has no registered links."
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "tags": ["Link Management"],
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/resolver/links/{linkId}": {
      "get": {
        "operationId": "LinkManagementController_getLink",
        "summary": "Get a specific link by linkId",
        "description": "Retrieves the full details of a single link variant by its server-generated identifier. Returns all properties including target URL, link type, access roles, encryption metadata, and active status.",
        "parameters": [
          {
            "name": "linkId",
            "required": true,
            "in": "path",
            "description": "The server-generated unique identifier for the link variant (UUID format).",
            "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Link variant retrieved successfully."
          },
          "404": {
            "description": "No link found with the specified linkId."
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "tags": ["Link Management"],
        "security": [
          {
            "bearer": []
          }
        ]
      },
      "put": {
        "operationId": "LinkManagementController_updateLink",
        "summary": "Update a specific link",
        "description": "Updates properties of a single link variant. All fields are optional (partial update semantics) — only the fields included in the request body are modified. Useful for rotating target URLs, changing access roles, toggling active status, or updating encryption methods without recreating the entire resolver entry.",
        "parameters": [
          {
            "name": "linkId",
            "required": true,
            "in": "path",
            "description": "The server-generated unique identifier for the link variant to update (UUID format).",
            "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateLinkDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Link variant updated successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "example": {
                    "message": "Link updated successfully"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error. Invalid field values — for example, an unrecognised MIME type or malformed target URL.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FieldErrorsResponse"
                }
              }
            }
          },
          "404": {
            "description": "No link found with the specified linkId."
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "tags": ["Link Management"],
        "security": [
          {
            "bearer": []
          }
        ]
      },
      "delete": {
        "operationId": "LinkManagementController_deleteLink",
        "summary": "Delete a specific link (soft by default, hard with ?hard=true)",
        "description": "Removes a link variant. By default, performs a soft delete: the link is deactivated (active=false) and excluded from resolution responses, but retained in storage for audit trail purposes. When hard=true, the link is permanently removed from the database. Soft-deleted links can be reactivated by updating their active status via PUT.",
        "parameters": [
          {
            "name": "linkId",
            "required": true,
            "in": "path",
            "description": "The server-generated unique identifier for the link variant to delete (UUID format).",
            "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "hard",
            "required": false,
            "in": "query",
            "description": "Set to true for permanent deletion. When false or omitted, the link is soft-deleted (deactivated but retained for audit).",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Link variant deleted successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "example": {
                    "message": "Link deleted successfully"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No link found with the specified linkId."
          },
          "500": {
            "description": "Internal Server Error"
          }
        },
        "tags": ["Link Management"],
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/{namespace}/{identifierKeyType}/{identifierKey}/{secondaryIdentifiersPath}": {
      "get": {
        "operationId": "LinkResolutionController_resolveClone",
        "summary": "Resolve a link resolver for an identifier",
        "description": "Core UNTP identity resolution endpoint. Resolves an identifier to its registered link targets.\n\nResolution behaviour depends on the linkType parameter and the Accept header:\n- When linkType is \"linkset\", or the Accept header includes \"application/linkset+json\": returns the **full RFC 9264 linkset** (200) and does not redirect. The Accept header selects its representation: \"application/linkset+json\" returns the RFC 9264 JSON representation, \"application/json\" SHOULD return the same representation, and \"text/html\" or an absent Accept header SHOULD return a human-readable HTML page that presents the links and embeds the linkset as JSON-LD. A client that requires JSON MUST send \"Accept: application/linkset+json\".\n- When linkType is omitted: returns a **302 redirect** to the default link target URL selected using language and context. When a specific linkType is requested and available: returns a **302 redirect** to a matching target. When the requested linkType is unavailable: returns **404 Not Found** instead of falling back to the default.\n\nFor compatibility with UNTP 0.7, resolvers SHOULD accept the legacy aliases \"untp:dpp\", \"untp:dcc\", \"untp:dfr\", \"untp:dte\", \"untp:dia\" and \"untp:idr\". The first five map to their canonical relation values. The legacy query value \"untp:idr\" selects a secondary-resolver link whose canonical relation is \"linkset\"; it does not request the full linkset. Resolvers MAY also accept linkType=all, used by GS1 Digital Link 1.1 and now deprecated by GS1, as an alias for linkset. Clients SHOULD NOT use aliases for new requests, but MAY use or retry them when communicating with a 0.7 resolver.\n\nPath structure (ISO/IEC 18975-style schemes): `/{namespace}/{identifierKeyType}/{identifierKey}/{optionalSegments}`.\n- `namespace` — registered identifier scheme (not limited to GS1).\n- `identifierKeyType` — scheme-defined shortcode for the primary identifier class (e.g. `gtin`, `25S`, `product`). Not mandatory GS1.\n- `identifierKey` — the primary identifier value.\n- `optionalSegments` — empty for opaque IDs; used only when the scheme encodes additional identity in the path (e.g. GS1 serial AI 21).\n\n**Non-GS1 example (opaque product register):** `GET /products.sample-register.example/product/ABC123456/?linkType=linkset`\n\n**Non-GS1 example (ASC MH10 DI):** `GET /industrial-ids.example/25S/IAC42PROD987654/?linkType=linkset`\n\n**GS1-style example (optional multi-segment):** `GET /gs1/01/09506000134352/21/SERIAL123?linkType=linkset`\n\n**Facility example:** `GET /facilities-register/facility/ABC123456/?linkType=linkset` returns a linkset whose anchor is the facility identifier and whose `dfr` link points to the Facility Record — no product carrier or serial/batch path segments are required.\n\nThis endpoint is public and does not require authentication.",
        "parameters": [
          {
            "name": "namespace",
            "required": true,
            "in": "path",
            "description": "Identifies the identifier scheme (e.g. \"gs1\", \"industrial-ids\", \"products.sample-register.example\", \"nlis\"). Maps to the scheme's registered configuration. Must match a namespace previously registered via the /identifiers endpoint. Not limited to GS1.",
            "example": "products.sample-register.example",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "identifierKeyType",
            "required": true,
            "in": "path",
            "description": "Scheme-defined shortcode or code for the primary identifier class (e.g. \"product\" for an opaque register, \"25S\" for an ASC MH10 Data Identifier, \"01\"/\"gtin\" for GS1 GTIN). Must be a registered shortcode within the specified namespace. GS1 Application Identifiers are one option, not a UNTP requirement.",
            "example": "product",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "identifierKey",
            "required": true,
            "in": "path",
            "description": "The primary identifier value (e.g. opaque register id \"ABC123456\", MH10 value \"IAC42PROD987654\", or GTIN digits). Must match the regex pattern registered for this identifier type in the scheme configuration.",
            "example": "ABC123456",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "secondaryIdentifiersPath",
            "required": true,
            "in": "path",
            "allowEmptyValue": true,
            "description": "Optional scheme-specific path continuation after the primary identifier. Empty for opaque single-segment IDs (typical for many non-GS1 registers) and for typical facility resolution, which reuses the product path structure without serial/batch segments. Used when a scheme encodes additional identity in the path (e.g. GS1 \"21/SERIAL123\"). These segments are part of that scheme's identifier syntax — not a general UNTP qualifier model. Implements optional IDR-09 grouping when the scheme defines it.",
            "example": "",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "linkType",
            "required": false,
            "in": "query",
            "description": "Filter by link relation type, written exactly as it appears in the linkset: an IANA-registered token (e.g. \"dpp\" for Digital Product Passport) or the percent-encoded URI of an extension type (e.g. \"https://test.uncefact.org/vocabulary/link-type/dcc\" for Digital Conformity Credential). See the Link Relations section of the UNTP Identity Resolver specification. Set to \"linkset\" to return the full RFC 9264 linkset containing all registered link variants instead of redirecting; the Accept header selects whether that linkset is represented as JSON or HTML. For compatibility with UNTP 0.7, resolvers SHOULD accept \"untp:dpp\", \"untp:dcc\", \"untp:dfr\", \"untp:dte\" and \"untp:dia\" as aliases for their canonical values. The legacy value \"untp:idr\" selects a secondary-resolver link whose canonical relation is \"linkset\"; unlike \"linkset\", it does not request the full linkset. Resolvers MAY also accept the legacy value \"all\" as an alias for \"linkset\". Clients SHOULD NOT use aliases for new requests, but MAY use or retry them when communicating with a 0.7 resolver. Implements IDR-07.",
            "example": "linkset",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Accept",
            "required": false,
            "in": "header",
            "description": "Selects both linkset retrieval and its representation. When this includes \"application/linkset+json\", the resolver returns the full RFC 9264 linkset as JSON instead of redirecting. \"application/json\" SHOULD return the same JSON representation. With linkType=linkset, \"text/html\" or an absent Accept header SHOULD return a human-readable HTML page that presents the links and embeds the linkset as JSON-LD. Resolvers that vary the response on this header SHOULD send Vary: Accept.",
            "example": "application/linkset+json",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "decryptionKey",
            "required": false,
            "in": "query",
            "description": "Shared secret for decrypting encrypted content at the target URL. Query parameters are forwarded to the target URL by default on redirect. A non-GS1 link may set fwqs to false to suppress forwarding; a GS1-Conformant Resolver always forwards the complete query string. This parameter is ignored when resolution returns a linkset rather than a redirect.",
            "example": "a3f2b8c1d5e6f7a8b9c0d1e2f3a4b5c6",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Full RFC 9264 linkset for the identifier, returned when linkType=linkset or the Accept header includes application/linkset+json. The Accept header selects the representation. JSON representations validate against the UNTP Linkset Schema (LinksetSchema.json).",
            "content": {
              "application/linkset+json": {
                "schema": {
                  "type": "object",
                  "required": ["linkset"],
                  "properties": {
                    "linkset": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["linkset"],
                  "properties": {
                    "linkset": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "302": {
            "description": "Redirect to the selected link target URL. The Location header contains the target URL. Query parameters from the resolution request are forwarded by default. A non-GS1 link may set fwqs to false to suppress forwarding; a GS1-Conformant Resolver always forwards the complete query string."
          },
          "400": {
            "description": "Identifier path does not match registered scheme patterns. The namespace is not registered, the identifierKeyType is not a valid application identifier within the namespace, or the identifierKey does not match the registered regex pattern.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FieldErrorsResponse"
                }
              }
            }
          },
          "404": {
            "description": "No link resolver entry was found for this identifier, the entry exists but is inactive, or the specifically requested linkType is not available for the identifier."
          }
        },
        "tags": ["Link Resolution"]
      }
    },
    "/.well-known/resolver": {
      "get": {
        "operationId": "CommonController_getResolver",
        "summary": "Resolver Description File",
        "description": "Returns the Resolver Description File per ISO/IEC 18975 section 7 and IETF RFC 8615 (well-known URIs). Declares the resolver's supported identifier namespaces, capabilities, and conformance level. Clients SHOULD check this endpoint before assuming a domain hosts an IDR service. Implements IDR-14 (resolver discovery). This endpoint is public and does not require authentication.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Resolver Description File returned successfully. Contains the resolver's supported namespaces, conformance level, and service metadata per ISO/IEC 18975 section 7."
          }
        },
        "tags": ["Link Resolution"]
      }
    },
    "/voc": {
      "get": {
        "operationId": "CommonController_getVoc",
        "summary": "List supported link type vocabularies",
        "description": "Returns the resolver's supported link type vocabulary. When show=linktypes, lists all registered link types with their definitions, titles, and namespace prefixes. The prefix parameter filters by vocabulary namespace — for example, \"untp\" returns only UNTP-defined link types (dpp, dcc, dfr, dte, dia, dcr), while \"gs1\" returns GS1-defined types (certificationInfo, hasRetailers, etc.). This endpoint is public and does not require authentication.",
        "parameters": [
          {
            "name": "show",
            "required": false,
            "in": "query",
            "description": "Set to \"linktypes\" to retrieve the full link type vocabulary. When omitted, returns a summary of available vocabulary categories.",
            "schema": {
              "type": "string",
              "example": "linktypes"
            }
          },
          {
            "name": "prefix",
            "required": false,
            "in": "query",
            "description": "Filter link types by vocabulary namespace prefix (e.g. \"untp\" for UNTP-defined types, \"gs1\" for GS1 types). Only applies when show=linktypes. When omitted, all link types across all namespaces are returned.",
            "schema": {
              "type": "string",
              "example": "gs1"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Link type vocabulary returned successfully. When show=linktypes, returns an array of link type definitions with their namespace, shortcode, title, and description."
          }
        },
        "tags": ["Link Resolution"]
      }
    },
    "/voc/{linktype}": {
      "get": {
        "operationId": "CommonController_getLinkType",
        "summary": "Get link type details",
        "description": "Returns the detailed definition of a specific link type, including its namespace, title, description, and any associated metadata. Use this to look up the semantics of a link type encountered in a linkset response. This endpoint is public and does not require authentication.",
        "parameters": [
          {
            "name": "linktype",
            "required": true,
            "in": "path",
            "description": "The link type identifier to look up (e.g. \"dpp\", or a percent-encoded URI such as \"https://test.uncefact.org/vocabulary/link-type/dcc\"). Must match a link type registered in the resolver's vocabulary. For compatibility with UNTP 0.7, resolvers SHOULD map the legacy \"untp:\" aliases to their canonical values.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Link type definition returned successfully."
          },
          "404": {
            "description": "The specified link type is not registered in this resolver's vocabulary."
          }
        },
        "tags": ["Link Resolution"]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearer": {
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "type": "http",
        "description": "JWT bearer token for authenticated management operations (link registration, link management, identifier scheme management). Resolution endpoints (link resolution, /.well-known/resolver, vocabulary) are public and do not require authentication. Token issuance and claims are implementation-specific — the resolver operator defines the authentication provider and authorization model."
      }
    },
    "schemas": {
      "ApplicationIdentifier": {
        "type": "object",
        "description": "Defines a single identifier type within an identifier scheme. Application identifiers can be primary identifiers (type \"I\") that form the main resolution key, optional scheme-defined path continuations historically called qualifiers (type \"Q\"), or data attributes (type \"D\"). Type \"Q\" entries are optional and scheme-specific — many non-GS1 schemes register only type \"I\".",
        "properties": {
          "title": {
            "type": "string",
            "description": "Human-readable name for this identifier type (e.g. \"Global Trade Item Number (GTIN)\", \"Batch or Lot Number\"). Displayed in management interfaces and documentation.",
            "example": "Opaque product identifier"
          },
          "label": {
            "type": "string",
            "description": "Short display label for compact UI rendering (e.g. \"GTIN\", \"BATCH/LOT\", \"NLISID\"). Typically an abbreviation or acronym.",
            "example": "PRODUCT"
          },
          "shortcode": {
            "type": "string",
            "description": "URL-friendly identifier used in resolution paths (e.g. \"product\", \"25S\", \"gtin\", \"nlisid\"). For GS1 schemes, the shortcode may map to a human-readable form of an Application Identifier. This value appears in the identifierKeyType segment of the resolution URL path. Not required to be a GS1 AI.",
            "example": "product"
          },
          "ai": {
            "type": "string",
            "description": "Optional scheme code corresponding to ISO/IEC 15418 Application Identifier or Data Identifier encoding (e.g. \"01\" for GS1 GTIN, \"25S\" for an ASC MH10 DI). Omit for opaque register schemes that do not use 15418 codes. Defaults to the shortcode value if omitted.",
            "example": "product"
          },
          "type": {
            "type": "string",
            "description": "Classification of this application identifier. \"I\" = primary Identifier (forms the main resolution key), \"Q\" = optional scheme-defined path continuation (e.g. GS1 batch/serial), \"D\" = Data attribute (does not participate in resolution path construction). Only \"I\" is required for a usable scheme.",
            "example": "I",
            "enum": ["I", "Q", "D"]
          },
          "regex": {
            "type": "string",
            "description": "Regular expression validation pattern for the identifier value. Resolution rejects identifiers that do not match this pattern with a 400 error. Patterns should be anchored and specific to prevent false matches (e.g. \"(\\\\d{12,14}|\\\\d{8})\" for GTIN).",
            "example": "[A-Za-z0-9_-]{1,64}"
          },
          "format": {
            "type": "string",
            "description": "Human-readable format descriptor (e.g. \"N14\" for 14 numeric digits, \"X..20\" for up to 20 alphanumeric characters). Informational only — validation is performed using the regex field.",
            "example": "X..20"
          },
          "qualifiers": {
            "description": "Optional list of type-\"Q\" codes that may appear after this primary identifier in the resolution path. Only applicable when type is \"I\". Leave empty or omit for schemes that use a single opaque identifier. For GS1, a GTIN might list [\"10\", \"21\"] for batch/serial — that is scheme configuration, not a UNTP mandate.",
            "example": [],
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": ["title", "label", "shortcode", "type", "regex"]
      },
      "IdentifierDto": {
        "type": "object",
        "description": "Configuration for an identifier scheme (namespace). Defines the set of identifier types that may be resolved under this namespace and their validation rules. Must be registered before any link registration can reference this namespace. Namespaces may represent GS1, ISO/IEC 15459 / ASC MH10, national registers, or other EN 18219-aligned schemes.",
        "properties": {
          "namespace": {
            "type": "string",
            "description": "Unique name for the identifier scheme (e.g. \"gs1\", \"nlis\", \"integrity-systems\"). Must match the scheme's registration in the UNTP IDR register. This value appears as the first path segment in resolution URLs.",
            "example": "products.sample-register.example"
          },
          "namespaceURI": {
            "type": "string",
            "description": "Optional URI providing an authoritative reference for the identifier scheme (e.g. the scheme owner's domain or specification URL). Informational — used for documentation and provenance, not for resolution.",
            "example": ""
          },
          "namespaceProfile": {
            "type": "string",
            "description": "Optional profile identifier for scheme-specific resolver configuration. Allows a single namespace to support multiple resolution profiles (e.g. different validation rules or link type vocabularies for different use cases).",
            "example": ""
          },
          "applicationIdentifiers": {
            "description": "The set of identifier types supported within this scheme. Must include at least one primary identifier (type \"I\"). Optional type \"Q\" path continuations and type \"D\" data attributes are scheme-specific and not required.",
            "example": [
              {
                "title": "Opaque product identifier",
                "label": "PRODUCT",
                "shortcode": "product",
                "type": "I",
                "regex": "[A-Za-z0-9_-]{1,64}"
              }
            ],
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/ApplicationIdentifier"
            }
          }
        },
        "required": ["namespace", "applicationIdentifiers"]
      },
      "Response": {
        "type": "object",
        "description": "A single link variant within a resolver entry. Each response represents one combination of link type, language, MIME type, geographic context, and access role — per UNTP IDR-06 (one carrier, many links). Multiple responses on the same identifier allow different stakeholders to discover different credentials in different formats and languages.",
        "properties": {
          "linkId": {
            "type": "string",
            "description": "Server-generated unique identifier for this link variant (UUID format). Read-only — assigned on creation and used for subsequent GET, PUT, and DELETE operations on this specific link.",
            "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
            "readOnly": true
          },
          "linkType": {
            "type": "string",
            "description": "Link relation type per RFC 8288: an IANA-registered token or the URI of an extension type. UNTP uses \"dpp\" (Digital Product Passport, IANA registered), \"https://test.uncefact.org/vocabulary/link-type/dcc\" (Digital Conformity Credential), \"https://test.uncefact.org/vocabulary/link-type/dfr\" (Digital Facility Record), \"https://test.uncefact.org/vocabulary/link-type/dte\" (Digital Traceability Event), \"https://test.uncefact.org/vocabulary/link-type/dia\" (Digital Identity Anchor) and \"https://test.uncefact.org/vocabulary/link-type/dcr\" (Digital Corporate Record). A link to another resolver uses the IANA-registered \"linkset\" relation. Scheme vocabularies are also URIs (e.g. \"https://ref.gs1.org/voc/certificationInfo\"). For compatibility with UNTP 0.7 publishers, resolvers SHOULD accept \"untp:dpp\", \"untp:dcc\", \"untp:dfr\", \"untp:dte\", \"untp:dia\" and \"untp:idr\" as registration aliases and map them to canonical values. Resolvers MUST emit canonical values in linksets and management responses. Implements IDR-07.",
            "example": "https://test.uncefact.org/vocabulary/link-type/dcc"
          },
          "rel": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Additional IANA or extension link relation types that qualify this link beyond its primary linkType — for example \"edit\" or \"latest-version\". Optional. Note: \"predecessor-version\" is reserved — the resolver derives it automatically from version history when a link variant is replaced and SHOULD NOT be set explicitly by publishers.",
            "example": ["edit"]
          },
          "title": {
            "type": "string",
            "description": "Human-readable title for this link variant, displayed in linkset responses and management interfaces (e.g. \"Certification Information\", \"Digital Product Passport (English)\").",
            "example": "Certification Information"
          },
          "targetUrl": {
            "type": "string",
            "description": "The URL that resolution redirects to or includes in the linkset. For UNTP credentials, this is typically the URL where the Verifiable Credential can be retrieved. Query parameters from the resolution request are appended by default unless a non-GS1 link explicitly sets fwqs to false.",
            "example": "https://example.com"
          },
          "mimeType": {
            "type": "string",
            "pattern": "\\w+/[-+.\\w]+",
            "description": "Media type of the target resource — any well-formed MIME type per RFC 6838 is accepted. For UNTP verifiable credentials, use \"application/vc+ld+json\" (W3C VC 2.0). Other common values: \"application/json\", \"application/pdf\" for human-readable documents, \"application/linkset+json\" for embedded linksets. Implements content negotiation per IDR-08.",
            "example": "application/json"
          },
          "hreflang": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "pattern": "(^\\w{2}$)|(^\\w{2}-\\w{2}$)"
            },
            "description": "BCP 47 language tags supported by the resource at the target URL. Content negotiation is handled at the target — a single targetUrl may return, for example, English, French, or German variants of the same resource based on the request's Accept-Language header, in which case hreflang would be [\"en\", \"fr\", \"de\"]. Consumers use this list to know which languages they can request from the target before issuing the call. Implements IDR-08 language negotiation.",
            "example": ["en"]
          },
          "context": {
            "type": "string",
            "description": "Geographic or situational context code (e.g. \"au\" for Australia, \"eu\" for European Union, \"us\" for United States). Implements IDR-08 context negotiation — allows different link targets for different jurisdictions. For example, a product may have different conformity credentials for different regulatory contexts.",
            "example": "au"
          },
          "defaultLinkType": {
            "type": "boolean",
            "description": "When true, this variant is the default for its link type dimension. Resolution returns this variant when no linkType filter is specified in the request. Exactly one response per identifier SHOULD be marked as the default link type.",
            "example": true
          },
          "defaultMimeType": {
            "type": "boolean",
            "description": "When true, this variant is the default for its MIME type dimension. Resolution returns this variant when multiple MIME types are available and no preference is specified.",
            "example": true
          },
          "defaultContext": {
            "type": "boolean",
            "description": "When true, this variant is the default for its context dimension. Resolution returns this variant when no geographic/situational context is specified.",
            "example": true
          },
          "fwqs": {
            "type": "boolean",
            "description": "Forward Query String. Defaults to true. When true, query parameters from the resolution request are appended to the targetUrl on redirect. A non-GS1 link may set this to false to suppress forwarding. GS1-Conformant Resolvers always forward the complete query string and ignore false for GS1 Digital Link requests.",
            "default": true,
            "example": true
          },
          "active": {
            "type": "boolean",
            "description": "Whether this link variant is live and included in resolution responses. Inactive links are excluded from resolution but retained in storage. Set to false for soft-delete or to temporarily disable a link variant.",
            "example": true
          },
          "encryptionMethod": {
            "type": "string",
            "description": "Encryption algorithm applied to the target resource. \"none\" indicates the target is unencrypted (public access). \"AES-128\" or \"AES-256\" indicate the credential at the target URL is encrypted and requires a decryptionKey to access. Implements IDR-10 secure targets for confidential credentials.",
            "enum": ["none", "AES-128", "AES-256"],
            "example": "none"
          },
          "accessRole": {
            "type": "array",
            "description": "Absolute role URIs describing the audiences for which this link variant is intended, per IDR-10 (secure targets). UNTP does not define a fixed role list: publishers may identify registrar-recognised capacities or relationships established under their own policy. This is metadata returned with the link so a client can select the variant meant for it; it is not a resolution filter, a query parameter, or proof of authorisation. The resolver returns every variant regardless of role. Non-public targets are protected by encryption or access control at the target.",
            "example": ["https://roles.example/public"],
            "items": {
              "type": "string",
              "format": "uri"
            }
          },
          "public": {
            "type": "boolean",
            "description": "Whether the link itself is safe to share publicly (e.g. publish in directories, broadcast on product packaging). Distinct from accessRole, which says who the resource at the URL is intended for — a link with public: true and accessRole: [\"https://roles.example/customer\"] is safe to advertise, but the resource is meant for (and, if encrypted or access-controlled, only readable by) customers. Optional; if omitted, publishers SHOULD assume the link is not safe to share.",
            "example": true
          },
          "method": {
            "type": "string",
            "description": "HTTP method for accessing the target resource. \"GET\" for retrieval of existing credentials. \"POST\" for creating new linked resources (e.g. submitting traceability events or conformity credentials back to the identifier owner). Implements IDR-10 edit links, enabling bidirectional data flows in supply chains.",
            "example": "POST"
          }
        },
        "required": [
          "defaultLinkType",
          "defaultMimeType",
          "active",
          "linkType",
          "title",
          "targetUrl",
          "mimeType",
          "hreflang",
          "context",
          "defaultContext"
        ]
      },
      "CreateLinkRegistrationDto": {
        "type": "object",
        "description": "Registers a link resolver entry per IDR-02 (one carrier, many links). Associates a specific identifier — defined by namespace, identificationKeyType, identificationKey, and optional qualifierPath — with one or more typed link targets. Each entry in the responses array represents a link variant that can be discovered through resolution.",
        "properties": {
          "namespace": {
            "type": "string",
            "description": "Must reference a previously registered identifier scheme (see POST /identifiers). The namespace identifies which scheme this identifier belongs to (e.g. \"gs1\", \"nlis\").",
            "example": "example-identifier-scheme"
          },
          "identificationKeyType": {
            "type": "string",
            "description": "Must be a registered shortcode within the specified namespace (e.g. \"product\" for an opaque register, \"25S\" for ASC MH10, \"gtin\" for GS1). Determines which application identifier type this entry is for. Not limited to GS1.",
            "example": "product"
          },
          "identificationKey": {
            "type": "string",
            "description": "The specific identifier value being registered (e.g. \"ABC123456\", \"IAC42PROD987654\", or a GTIN). Must match the regex pattern configured for the identificationKeyType in the namespace's scheme definition.",
            "example": "ABC123456"
          },
          "description": {
            "type": "string",
            "description": "Human-readable description of the identified item (e.g. \"Recycled Copper Cathode — 99.99% purity\"). Included in linkset responses to help consumers understand what the identifier refers to.",
            "example": "Product description"
          },
          "itemDescription": {
            "type": "string",
            "description": "Deprecated: Use \"description\" instead. If both are provided, \"description\" takes precedence.",
            "example": "Product description",
            "deprecated": true
          },
          "qualifierPath": {
            "type": "string",
            "description": "Optional scheme-specific path continuation after the primary identifier (e.g. \"/21/SER456\" for a GS1 serial). Use \"/\" or empty for opaque identifiers with no extra segments. Not required for non-GS1 schemes that encode full identity in identificationKey.",
            "example": "/10/12345678901234567890"
          },
          "active": {
            "type": "boolean",
            "description": "Whether this resolver entry is live. When false, the identifier returns 404 on resolution even if link variants exist. Useful for pre-registering links before they should be publicly discoverable.",
            "example": true
          },
          "responses": {
            "description": "Array of link targets (variants). Each entry represents one link variant per IDR-06 — a unique combination of link type, language, MIME type, geographic context, and access role. For a typical UNTP product, this might include a DPP credential (JSON), a conformity certificate (PDF), and a human-readable product page (HTML), each potentially in multiple languages.",
            "example": [
              {
                "defaultLinkType": true,
                "defaultMimeType": true,
                "defaultContext": true,
                "fwqs": true,
                "active": true,
                "linkType": "https://test.uncefact.org/vocabulary/link-type/dcc",
                "rel": ["edit"],
                "hreflang": ["en"],
                "context": "au",
                "title": "Certification Information",
                "targetUrl": "https://example.com",
                "mimeType": "application/json",
                "encryptionMethod": "none",
                "accessRole": ["https://roles.example/public"],
                "public": true,
                "method": "POST"
              }
            ],
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/Response"
            }
          }
        },
        "required": [
          "namespace",
          "identificationKeyType",
          "identificationKey",
          "description",
          "qualifierPath",
          "active",
          "responses"
        ]
      },
      "FieldError": {
        "type": "object",
        "properties": {
          "field": {
            "type": "string",
            "example": "identificationKeyType"
          },
          "message": {
            "type": "string",
            "example": "Identification key type 'invalid_key_type' is not registered with the namespace 'example-identifier-scheme'"
          }
        },
        "required": ["field", "message"]
      },
      "FieldErrorsResponse": {
        "type": "object",
        "properties": {
          "errors": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/FieldError"
            }
          }
        },
        "required": ["errors"]
      },
      "UpdateLinkDto": {
        "type": "object",
        "description": "Partial update for a single link variant. All fields are optional — only the fields included in the request body are modified. Omitted fields retain their current values. Use this to rotate target URLs, update access roles, toggle active status, or change encryption methods without affecting other properties.",
        "properties": {
          "targetUrl": {
            "type": "string",
            "description": "The URL that resolution redirects to or includes in the linkset. Update this to rotate credential hosting URLs or point to a new version of the target resource.",
            "example": "https://example.com"
          },
          "linkType": {
            "type": "string",
            "description": "Link relation type per RFC 8288: an IANA-registered token (e.g. \"dpp\") or the URI of an extension type (e.g. \"https://test.uncefact.org/vocabulary/link-type/dcc\", \"https://ref.gs1.org/voc/certificationInfo\"). For compatibility with UNTP 0.7 publishers, resolvers SHOULD accept the legacy \"untp:\" aliases and map them to canonical values. Resolvers MUST emit the canonical value. Changing the link type changes how this variant is classified in resolution and linkset responses.",
            "example": "https://test.uncefact.org/vocabulary/link-type/dcc"
          },
          "rel": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Additional IANA or extension link relation types that qualify this link beyond its primary linkType. Replaces the existing rel array (does not merge). \"predecessor-version\" is reserved — see Response schema.",
            "example": ["edit"]
          },
          "title": {
            "type": "string",
            "description": "Human-readable title for this link variant.",
            "example": "Certification Information"
          },
          "mimeType": {
            "type": "string",
            "pattern": "\\w+/[-+.\\w]+",
            "description": "Media type of the target resource — any well-formed MIME type per RFC 6838 is accepted (e.g. \"application/vc+ld+json\", \"application/json\", \"application/pdf\", \"application/linkset+json\").",
            "example": "application/json"
          },
          "hreflang": {
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "(^\\w{2}$)|(^\\w{2}-\\w{2}$)"
            },
            "description": "BCP 47 language tags supported by the resource at the target URL — content negotiation is handled at the target.",
            "example": ["en"]
          },
          "context": {
            "type": "string",
            "description": "Geographic or situational context code (e.g. \"au\", \"eu\", \"us\").",
            "example": "au"
          },
          "active": {
            "type": "boolean",
            "description": "Whether this link variant is live and included in resolution responses. Set to false to deactivate without deleting."
          },
          "fwqs": {
            "type": "boolean",
            "description": "Forward Query String. When supplied, replaces the current setting. New links default to true. A non-GS1 link may set this to false; GS1-Conformant Resolvers always forward the complete query string."
          },
          "defaultLinkType": {
            "type": "boolean",
            "description": "When true, this variant is the default for its link type dimension."
          },
          "defaultContext": {
            "type": "boolean",
            "description": "When true, this variant is the default for its context dimension."
          },
          "defaultMimeType": {
            "type": "boolean",
            "description": "When true, this variant is the default for its MIME type dimension."
          },
          "encryptionMethod": {
            "type": "string",
            "description": "Encryption algorithm applied to the target resource. \"none\" for unencrypted, \"AES-128\" or \"AES-256\" for encrypted credentials requiring a decryptionKey.",
            "enum": ["none", "AES-128", "AES-256"],
            "example": "none"
          },
          "accessRole": {
            "type": "array",
            "description": "Absolute role URIs describing the audiences for which this link variant is intended. UNTP does not define a fixed role list. Updates the full role list (replaces, does not merge).",
            "example": ["https://roles.example/public"],
            "items": {
              "type": "string",
              "format": "uri"
            }
          },
          "public": {
            "type": "boolean",
            "description": "Whether the link itself is safe to share publicly. Distinct from accessRole — see Response schema for full semantics."
          },
          "method": {
            "type": "string",
            "description": "HTTP method for accessing the target. \"GET\" for retrieval, \"POST\" for creating new linked resources.",
            "example": "POST"
          }
        }
      }
    }
  }
}
