From f5a84d26d6bb6b8cdb1122a55542f0850cfa1f57 Mon Sep 17 00:00:00 2001 From: Nico Koprowski Date: Mon, 11 May 2026 17:01:07 +0800 Subject: [PATCH 01/14] docs(0135): align endpoints for a harmonized push and pull mechanism --- .../CX-0135-CompanyCertificateManagement.md | 627 +++++++++--------- .../assets/openapi-spec.yaml | 610 ++++++++++------- 2 files changed, 702 insertions(+), 535 deletions(-) diff --git a/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md b/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md index c73816415e..cb722c7129 100644 --- a/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md +++ b/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md @@ -102,13 +102,18 @@ Use case to give feedback on the status for consumed certificates: - Certificate Consumer -> Certificate Provider: Company Certificate Feedback (Received, Accepted, or Rejected) -Use case to notify about availability of certificates: +Use cases to discover and retrieve certificates: -- Certificate Provider -> Certificate Consumer: Company Certificate Available +- Certificate Consumer -> Certificate Provider: Search Certificates +- Certificate Consumer -> Certificate Provider: Retrieve Certificate Metadata and Documents + +Use case to notify about certificate lifecycle changes (creation, modification, deletion): + +- Certificate Provider -> Certificate Consumer: Company Certificate Push (lifecycle notification) ### 2.1 API Specification -This section introduces the certificate management notification API which is further detailed in the corresponding [OpenAPI specification](assets/openapi-spec.yaml). +This section introduces the certificate management API which is further detailed in the corresponding [OpenAPI specification](assets/openapi-spec.yaml). #### 2.1.1 API endpoints and resources @@ -121,41 +126,158 @@ This section introduces the certificate management notification API which is fur > A future change is required in that regard, especially when considering the deprecation of the v1 DSP endpoint in favor of an upcoming EDC `.well-known` endpoint that supports multiple DSP versions. > This attribute will be deprecated in future releases and it will no longer be possible to use it for specifying the endpoint to receive feedback on. -> [!CAUTION] -> **`documentId` explanation** -> -> The `documentId` in the payloads of the Request, Feedback, and Available notifications does not refer to the `documentID` of the certificate. -> Instead, it references the unique ID of the EDC asset of the certificate. -> This is different for the Push notification, where the certificate itself is in the payload and therefore the `documentID` of the certificate is referenced. -##### 2.1.1.1 Company Certificate Request +##### 2.1.1.1 Retrieve Certificate Metadata -The Certificate Consumer is requesting a specific certificate from the Certificate Provider. +The Certificate Consumer retrieves the metadata of a certificate by its ID from the Certificate Provider. -![alt text](assets/state-machine-certificate-distributor.svg "Certificate Request API State Machine") +`GET /certificates/{certificateId}` + +###### 2.1.1.1.1 HTTP Response Codes + +| HTTP Code | Description | +|-----------|--------------------------------| +| 200 | Certificate metadata returned. | +| 404 | Certificate not found. | +| 500 | Internal Server Error. | + +###### 2.1.1.1.2 HTTP Response Body for HTTP Code 200 -`POST /companycertificate/request` +The response contains the full certificate metadata including all associated document IDs. +Use `GET /certificates/{certificateId}/documents/{documentId}` to retrieve each document as a binary PDF. ```json { - "header": { - "messageId": "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72", - "context": "CompanyCertificateManagement-CCMAPI-Request:1.0.0", - "sentDateTime": "2024-10-07T10:15:00Z", - "senderBpn": "BPNL0000000001AB", - "receiverBpn": "BPNL0000000002CD", - "relatedMessageId": "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72", - "version": "3.1.0" + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "certifiedBpn": "BPNL0000000002CD", + "type": { + "certificateType": "ISO9001", + "certificateVersion": "2015" }, - "content": { - "certifiedBpn": "BPNL00000003AYRE", - "certificateType": "iso9001", - "locationBpns": [ - "BPNA000000000001", - "BPNA000000000002", - "BPNS000000000003" - ] - } + "registrationNumber": "12 198 54182 TMS", + "areaOfApplication": "Development, Marketing and Sales", + "locations": [ + { + "locationBpn": "BPNS000000000002", + "areaOfApplication": "Development, Marketing and Sales" + } + ], + "validFrom": "2023-01-25", + "validUntil": "2026-01-24", + "issuer": { + "issuerName": "TUEV Sued", + "issuerBpn": "BPNL0000000001AB" + }, + "trustLevel": "high", + "validator": { + "validatorName": "Data service provider X", + "validatorBpn": "BPNL0000000003EF" + }, + "uploader": "BPNL0000000001AB", + "documents": [ + "doc-3fa85f64-5717-4562-b3fc-2c963f66afa6" + ] +} +``` + +##### 2.1.1.2 Retrieve Certificate Document + +The Certificate Consumer retrieves a specific certificate document by its ID. +The document is returned as a binary PDF. +Document IDs are listed in the `documents` array of the certificate metadata (see [2.1.1.1 Retrieve Certificate Metadata](#2111-retrieve-certificate-metadata)). + +`GET /certificates/{certificateId}/documents/{documentId}` + +###### 2.1.1.2.1 HTTP Response Codes + +| HTTP Code | Description | +|-----------|--------------------------------------| +| 200 | Binary PDF document returned. | +| 404 | Certificate or document not found. | +| 500 | Internal Server Error. | + +##### 2.1.1.3 Search Certificates + +The Certificate Consumer searches for certificates using BPN filters. +All filter fields are optional and combined with AND logic. +Omitting all filters returns all accessible certificates. + +`POST /certificate-search` + +Query parameters: + +| Parameter | Description | Default | +|------------|--------------------------------------|---------| +| `page` | Zero-based page number | 0 | +| `pageSize` | Number of results per page (max 100) | 10 | + +```json +{ + "legalEntityBpns": [ + "BPNL0000000002CD" + ], + "siteBpns": [ + "BPNS000000000002" + ], + "addressBpns": [ + "BPNA000000000001" + ] +} +``` + +###### 2.1.1.3.1 HTTP Response Codes + +| HTTP Code | Description | +|-----------|---------------------------------------------| +| 200 | Paginated list of matching certificates. | +| 400 | Request malformed and can not be processed. | +| 500 | Internal Server Error. | + +###### 2.1.1.3.2 HTTP Response Body for HTTP Code 200 + +```json +{ + "totalElements": 42, + "totalPages": 5, + "page": 0, + "pageSize": 10, + "content": [ + { + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "certifiedBpn": "BPNL0000000002CD", + "type": { + "certificateType": "ISO9001", + "certificateVersion": "2015" + }, + "registrationNumber": "12 198 54182 TMS", + "validFrom": "2023-01-25", + "validUntil": "2026-01-24", + "trustLevel": "high", + "documents": [ + "doc-3fa85f64-5717-4562-b3fc-2c963f66afa6" + ] + } + ] +} +``` + +##### 2.1.1.4 Company Certificate Request + +The Certificate Consumer requests a specific certificate from the Certificate Provider. + +![alt text](assets/state-machine-certificate-distributor.svg "Certificate Request API State Machine") + +`POST /certificate-request` + +```json +{ + "certifiedBpn": "BPNL00000003AYRE", + "certificateType": "iso9001", + "locationBpns": [ + "BPNA000000000001", + "BPNA000000000002", + "BPNS000000000003" + ] } ``` @@ -163,7 +285,7 @@ The Certificate Consumer is requesting a specific certificate from the Certifica > When a certificate is requested for multiple locations specified in `locationBpns`, the returned certificate's `enclosedSites` attribute may not cover all requested locations. > However, it should include at least one of the specified locations. -##### 2.1.1.1.1 HTTP Response Codes +###### 2.1.1.4.1 HTTP Response Codes | HTTP Code | Description | |-----------|---------------------------------------------------------------------------| @@ -177,60 +299,32 @@ The Certificate Consumer is requesting a specific certificate from the Certifica > This means that in the case of a malformed request, while the API should return a `400` status code, the final EDC response that the consumer receives will be `500`. > Until a future EDC update changes this behavior to proxy all status codes without changes, applications will need to be able to deal with this technical reality. -The detailed response bodies for HTTP Code 200 are described in 2.1.1.1.2 and following. +The detailed response bodies are described in 2.1.1.4.2 and following. HTTP Status Codes 202, 400 and 500 do not come with a response body. -##### 2.1.1.1.2 HTTP Response Body for HTTP Code 200, Status: IN PROGRESS +###### 2.1.1.4.2 HTTP Response Body for HTTP Code 202 -Case: Certificate Request Still In Process +Case: Certificate Request Accepted and In Processing. ```json { - "header": { - "messageId": "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72", - "context": "CompanyCertificateManagement-CCMAPI-Request:1.0.0", - "sentDateTime": "2024-10-07T10:15:00Z", - "senderBpn": "BPNL0000000001AB", - "receiverBpn": "BPNL0000000002CD", - "relatedMessageId": "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72", - "version": "3.1.0" - }, - "content": { - "requestStatus": "IN_PROGRESS" - } + "requestStatus": "IN_PROGRESS" } ``` -##### 2.1.1.1.3 HTTP Response Body for HTTP Code 200, Status: COMPLETED +###### 2.1.1.4.3 HTTP Response Body for HTTP Code 200, Status: COMPLETED -Finished Processing and Certificate available in EDC. -The content body also provides the documentId of the certificate. -This simplifies finding the correct offer for the requested certificate. +Finished Processing and Certificate available. +The `certificateId` can be used to retrieve the certificate via `GET /certificates/{certificateId}`. ```json { - "header": { - "messageId": "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72", - "context": "CompanyCertificateManagement-CCMAPI-Request:1.0.0", - "sentDateTime": "2024-10-07T10:15:00Z", - "senderBpn": "BPNL0000000001AB", - "receiverBpn": "BPNL0000000002CD", - "relatedMessageId": "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72", - "version": "3.1.0" - }, - "content": { - "documentId": "00000000-0000-0000-0000-000000000001", - "requestStatus": "COMPLETED" - } + "requestStatus": "COMPLETED", + "certificateId": "3b4edc05-e214-47a1-b0c2-1d831cdd9ba0" } ``` -> **`documentId` explanation**: -> The reasoning why a documentId (the unique ID of EDC asset of the certificate) is to be returned (and not for example the certificate as a return payload) is, -> so that the Certificate Provider can specify (for each certificate) a dedicated contract offer, and thus use different usage policies for the certificates and API(s). -> That way the Certificate Provider has all options available in terms of data sovereignty and full access control on an EDC (contract based) level. - -##### 2.1.1.1.4 HTTP Response Body for HTTP Code 200, Status: REJECTED +###### 2.1.1.4.4 HTTP Response Body for HTTP Code 200, Status: REJECTED Finished Processing and Certificate Request Rejected. The request errors and location errors SHOULD contain all encountered problems in detail. @@ -238,46 +332,34 @@ The error message is free text. ```json { - "header": { - "messageId": "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72", - "context": "CompanyCertificateManagement-CCMAPI-Request:1.0.0", - "sentDateTime": "2024-10-07T10:15:00Z", - "senderBpn": "BPNL0000000001AB", - "receiverBpn": "BPNL0000000002CD", - "relatedMessageId": "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72", - "version": "3.1.0" - }, - "content": { - "requestStatus": "REJECTED", - "requestErrors": [ - { - "message": "We do not process certificates on Sunday" - }, - { - "message": "Can not provide certicate for requested locations" - } - ], - "locationErrors": [ - { - "bpn": "BPNS000000000003", - "locationErrors": [ - { - "message": "Site BPNS000000000003 is unknown" - } - ] - } - ] - } + "requestStatus": "REJECTED", + "requestErrors": [ + { + "message": "We do not process certificates on Sunday" + }, + { + "message": "Can not provide certificate for requested locations" + } + ], + "locationErrors": [ + { + "bpn": "BPNS000000000003", + "locationErrors": [ + { + "message": "Site BPNS000000000003 is unknown" + } + ] + } + ] } ``` -##### 2.1.1.2 Company Certificate Push +##### 2.1.1.5 Company Certificate Push -Certificate is pushed by the Certificate Provider to the Certificate Consumer. -The enclosed BPNs can be a mix of sites and addresses. -The Certificate Consumer may want to send a subsequent feedback message. +The Certificate Provider notifies the Certificate Consumer about a lifecycle change for a certificate. +The Certificate Consumer can use the `certificateId` to retrieve the certificate metadata and documents using the pull mechanism. -`POST /companycertificate/push` +`POST /certificate-notification/push` ```json { @@ -287,243 +369,162 @@ The Certificate Consumer may want to send a subsequent feedback message. "sentDateTime": "2024-10-07T10:15:00Z", "senderBpn": "BPNL0000000001AB", "receiverBpn": "BPNL0000000002CD", - "relatedMessageId": "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72", "version": "3.1.0", "senderFeedbackUrl": "https://domain.tld/path/to/edc/api/v1/dsp" }, "content": { - "businessPartnerNumber": "BPNL0000000001AB", - "enclosedSites": [ - { - "areaOfApplication": "Development, Marketing und Sales and also Procurement for interior components", - "enclosedSiteBpn": "BPNS00000003AYRE" - } - ], - "registrationNumber": "12 198 54182 TMS", - "uploader": "BPNL0000000001AB", - "document": { - "documentID": "UUID--123456789", - "creationDate": "2024-08-23T13:19:00.280+02:00", - "contentType": "application/pdf", - "contentBase64": "iVBORw0KGgoAAdsfwerTETEfdgd" - }, - "validator": { - "validatorName": "Data service provider X", - "validatorBpn": "BPNL00000007YREZ" - }, - "validUntil": "2026-01-24", - "validFrom": "2023-01-25", - "trustLevel": "none", - "type": { - "certificateVersion": "2015", - "certificateType": "iso9001" - }, - "areaOfApplication": "Development, Marketing und Sales and also Procurement for interior components", - "issuer": { - "issuerName": "TÜV", - "issuerBpn": "BPNL133631123120" - } + "certificateId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "status": "CREATED" } } ``` -The `senderFeedbackUrl` specifies, where the Certificate Provider expects feedback on the status from the Certificate Consumer. +The `status` field indicates the type of lifecycle change: + +| Status | Description | +|----------|--------------------------------------------------------------------| +| CREATED | A new certificate has been created and is available for retrieval. | +| MODIFIED | An existing certificate has been updated. | +| DELETED | A certificate has been removed and is no longer available. | + +The `senderFeedbackUrl` specifies where the Certificate Provider expects feedback on the certificate status from the Certificate Consumer. The expected value **MUST** be a concrete path to the version 1 dataspace protocol endpoint, where a data offer for an asset of type `cx-taxo:CCMAPI` **MUST** be available for the Certificate Consumer. -> [!NOTE] -> **`documentID` spelling** -> -> Please note that in contrast to other requests, the field `documentID` in the push notification request is spelled with a capital `D` due to the spelling in the [aspect model](#31-aspect-model-businesspartnercertificate) -> and refers to the ID of the document of the certificate, not the unique ID of the EDC asset of the certificate. +###### 2.1.1.5.1 HTTP Response Codes + +| HTTP Code | Description | +|-----------|---------------------------------------------------------------------------------------| +| 200 | Notification processed successfully. | +| 500 | Internal Server Error. | +| 501 | The Certificate Consumer currently does not support processing of push notifications. | -##### 2.1.1.3 Company Certificate Feedback +##### 2.1.1.6 Company Certificate Feedback -`POST /companycertificate/status` +`POST /certificate-status` This API is used by the Certificate Consumer to provide feedback on the status to the Certificate Provider, either accepting or rejecting the provided certificate. This applies regardless of whether the certificate was [pulled](#2152-pull-mechanism) or [pushed](#2151-push-mechanism). -If the certificate being given feedback on was consumed using the pull mechanism, the `documentId` in the payload must refer to the unique ID of the EDC asset of the certificate. - -When the push mechanism was used, the `relatedMessageId` must be set to the `messageId` of the push notification for which feedback is being provided. - -##### 2.1.1.3.1 Company Certificate Feedback: Received +###### 2.1.1.6.1 Company Certificate Feedback: Received Certificate has been received by Certificate Consumer and validation is in progress. ```json { - "header": { - "messageId": "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72", - "context": "CompanyCertificateManagement-CCMAPI-Status:1.0.0", - "sentDateTime": "2024-10-07T10:15:00Z", - "senderBpn": "BPNL0000000001AB", - "receiverBpn": "BPNL0000000002CD", - "relatedMessageId": "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72", - "version": "3.1.0", - "senderFeedbackUrl": "https://domain.tld/path/to/edc/api/v1/dsp" - }, - "content": { - "documentId": "00000000-0000-0000-0000-000000000002", - "certificateStatus": "RECEIVED", - "locationBpns": [ - "BPNS000000000001", - "BPNS000000000002", - "BPNS000000000003", - "BPNA000000000001", - "BPNA000000000002", - "BPNA000000000003" - ] - } + "certificateId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "certificateStatus": "RECEIVED", + "locationBpns": [ + "BPNS000000000001", + "BPNS000000000002", + "BPNS000000000003", + "BPNA000000000001", + "BPNA000000000002", + "BPNA000000000003" + ] } ``` -##### 2.1.1.3.2 Company Certificate Feedback: Accepted +###### 2.1.1.6.2 Company Certificate Feedback: Accepted Certificate is accepted. -The documentId **MUST** match the documentId that was communicated by the certificate provider. +The `certificateId` **MUST** match the `certificateId` communicated by the Certificate Provider. The `locationBpns` can be a mix of sites and addresses. ```json { - "header": { - "messageId": "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72", - "context": "CompanyCertificateManagement-CCMAPI-Status:1.0.0", - "sentDateTime": "2024-10-07T10:15:00Z", - "senderBpn": "BPNL0000000001AB", - "receiverBpn": "BPNL0000000002CD", - "relatedMessageId": "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72", - "version": "3.1.0", - "senderFeedbackUrl": "https://domain.tld/path/to/edc/api/v1/dsp" - }, - "content": { - "documentId": "00000000-0000-0000-0000-000000000001", - "certificateStatus": "ACCEPTED", - "locationBpns": [ - "BPNS000000000001", - "BPNS000000000002", - "BPNS000000000003", - "BPNA000000000001", - "BPNA000000000002", - "BPNA000000000003" - ] - } + "certificateId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "certificateStatus": "ACCEPTED", + "locationBpns": [ + "BPNS000000000001", + "BPNS000000000002", + "BPNS000000000003", + "BPNA000000000001", + "BPNA000000000002", + "BPNA000000000003" + ] } ``` -##### 2.1.1.3.3 Company Certificate Feedback: Rejected +###### 2.1.1.6.3 Company Certificate Feedback: Rejected Certificate is rejected by the Certificate Consumer with one or multiple reasons. ```json { - "header": { - "messageId": "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72", - "context": "CompanyCertificateManagement-CCMAPI-Status:1.0.0", - "sentDateTime": "2024-10-07T10:15:00Z", - "senderBpn": "BPNL0000000001AB", - "receiverBpn": "BPNL0000000002CD", - "relatedMessageId": "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72", - "version": "3.1.0", - "senderFeedbackUrl": "https://domain.tld/path/to/edc/api/v1/dsp" - }, - "content": { - "documentId": "00000000-0000-0000-0000-000000000003", - "certificateStatus": "REJECTED", - "certificateErrors": [ - { - "message": "We do not process certificates on Sunday" - }, - { - "message": "Certificate has expired in 2024" - }, - { - "message": "Certificate was revoked" - }, - { - "message": "Unexpected data format" - }, - { - "message": "Unexpected language expected English, received Mandarin" - }, - { - "message": "Expected PDF, received JPG" - }, - { - "message": "Unknown BPNL000000000000" - } - ], - "locationBpns": [ - "BPNS000000000001", - "BPNS000000000002", - "BPNS000000000003", - "BPNA000000000001", - "BPNA000000000002", - "BPNA000000000003" - ], - "locationErrors": [ - { - "bpn": "BPNS000000000002", - "locationErrors": [ - { - "message": "Site BPNS000000000002 has been Rejected" - } - ] - }, - { - "bpn": "BPNS000000000003", - "locationErrors": [ - { - "message": "Site BPNS000000000003 is missing" - } - ] - } - ] - } + "certificateId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "certificateStatus": "REJECTED", + "certificateErrors": [ + { + "message": "We do not process certificates on Sunday" + }, + { + "message": "Certificate has expired in 2024" + }, + { + "message": "Certificate was revoked" + }, + { + "message": "Unexpected data format" + }, + { + "message": "Unexpected language expected English, received Mandarin" + }, + { + "message": "Expected PDF, received JPG" + }, + { + "message": "Unknown BPNL000000000000" + } + ], + "locationBpns": [ + "BPNS000000000001", + "BPNS000000000002", + "BPNS000000000003", + "BPNA000000000001", + "BPNA000000000002", + "BPNA000000000003" + ], + "locationErrors": [ + { + "bpn": "BPNS000000000002", + "locationErrors": [ + { + "message": "Site BPNS000000000002 has been Rejected" + } + ] + }, + { + "bpn": "BPNS000000000003", + "locationErrors": [ + { + "message": "Site BPNS000000000003 is missing" + } + ] + } + ] } ``` -##### 2.1.1.4 Company Certificate Available - -The Certificate Consumer is notified that a certificate is available. -The Certificate Consumer may want to consume the certificate via the pull mechanism. +###### 2.1.1.6.4 HTTP Response Codes -```json -{ - "header": { - "messageId": "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72", - "context": "CompanyCertificateManagement-CCMAPI-Available:1.0.0", - "sentDateTime": "2024-10-07T10:15:00Z", - "senderBpn": "BPNL0000000001AB", - "receiverBpn": "BPNL0000000002CD", - "relatedMessageId": "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72", - "version": "3.1.0", - "senderFeedbackUrl": "https://domain.tld/path/to/edc/api/v1/dsp" - }, - "content": { - "documentId": "00000000-0000-0000-0000-000000000001", - "certificateType": "iso9001", - "locationBpns": [ - "BPNS000000000001", - "BPNS000000000002", - "BPNS000000000003", - "BPNA000000000001", - "BPNA000000000002", - "BPNA000000000003" - ] - } -} -``` +| HTTP Code | Description | +|-----------|------------------------| +| 200 | Status updated. | +| 500 | Internal Server Error. | #### 2.1.2 ERROR HANDLING -The following HTTP response codes **MUST** be defined for all resources: +The following HTTP response codes apply across the API resources: -| Status Code | Description | -|-------------|----------------------------| -| 200 | OK | -| 500 | Internal Server Error | +| Status Code | Description | +|-------------|---------------------------------------------------------| +| 200 | OK | +| 202 | Request accepted and in processing | +| 400 | Bad Request — request malformed and cannot be processed | +| 404 | Not Found | +| 500 | Internal Server Error | +| 501 | Not Implemented — feature not supported by the receiver | #### 2.1.3 Available data types @@ -548,7 +549,7 @@ The property [[type]](http://purl.org/dc/terms/type) **MUST** reference the name | **Type** | **Subject** | **Version** | **Description** | |----------------|-----------------------------------------------------|-------------|-----------------| -| cx-taxo:CCMAPI | cx-taxo:CompanyCertificateManagementNotificationApi | 3.0 | Offers *Certificate Notification API* for [requesting](#2111-company-certificate-request) and [pushing](#2112-company-certificate-push) certificates as well as sending [feedback](#2113-company-certificate-feedback) on the status for provided certificates and receiving [availability](#2114-company-certificate-available) notifications. | +| cx-taxo:CCMAPI | cx-taxo:CompanyCertificateManagementNotificationApi | 3.0 | Offers *Certificate Management API* for [retrieving certificates](#2111-retrieve-certificate-metadata), [searching certificates](#2113-search-certificates), [requesting](#2114-company-certificate-request) and [pushing](#2115-company-certificate-push) certificates, as well as sending [feedback](#2116-company-certificate-feedback) on the status for provided certificates. | There **MUST** only be one unique asset per API (subject and version) across all connectors of one BPNL. @@ -580,14 +581,14 @@ It doesn't matter if the assets are offered in one or in different connectors, a "dct:subject": { "@id": "cx-taxo:CompanyCertificateManagementNotificationApi" }, - "dct:description": "Offers Certificate Notification API for requesting and pushing certificates as well as sending feedback on the status for provided certificates and receiving availability notifications.", + "dct:description": "Offers Certificate Management API for retrieving, searching, requesting and pushing certificates, as well as sending feedback on the status for provided certificates.", "cx-common:version": "3.0" }, "dataAddress": { "@type": "DataAddress", "type": "HttpData", - "baseUrl": "https://backend-base-url/certificate-notification-api-base-path", - "proxyQueryParams": "false", + "baseUrl": "https://backend-base-url/certificate-api-base-path", + "proxyQueryParams": "true", "proxyPath": "true", "proxyMethod": "false", "proxyBody": "true" @@ -672,28 +673,28 @@ Certificate Provider & Certificate Consumer: - Certificate Provider **MUST** expose company certificates in their catalog when using the pull mechanism. - Certificate Provider **MUST** set the correct access and usage policy on the certificate offer to allow consumption by Consumer(s) when using the pull mechanism. -- Certificate Consumer **MAY** implement the [push endpoint](#2112-company-certificate-push) for the Certificate Provider to push certificates to, but **MUST** set the correct access and usage policy on the offer, when choosing to do so. -- Certificate Consumer **MAY** send a certificate request via `POST /companycertificate/request` which **MUST** be replied to by the Certificate Provider according to the endpoint definitions. -- Certificate Consumer **MAY** send a notification of reception when the certificate validation has started via `POST /companycertificate/status`. -- Certificate Consumer **MAY** send a notification of acceptance or rejection via `POST /companycertificate/status`. +- Certificate Consumer **MAY** implement the [push endpoint](#2115-company-certificate-push) for the Certificate Provider to send push notifications to, but **MUST** set the correct access and usage policy on the offer, when choosing to do so. +- Certificate Consumer **MAY** send a certificate request via `POST /certificate-request` which **MUST** be replied to by the Certificate Provider according to the endpoint definitions. +- Certificate Consumer **MAY** send a notification of reception when the certificate validation has started via `POST /certificate-status`. +- Certificate Consumer **MAY** send a notification of acceptance or rejection via `POST /certificate-status`. Certificate Provider **MUST** respond according to the [error handling](#212-error-handling). -- Certificate Provider **MAY** send a notification of availability via `POST /companycertificate/available` after the referenced company certificate is exposed in their catalog. - Certificate Consumer **MUST** respond according to the [error handling](#212-error-handling) and **SHOULD** get the new certificate via the pull mechanism. -- Certificate Consumer **MAY** implement the [available endpoint](#2114-company-certificate-available) for the Certificate Provider to send availability notifications to, but **MUST** set the correct access and usage policy on the offer, when choosing to do so. +- Certificate Provider **MUST** send a push notification with `status: CREATED` via `POST /certificate-notification/push` after the referenced company certificate is exposed in their catalog. + Certificate Consumer **MUST** respond according to the [error handling](#212-error-handling) and **SHOULD** retrieve the new certificate via the pull mechanism. +- Certificate Consumer **MAY** implement the [push endpoint](#2115-company-certificate-push) for the Certificate Provider to send lifecycle notifications to, but **MUST** set the correct access and usage policy on the offer, when choosing to do so. Business Application Provider: -- Business Application Provider **MUST** implement all features of the Certificate Notification API, including the support of the push, the pull and also the feedback and the available mechanism. +- Business Application Provider **MUST** implement all features of the Certificate Management API, including the support of the push, the pull and also the feedback mechanism. - Business Application Provider **MUST** offer the push mechanism option to the application user, if the Certificate Consumer supports the push mechanism. ##### 2.1.5.1 PUSH Mechanism ![PUSH Scenarios](assets/certificate-push.svg) -The Certificate PUSH Diagram describes the secure transmission of certificates from a Backend Certificate Provider to a Backend Certificate Consumer via EDC (Eclipse Data Connector) components. -The process starts with a contract agreement for a Notification Asset, followed by the provider pushing the certificate to the provided endpoint in the asset. -The certificate is then processed by the Backend Certificate Consumer, which finalizes the workflow by generating a feedback message which is pushed to the provider. +The Certificate PUSH Diagram describes the lifecycle notification flow from a Backend Certificate Provider to a Backend Certificate Consumer via EDC (Eclipse Data Connector) components. +The process starts with a contract agreement for a Notification Asset, followed by the provider sending a push notification (containing the `certificateId` and a `status` of CREATED, MODIFIED, or DELETED) to the Consumer's endpoint. +The Certificate Consumer then uses the pull mechanism to retrieve the certificate data, and finalizes the workflow by generating a feedback message sent to the provider. ##### 2.1.5.2 PULL Mechanism @@ -705,11 +706,11 @@ The Consumer searches the catalog using specific filters, initiates a contract n The Data Plane then facilitates secure data transfer, allowing the consumer to pull the certificate. Once retrieved, the Backend Certificate Consumer processes the certificate and sends a feedback message to confirm the status. -##### 2.1.5.3 AVAILABLE notification followed by PULL mechanism +##### 2.1.5.3 PUSH Notification followed by PULL mechanism -After the Certificate Provider has created a Certificate Asset with the corresponding contract definition in the EDC Catalog, the Certificate Provider sends a Certificate Available Notification to the Certificate Consumer. -The Certificate Consumer uses the above described PULL mechanism to get the certificate data. -This reduces the Certificate Consumers need for active checks for missing certificates or certificate updates and enables access to the latest certificate data. +After the Certificate Provider has created a Certificate Asset with the corresponding contract definition in the EDC Catalog, the Certificate Provider sends a push notification with `status: CREATED` to the Certificate Consumer. +The Certificate Consumer uses the above described PULL mechanism to retrieve the certificate data using the `certificateId` provided in the notification. +This reduces the Certificate Consumer's need for active checks for missing certificates or certificate updates and enables access to the latest certificate data. #### 2.1.6 POLICY CONSTRAINTS FOR DATA EXCHANGE diff --git a/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml b/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml index fc925a974b..757d53a980 100644 --- a/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml +++ b/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml @@ -89,83 +89,59 @@ components: CertificateRequest: type: object required: - - header - - content + - certifiedBpn + - certificateType properties: - header: - $ref: '#/components/schemas/Header' - content: - type: object - required: - - certifiedBpn - - certificateType - properties: - certifiedBpn: - type: string - description: The BPNL of the legal entity for which the requested certificate has been issued - pattern: "^BPNL[a-zA-Z0-9]{12}$" - examples: - - "BPNL0000000002CD" - certificateType: - type: string - description: The type of certificate the certificate consumer requests - examples: - - "iso9001" - locationBpns: - type: array - description: The BPNs of the site or address locations for which the certificate consumer requests the certificate - items: - $ref: '#/components/schemas/BpnLocation' + certifiedBpn: + type: string + description: The BPNL of the legal entity for which the requested certificate has been issued + pattern: "^BPNL[a-zA-Z0-9]{12}$" + examples: + - "BPNL0000000002CD" + certificateType: + type: string + description: The type of certificate the certificate consumer requests + examples: + - "iso9001" + locationBpns: + type: array + description: The BPNs of the site or address locations for which the certificate consumer requests the certificate + items: + $ref: '#/components/schemas/BpnLocation' CertificateRequestFinishedResponse: - type: object - required: - - header - - content - properties: - header: - $ref: '#/components/schemas/Header' - content: - oneOf: - - $ref: '#/components/schemas/CertificateRequestCompletedResponseContent' - - $ref: '#/components/schemas/CertificateRequestRejectedResponseContent' - discriminator: - propertyName: requestStatus + oneOf: + - $ref: '#/components/schemas/CertificateRequestCompletedResponse' + - $ref: '#/components/schemas/CertificateRequestRejectedResponse' + discriminator: + propertyName: requestStatus CertificateRequestInProgressResponse: type: object required: - - header - - content + - requestStatus properties: - header: - $ref: '#/components/schemas/Header' - content: - type: object - required: - - requestStatus - properties: - requestStatus: - type: string - description: The processing status in which the certificate request process is currently in - const: "IN_PROGRESS" + requestStatus: + type: string + description: The processing status in which the certificate request process is currently in + const: "IN_PROGRESS" - CertificateRequestCompletedResponseContent: + CertificateRequestCompletedResponse: type: object required: - requestStatus - - documentId + - certificateId properties: requestStatus: type: string description: The processing status in which the certificate request process is currently in const: "COMPLETED" - documentId: + certificateId: type: string pattern: uuid description: The UUID of the asset under which the certificate is available - CertificateRequestRejectedResponseContent: + CertificateRequestRejectedResponse: type: object required: - requestStatus @@ -203,7 +179,20 @@ components: header: $ref: '#/components/schemas/FeedbackUrlHeader' content: - $ref: 'https://raw.githubusercontent.com/eclipse-tractusx/sldt-semantic-models/refs/heads/main/io.catenax.business_partner_certificate/3.1.0/gen/BusinessPartnerCertificate-schema.json' + $ref: '#/components/schemas/CertificatePushContent' + + CertificatePushContent: + type: object + required: + - certificateId + - status + properties: + certificateId: + type: string + format: uuid + status: + type: string + enum: [CREATED, MODIFIED, DELETED] Error: type: object @@ -238,180 +227,373 @@ components: CertificateStatus: type: object required: - - header - - content + - certificateId + - certificateStatus + - locationBpns properties: - header: - $ref: '#/components/schemas/FeedbackUrlHeader' - content: - type: object - required: - - documentId - - certificateStatus - - locationBpns - properties: - documentId: - type: string - format: uuid - certificateStatus: - type: string - enum: [ACCEPTED, REJECTED, RECEIVED] - certificateErrors: - type: array - items: - $ref: '#/components/schemas/Error' - locationBpns: - type: array - items: - $ref: '#/components/schemas/BpnLocation' - locationErrors: - type: array - items: - $ref: '#/components/schemas/LocationErrorCollection' + certificateId: + type: string + format: uuid + certificateStatus: + type: string + enum: [ACCEPTED, REJECTED, RECEIVED] + certificateErrors: + type: array + items: + $ref: '#/components/schemas/Error' + locationBpns: + type: array + items: + $ref: '#/components/schemas/BpnLocation' + locationErrors: + type: array + items: + $ref: '#/components/schemas/LocationErrorCollection' - CertificateAvailable: + CertificateType: type: object required: - - header + - certificateType + properties: + certificateType: + type: string + description: Type of the certificate as defined on the document, valid types are registered at BPN metadatacontroller + examples: + - "ISO9001" + - "IATF 16949" + certificateVersion: + type: string + description: Version of the certificate as defined on the document, usually the specific version of a certification standard + examples: + - "2015" + + LocationBpn: + type: object + required: + - locationBpn + properties: + locationBpn: + type: string + description: The Business Partner Number (BPNS or BPNA) of a location + pattern: "^(?:BPNS|BPNA)[a-zA-Z0-9]{12}$" + examples: + - "BPNS000000000002" + - "BPNA000000000001" + areaOfApplication: + type: string + description: Details on which areas / application types a certificate is valid for a company and/or site + + CertificateIssuer: + type: object + required: + - issuerName + properties: + issuerName: + type: string + description: Name of the Issuer i.e. Certifying Authority + examples: + - "TUEV Sued" + issuerBpn: + type: string + description: The Business Partner Number (BPN) of the Issuer + pattern: "^BPNL[a-zA-Z0-9]{12}$" + examples: + - "BPNL0000000001AB" + + CertificateValidator: + type: object + properties: + validatorName: + type: string + description: The name of the data service provider who validated the given certificate + validatorBpn: + type: string + description: The Business Partner Number (BPN) of the data service provider who validated the given certificate + pattern: "^BPNL[a-zA-Z0-9]{12}$" + examples: + - "BPNL0000000003EF" + + CertificateRetrievalMetadata: + type: object + required: + - certificateId + - certifiedBpn + - type + - registrationNumber + - validFrom + - validUntil + - trustLevel + properties: + certificateId: + type: string + description: The id of the certificate document as stored by the data service provider + examples: + - "cert-550e8400-e29b-41d4-a716-446655440000" + certifiedBpn: + type: string + description: The Business Partner Number (BPN) of the certified legal entity on which the certificate is issued + pattern: "^BPNL[a-zA-Z0-9]{12}$" + examples: + - "BPNL0000000002CD" + type: + $ref: '#/components/schemas/CertificateType' + registrationNumber: + type: string + description: Registration number of the certificate as defined on the certificate + areaOfApplication: + type: string + description: Details on which areas / application types a certificate is valid for a company and/or site + locations: + type: array + description: Locations covered by the certificate (BPNS or BPNA) + items: + $ref: '#/components/schemas/LocationBpn' + validFrom: + type: string + format: date + description: Valid from date as defined on the certificate + examples: + - "2023-01-25" + validUntil: + type: string + format: date + description: Valid until date as defined on the certificate. If the certificate never expires, the value is expected to be 9999-12-31 + examples: + - "2026-01-24" + issuer: + $ref: '#/components/schemas/CertificateIssuer' + trustLevel: + type: string + description: The trust level of the given certificate + enum: [none, low, high, trusted] + validator: + $ref: '#/components/schemas/CertificateValidator' + uploader: + type: string + description: The Business Partner Number (BPN) of the business partner who originally provided the certificate data or document + pattern: "^BPNL[a-zA-Z0-9]{12}$" + examples: + - "BPNL0000000001AB" + documents: + type: array + description: IDs of one or more documents associated with the certificate (e.g. main certificate and annexes). Use GET /certificates/{certificateId}/documents/{documentId} to retrieve each document. + items: + type: string + description: The document ID as stored by the data service provider + examples: + - "doc-3fa85f64-5717-4562-b3fc-2c963f66afa6" + + CertificateSearchRequest: + type: object + properties: + legalEntityBpns: + type: array + description: Filter results to certificates issued for these legal entity BPNLs + items: + type: string + pattern: "^BPNL[a-zA-Z0-9]{12}$" + examples: + - "BPNL0000000002CD" + siteBpns: + type: array + description: Filter results to certificates covering these site BPNSs + items: + type: string + pattern: "^BPNS[a-zA-Z0-9]{12}$" + examples: + - "BPNS000000000002" + addressBpns: + type: array + description: Filter results to certificates covering these address BPNAs + items: + type: string + pattern: "^BPNA[a-zA-Z0-9]{12}$" + examples: + - "BPNA000000000001" + + PaginatedCertificateList: + type: object + required: + - totalElements + - totalPages + - page + - pageSize - content properties: - header: - $ref: '#/components/schemas/FeedbackUrlHeader' + totalElements: + type: integer + description: Total number of certificates matching the search criteria + totalPages: + type: integer + description: Total number of pages + page: + type: integer + description: Current page number (zero-based) + pageSize: + type: integer + description: Number of items per page content: - type: object - required: - - documentId - - certificateType - properties: - documentId: - type: string - format: uuid - certificateType: - type: string - description: "Type of the certificate as defined on the document, valid types are registered at BPN metadatacontroller" - locationBpns: - type: array - items: - $ref: '#/components/schemas/BpnLocation' + type: array + description: Certificates on the current page + items: + $ref: '#/components/schemas/CertificateRetrievalMetadata' examples: CertificateRequestCompleted: summary: A response for a successfully completed certificate request process value: - header: - messagedId: "urn:uuid:3b4edc05-e214-47a1-b0c2-1d831cdd9ba9" - context: "CompanyCertificateManagement-CCMAPI-Request:1.0.0" - sentDateTime: "2024-10-07T10:15:00Z" - senderBpn: "BPNL0000000001AB" - receiverBpn: "BPNL0000000002CD" - relatedMessageId: "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72" - version: "3.1.0" - content: - requestStatus: "COMPLETED" - documentId: "3b4edc05-e214-47a1-b0c2-1d831cdd9ba" + requestStatus: "COMPLETED" + certificateId: "3b4edc05-e214-47a1-b0c2-1d831cdd9ba" CertificateRequestRejected: summary: A response for a rejected certificate request process value: - header: - messagedId: "urn:uuid:3b4edc05-e214-47a1-b0c2-1d831cdd9ba9" - context: "CompanyCertificateManagement-CCMAPI-Request:1.0.0" - sentDateTime: "2024-10-07T10:15:00Z" - senderBpn: "BPNL0000000001AB" - receiverBpn: "BPNL0000000002CD" - relatedMessageId: "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72" - version: "3.1.0" - content: - requestStatus: "REJECTED" - requestErrors: - - message: "We do not process certificates on Sunday" - - message: "Can not provide certicate for requested locations" - locationErrors: - - bpn: "BPNS000000000003" - locationErrors: - - message: "Site BPNS000000000003 is unknown" + requestStatus: "REJECTED" + requestErrors: + - message: "We do not process certificates on Sunday" + - message: "Can not provide certicate for requested locations" + locationErrors: + - bpn: "BPNS000000000003" + locationErrors: + - message: "Site BPNS000000000003 is unknown" CertificateReceivedStatus: summary: Consumer received certificate value: - header: - messagedId: "urn:uuid:3b4edc05-e214-47a1-b0c2-1d831cdd9ba9" - context: "CompanyCertificateManagement-CCMAPI-Status:1.0.0" - sentDateTime: "2024-10-07T10:15:00Z" - senderBpn: "BPNL0000000001AB" - receiverBpn: "BPNL0000000002CD" - relatedMessageId: "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72" - version: "3.1.0" - senderFeedbackUrl: "https://domain.tld/path/to/edc/api/v1/dsp" - content: - documentId: "3fa85f64-5717-4562-b3fc-2c963f66afa6" - certificateStatus: "RECEIVED" - locationBpns: - - "BPNS000000000003" + certificateId: "3fa85f64-5717-4562-b3fc-2c963f66afa6" + certificateStatus: "RECEIVED" + locationBpns: + - "BPNS000000000003" CertificateAcceptedStatus: summary: Consumer accepts received certificate value: - header: - messagedId: "urn:uuid:3b4edc05-e214-47a1-b0c2-1d831cdd9ba9" - context: "CompanyCertificateManagement-CCMAPI-Status:1.0.0" - sentDateTime: "2024-10-07T10:15:00Z" - senderBpn: "BPNL0000000001AB" - receiverBpn: "BPNL0000000002CD" - relatedMessageId: "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72" - version: "3.1.0" - senderFeedbackUrl: "https://domain.tld/path/to/edc/api/v1/dsp" - content: - documentId: "3fa85f64-5717-4562-b3fc-2c963f66afa6" - certificateStatus: "ACCEPTED" - locationBpns: - - "BPNS000000000003" + certificateId: "3fa85f64-5717-4562-b3fc-2c963f66afa6" + certificateStatus: "ACCEPTED" + locationBpns: + - "BPNS000000000003" CertificateRejectedStatus: summary: Consumer rejected received certificate value: - header: - messagedId: "urn:uuid:3b4edc05-e214-47a1-b0c2-1d831cdd9ba9" - context: "CompanyCertificateManagement-CCMAPI-Status:1.0.0" - sentDateTime: "2024-10-07T10:15:00Z" - senderBpn: "BPNL0000000001AB" - receiverBpn: "BPNL0000000002CD" - relatedMessageId: "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72" - version: "3.1.0" - senderFeedbackUrl: "https://domain.tld/path/to/edc/api/v1/dsp" - content: - documentId: "3fa85f64-5717-4562-b3fc-2c963f66afa6" - certificateStatus: "REJECTED" - locationBpns: - - "BPNS000000000003" - certificateErrors: - - message: "We do not process certificates on Sunday" - - message: "Can not provide certicate for requested locations" - locationErrors: - - bpn: "BPNS000000000003" - locationErrors: - - message: "Site BPNS000000000003 is unknown" - - CertificateAvailable: - summary: Provider notifies consumer that a certificate is available - value: - header: - messagedId: "urn:uuid:3b4edc05-e214-47a1-b0c2-1d831cdd9ba9" - context: "CompanyCertificateManagement-CCMAPI-Available:1.0.0" - sentDateTime: "2024-10-07T10:15:00Z" - senderBpn: "BPNL0000000001AB" - receiverBpn: "BPNL0000000002CD" - relatedMessageId: "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72" - version: "3.1.0" - senderFeedbackUrl: "https://domain.tld/path/to/edc/api/v1/dsp" - content: - documentId: "3fa85f64-5717-4562-b3fc-2c963f66afa6" - certificateType: "iso9001" - locationBpns: - - "BPNS000000000003" + certificateId: "3fa85f64-5717-4562-b3fc-2c963f66afa6" + certificateStatus: "REJECTED" + locationBpns: + - "BPNS000000000003" + certificateErrors: + - message: "We do not process certificates on Sunday" + - message: "Can not provide certicate for requested locations" + locationErrors: + - bpn: "BPNS000000000003" + locationErrors: + - message: "Site BPNS000000000003 is unknown" paths: - /companycertificate/request: + /certificates/{certificateId}: + get: + summary: Retrieve a certificate by its ID + description: | + Returns the certificate metadata. The `documents` array lists the IDs of associated documents + (e.g. main certificate and annexes). Use GET /certificates/{certificateId}/documents/{documentId} + to retrieve each document as a binary PDF. + tags: + - Certificate Provider + parameters: + - name: certificateId + in: path + required: true + schema: + type: string + description: The certificate identifier + responses: + '200': + description: Certificate metadata returned successfully + content: + application/json: + schema: + $ref: '#/components/schemas/CertificateRetrievalMetadata' + '404': + description: Certificate not found + '500': + description: Internal server error + + /certificates/{certificateId}/documents/{documentId}: + get: + summary: Retrieve a certificate document by its ID + description: | + Returns the binary PDF document identified by `documentId` associated with the given certificate. + Document IDs are listed in the `documents` array of the certificate metadata. + tags: + - Certificate Provider + parameters: + - name: certificateId + in: path + required: true + schema: + type: string + description: The certificate identifier + - name: documentId + in: path + required: true + schema: + type: string + description: The document identifier + responses: + '200': + description: Document returned successfully + content: + application/pdf: + schema: + type: string + format: binary + '404': + description: Certificate or document not found + '500': + description: Internal server error + + /certificate-search: + post: + summary: Search certificates by BPN filters + description: | + Returns a paginated list of certificates matching the given BPN filters. + All filter lists are optional and combined with AND logic; omitting all filters returns all accessible certificates. + tags: + - Certificate Provider + parameters: + - name: page + in: query + description: Zero-based page number + schema: + type: integer + minimum: 0 + default: 0 + - name: pageSize + in: query + description: Number of results per page + schema: + type: integer + minimum: 1 + maximum: 100 + default: 10 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CertificateSearchRequest' + responses: + '200': + description: Paginated list of matching certificates + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedCertificateList' + '400': + description: Request malformed and can not be processed + '500': + description: Internal server error + + /certificate-request: post: summary: Certificate consumer requests a specific certificate from certificate provider tags: @@ -445,9 +627,9 @@ paths: '500': description: Internal server error - /companycertificate/push: + /certificate-notification/push: post: - summary: Certificate provider sends a certificate to the certificate consumer + summary: Certificate provider notifies certificate consumer about a lifecycle change for a certificate tags: - Certificate Consumer requestBody: @@ -461,8 +643,10 @@ paths: description: Notification processed successfully '500': description: Internal server error + '501': + description: The certificate consumer currently does not support processing of notifications - /companycertificate/status: + /certificate-status: post: summary: Certificate consumer sends feedback on the status of a consumed certificate to the certificate provider tags: @@ -485,21 +669,3 @@ paths: description: Status updated successfully '500': description: Internal server error - - /companycertificate/available: - post: - summary: Certificate provider notifies the certificate consumer that a certificate is available - tags: - - Certificate Consumer - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CertificateAvailable' - examples: - CertificateAvailable: - $ref: '#/components/examples/CertificateAvailable' - responses: - '200': - description: Notification processed successfully From f9d9530742a579a2c855e461755f268908a59391 Mon Sep 17 00:00:00 2001 From: Nico Koprowski Date: Tue, 12 May 2026 11:34:02 +0800 Subject: [PATCH 02/14] docs(0135): remove mentions of the certificate Asset from the standardization document --- .../CX-0135-CompanyCertificateManagement.md | 82 +++---------------- 1 file changed, 10 insertions(+), 72 deletions(-) diff --git a/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md b/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md index cb722c7129..01d6a76a9c 100644 --- a/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md +++ b/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md @@ -38,7 +38,7 @@ The following company certificate use cases are supported in this release: 1. Certificate Provider wants to publish a certificate / Certificate Consumer wants to discover a published certificate. 2. Certificate Consumer wants to request a certificate from a specific Certificate Provider 3. Certificate Consumer wants to notify a Certificate Provider of acceptance or rejection of a consumed certificate via a feedback message -4. Certificate Provider wants to notify a Certificate Consumer of the availability of a new certificate asset +4. Certificate Provider wants to notify a Certificate Consumer of a certificate lifecycle change via a push notification For avoidance of the doubt, we are not replacing the existing publication semantic model. @@ -532,7 +532,7 @@ The API **MUST** use JSON formatted data transmitted over HTTPS. #### 2.1.4 Data asset structure -The following sections detail how notification API and certificate assets should be offered in the dataspace. +The following section details how the Certificate Management API asset should be offered in the dataspace. Please note the depicted examples show `@id` fields with random example UUIDs. Every dataspace participant may use their individual random uuid. @@ -604,66 +604,6 @@ It doesn't matter if the assets are offered in one or in different connectors, a > The **API assets** are identified by the combination of `dct:subject` and `cx-common:version`. > When searching the EDC catalog for a specific API, make use of those properties in the catalog filter. -##### 2.1.4.2 Certificates - -- The certificate assets **MUST** be created by the Certificate Provider in their connector catalog to be consumed by the Certificate Consumer when using the pull mechanism. -- The property certificateType **MUST** reference the type of the certificate as defined in [3.2.2 Certificate Type](#322-certificate-type). -- The property enclosedSites **MUST** contain all BPNSs and BPNAs for which the certificate is valid. -- The subject **MUST** reference `cx-taxo:CompanyCertificate`. -- Additionally, the assets **MUST** contain the type `cx-taxo:Submodel` and the semanticId specified in [3.1.2 IDENTIFIER OF SEMANTIC MODEL](#312-identifier-of-semantic-model). - -**Example Certificate EDC Asset:** - -```json -{ - "@id": "d195fa2f-e6bc-4cd6-94d4-2bb76e4bb548", - "@type": "Asset", - "properties": { - "dct:type": { - "@id": "cx-taxo:Submodel" - }, - "aas:semanticId": { - "@id": "urn:samm:io.catenax.business_partner_certificate:3.1.0#BusinessPartnerCertificate" - }, - "dct:certificateType": { - "@id": "cx-taxo:iso9001" - }, - "dct:enclosedSites": [ - { - "@id": "cx-taxo:BPNS000000000001" - }, - { - "@id": "cx-taxo:BPNS000000000002" - }, - { - "@id": "cx-taxo:BPNA000000000001" - } - ], - "dct:subject": { - "@id": "cx-taxo:CompanyCertificate" - }, - "dct:description": "Business Partner Company Certificate", - "cx-common:version": "3.0" - }, - "dataAddress": { - "@type": "DataAddress", - "type": "HttpData", - "baseUrl": "https://backend-base-url/certificate-management-api-base-path", - "proxyQueryParams": "false", - "proxyPath": "false", - "proxyMethod": "false", - "proxyBody": "false" - }, - "@context": { - "dct": "http://purl.org/dc/terms/", - "cx-taxo": "https://w3id.org/catenax/taxonomy#", - "cx-common": "https://w3id.org/catenax/ontology/common#" - } -} -``` - -> The **certificate assets** are identified by the combination of `dct:subject`, `dct:certificateType` and `dct:enclosedSites`. -> When searching the EDC catalog for a specific asset for which the assetId is unkown, make use of those properties in the catalog filter. #### 2.1.5 MESSAGE FLOW EXPECTATIONS @@ -671,7 +611,7 @@ Certificate Provider & Certificate Consumer: - Certificate Provider **MUST** support at least one of the certificate provision mechanisms (push and/or pull mechanism) - Certificate Provider **MUST** expose company certificates in their catalog when using the pull mechanism. -- Certificate Provider **MUST** set the correct access and usage policy on the certificate offer to allow consumption by Consumer(s) when using the pull mechanism. +- Certificate Provider **MUST** set the correct access and usage policy on the certificate API offer to allow consumption by Consumer(s) when using the pull mechanism. - Certificate Consumer **MAY** implement the [push endpoint](#2115-company-certificate-push) for the Certificate Provider to send push notifications to, but **MUST** set the correct access and usage policy on the offer, when choosing to do so. - Certificate Consumer **MAY** send a certificate request via `POST /certificate-request` which **MUST** be replied to by the Certificate Provider according to the endpoint definitions. @@ -700,15 +640,14 @@ The Certificate Consumer then uses the pull mechanism to retrieve the certificat ![PULL Scenarios](assets/certificate-pull.svg) -The Certificate PULL Diagram describes the process of Consumer retrieving a certificate from a Provider via an EDC. -It begins with the provider creating a Certificate Asset with corresponding contract definition in the EDC Catalog. -The Consumer searches the catalog using specific filters, initiates a contract negotiation, and retrieves the Endpoint Data Reference (EDR). -The Data Plane then facilitates secure data transfer, allowing the consumer to pull the certificate. +The Certificate PULL Diagram describes the process of a Consumer retrieving a certificate from a Provider via an EDC. +It begins with the consumer discovering the Certificate Management API asset in the EDC Catalog, initiating a contract negotiation, and retrieving the Endpoint Data Reference (EDR). +The Data Plane then facilitates secure data transfer, allowing the consumer to retrieve certificate data via the `GET /certificates/{certificateId}` endpoint. Once retrieved, the Backend Certificate Consumer processes the certificate and sends a feedback message to confirm the status. ##### 2.1.5.3 PUSH Notification followed by PULL mechanism -After the Certificate Provider has created a Certificate Asset with the corresponding contract definition in the EDC Catalog, the Certificate Provider sends a push notification with `status: CREATED` to the Certificate Consumer. +After a new certificate is available via the Certificate Management API, the Certificate Provider sends a push notification with `status: CREATED` to the Certificate Consumer. The Certificate Consumer uses the above described PULL mechanism to retrieve the certificate data using the `certificateId` provided in the notification. This reduces the Certificate Consumer's need for active checks for missing certificates or certificate updates and enables access to the latest certificate data. @@ -928,7 +867,7 @@ The internal reference id to request a certificate document. The entities **Certificate Provider** and **Certificate Consumer** are central to the certificate exchange mechanisms defined in this standard. **Certificate Provider**: A Certificate Provider is an entity that offers company certificates to other Catena-X participants. -The Certificate Provider is responsible for creating and maintaining certificate assets in their EDC catalog, responding to certificate requests, and optionally pushing certificates directly to Certificate Consumers or notifying them about certificate availability. +The Certificate Provider is responsible for offering the Certificate Management API as an EDC asset, managing certificates in their backend, responding to certificate requests, and optionally notifying Certificate Consumers about certificate lifecycle changes via push notifications. **Certificate Consumer**: A Certificate Consumer is an entity that requests, receives, and validates company certificates from Certificate Providers. The Certificate Consumer may actively request certificates, provide feedback on certificate status, and respond to availability notifications. @@ -937,11 +876,10 @@ The following table illustrates the entities in relation to the supported certif | Mechanism | Certificate Provider | Certificate Consumer | |-----------|----------------------|----------------------| -| **PULL** | Provider - Creates certificate assets in EDC catalog | Consumer - Discovers certificates in catalog, negotiates contract, retrieves certificate via EDC | -| **PUSH** | Consumer - Initiates connection to Certificate Consumer's CCMAPI, pushes certificate data directly | Provider - Offers CCMAPI as EDC asset, receives and processes pushed certificates | +| **PULL** | Provider - Offers CCMAPI as EDC asset, enabling consumers to search and retrieve certificates | Consumer - Discovers CCMAPI in catalog, negotiates contract, retrieves certificates via EDC | +| **PUSH** | Consumer - Initiates connection to Certificate Consumer's CCMAPI, sends lifecycle notification | Provider - Offers CCMAPI as EDC asset, receives lifecycle notification and retrieves certificate via pull | | **REQUEST** | Provider - Offers CCMAPI as EDC asset, processes incoming certificate requests | Consumer - Sends certificate request to Certificate Provider's CCMAPI | | **FEEDBACK** | Provider - Offers CCMAPI as EDC asset, receives and processes feedback messages | Consumer - Sends feedback (received, accepted, rejected) to Certificate Provider's CCMAPI | -| **AVAILABLE** | Consumer - Sends availability notification to Certificate Consumer's CCMAPI | Provider - Offers CCMAPI as EDC asset, receives notification about certificate availability | > **Note**: The roles of Provider and Consumer in the EDC context may differ from the business entities of Certificate Provider and Certificate Consumer, depending on the mechanism used. This is why the standard explicitly uses the terms Certificate Provider and Certificate Consumer to avoid ambiguity. From 602a2b9efbcfd0089c54f1bb095c94a3ae72c6bd Mon Sep 17 00:00:00 2001 From: Nico Koprowski Date: Tue, 12 May 2026 11:37:30 +0800 Subject: [PATCH 03/14] docs(0135): remove "push" path from certificate notification endpoints --- .../CX-0135-CompanyCertificateManagement.md | 4 ++-- .../assets/openapi-spec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md b/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md index 01d6a76a9c..2d8301f3fc 100644 --- a/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md +++ b/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md @@ -359,7 +359,7 @@ The error message is free text. The Certificate Provider notifies the Certificate Consumer about a lifecycle change for a certificate. The Certificate Consumer can use the `certificateId` to retrieve the certificate metadata and documents using the pull mechanism. -`POST /certificate-notification/push` +`POST /certificate-notification` ```json { @@ -619,7 +619,7 @@ Certificate Provider & Certificate Consumer: - Certificate Consumer **MAY** send a notification of acceptance or rejection via `POST /certificate-status`. Certificate Provider **MUST** respond according to the [error handling](#212-error-handling). -- Certificate Provider **MUST** send a push notification with `status: CREATED` via `POST /certificate-notification/push` after the referenced company certificate is exposed in their catalog. +- Certificate Provider **MUST** send a push notification with `status: CREATED` via `POST /certificate-notification` after the referenced company certificate is exposed in their catalog. Certificate Consumer **MUST** respond according to the [error handling](#212-error-handling) and **SHOULD** retrieve the new certificate via the pull mechanism. - Certificate Consumer **MAY** implement the [push endpoint](#2115-company-certificate-push) for the Certificate Provider to send lifecycle notifications to, but **MUST** set the correct access and usage policy on the offer, when choosing to do so. diff --git a/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml b/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml index 757d53a980..3b7895061a 100644 --- a/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml +++ b/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml @@ -627,7 +627,7 @@ paths: '500': description: Internal server error - /certificate-notification/push: + /certificate-notification: post: summary: Certificate provider notifies certificate consumer about a lifecycle change for a certificate tags: From 0fd4eea6f4ec1dbe99ac653f894fefaaba8d0d37 Mon Sep 17 00:00:00 2001 From: Nico Koprowski Date: Tue, 12 May 2026 13:03:25 +0800 Subject: [PATCH 04/14] docs(0135): change certificate status to certificate feedback --- .../CX-0135-CompanyCertificateManagement.md | 183 +++++++++++------- .../assets/openapi-spec.yaml | 82 +++++--- 2 files changed, 168 insertions(+), 97 deletions(-) diff --git a/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md b/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md index 2d8301f3fc..a6fc347146 100644 --- a/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md +++ b/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md @@ -401,9 +401,9 @@ where a data offer for an asset of type `cx-taxo:CCMAPI` **MUST** be available f ##### 2.1.1.6 Company Certificate Feedback -`POST /certificate-status` +`POST /certificate-feedback` -This API is used by the Certificate Consumer to provide feedback on the status to the Certificate Provider, either accepting or rejecting the provided certificate. +This API is used by the Certificate Consumer to provide feedback on the validation status to the Certificate Provider, either accepting or rejecting the provided certificate. This applies regardless of whether the certificate was [pulled](#2152-pull-mechanism) or [pushed](#2151-push-mechanism). ###### 2.1.1.6.1 Company Certificate Feedback: Received @@ -412,16 +412,27 @@ Certificate has been received by Certificate Consumer and validation is in progr ```json { - "certificateId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "certificateStatus": "RECEIVED", - "locationBpns": [ - "BPNS000000000001", - "BPNS000000000002", - "BPNS000000000003", - "BPNA000000000001", - "BPNA000000000002", - "BPNA000000000003" - ] + "header": { + "messageId": "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72", + "context": "CompanyCertificateManagement-CCMAPI-Status:1.0.0", + "sentDateTime": "2024-10-07T10:15:00Z", + "senderBpn": "BPNL0000000001AB", + "receiverBpn": "BPNL0000000002CD", + "version": "3.1.0", + "senderFeedbackUrl": "https://domain.tld/path/to/edc/api/v1/dsp" + }, + "content": { + "certificateId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "certificateStatus": "RECEIVED", + "locationBpns": [ + "BPNS000000000001", + "BPNS000000000002", + "BPNS000000000003", + "BPNA000000000001", + "BPNA000000000002", + "BPNA000000000003" + ] + } } ``` @@ -433,16 +444,27 @@ The `locationBpns` can be a mix of sites and addresses. ```json { - "certificateId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "certificateStatus": "ACCEPTED", - "locationBpns": [ - "BPNS000000000001", - "BPNS000000000002", - "BPNS000000000003", - "BPNA000000000001", - "BPNA000000000002", - "BPNA000000000003" - ] + "header": { + "messageId": "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72", + "context": "CompanyCertificateManagement-CCMAPI-Status:1.0.0", + "sentDateTime": "2024-10-07T10:15:00Z", + "senderBpn": "BPNL0000000001AB", + "receiverBpn": "BPNL0000000002CD", + "version": "3.1.0", + "senderFeedbackUrl": "https://domain.tld/path/to/edc/api/v1/dsp" + }, + "content": { + "certificateId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "certificateStatus": "ACCEPTED", + "locationBpns": [ + "BPNS000000000001", + "BPNS000000000002", + "BPNS000000000003", + "BPNA000000000001", + "BPNA000000000002", + "BPNA000000000003" + ] + } } ``` @@ -452,57 +474,68 @@ Certificate is rejected by the Certificate Consumer with one or multiple reasons ```json { - "certificateId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "certificateStatus": "REJECTED", - "certificateErrors": [ - { - "message": "We do not process certificates on Sunday" - }, - { - "message": "Certificate has expired in 2024" - }, - { - "message": "Certificate was revoked" - }, - { - "message": "Unexpected data format" - }, - { - "message": "Unexpected language expected English, received Mandarin" - }, - { - "message": "Expected PDF, received JPG" - }, - { - "message": "Unknown BPNL000000000000" - } - ], - "locationBpns": [ - "BPNS000000000001", - "BPNS000000000002", - "BPNS000000000003", - "BPNA000000000001", - "BPNA000000000002", - "BPNA000000000003" - ], - "locationErrors": [ - { - "bpn": "BPNS000000000002", - "locationErrors": [ - { - "message": "Site BPNS000000000002 has been Rejected" - } - ] - }, - { - "bpn": "BPNS000000000003", - "locationErrors": [ - { - "message": "Site BPNS000000000003 is missing" - } - ] - } - ] + "header": { + "messageId": "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72", + "context": "CompanyCertificateManagement-CCMAPI-Status:1.0.0", + "sentDateTime": "2024-10-07T10:15:00Z", + "senderBpn": "BPNL0000000001AB", + "receiverBpn": "BPNL0000000002CD", + "version": "3.1.0", + "senderFeedbackUrl": "https://domain.tld/path/to/edc/api/v1/dsp" + }, + "content": { + "certificateId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "certificateStatus": "REJECTED", + "certificateErrors": [ + { + "message": "We do not process certificates on Sunday" + }, + { + "message": "Certificate has expired in 2024" + }, + { + "message": "Certificate was revoked" + }, + { + "message": "Unexpected data format" + }, + { + "message": "Unexpected language expected English, received Mandarin" + }, + { + "message": "Expected PDF, received JPG" + }, + { + "message": "Unknown BPNL000000000000" + } + ], + "locationBpns": [ + "BPNS000000000001", + "BPNS000000000002", + "BPNS000000000003", + "BPNA000000000001", + "BPNA000000000002", + "BPNA000000000003" + ], + "locationErrors": [ + { + "bpn": "BPNS000000000002", + "locationErrors": [ + { + "message": "Site BPNS000000000002 has been Rejected" + } + ] + }, + { + "bpn": "BPNS000000000003", + "locationErrors": [ + { + "message": "Site BPNS000000000003 is missing" + } + ] + } + ] + } } ``` @@ -615,8 +648,8 @@ Certificate Provider & Certificate Consumer: - Certificate Consumer **MAY** implement the [push endpoint](#2115-company-certificate-push) for the Certificate Provider to send push notifications to, but **MUST** set the correct access and usage policy on the offer, when choosing to do so. - Certificate Consumer **MAY** send a certificate request via `POST /certificate-request` which **MUST** be replied to by the Certificate Provider according to the endpoint definitions. -- Certificate Consumer **MAY** send a notification of reception when the certificate validation has started via `POST /certificate-status`. -- Certificate Consumer **MAY** send a notification of acceptance or rejection via `POST /certificate-status`. +- Certificate Consumer **MAY** send a notification of reception when the certificate validation has started via `POST /certificate-feedback`. +- Certificate Consumer **MAY** send a notification of acceptance or rejection via `POST /certificate-feedback`. Certificate Provider **MUST** respond according to the [error handling](#212-error-handling). - Certificate Provider **MUST** send a push notification with `status: CREATED` via `POST /certificate-notification` after the referenced company certificate is exposed in their catalog. diff --git a/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml b/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml index 3b7895061a..f0702b82c4 100644 --- a/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml +++ b/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml @@ -181,6 +181,17 @@ components: content: $ref: '#/components/schemas/CertificatePushContent' + CertificateFeedback: + type: object + required: + - header + - content + properties: + header: + $ref: '#/components/schemas/FeedbackUrlHeader' + content: + $ref: '#/components/schemas/CertificateStatus' + CertificatePushContent: type: object required: @@ -460,33 +471,60 @@ components: CertificateReceivedStatus: summary: Consumer received certificate value: - certificateId: "3fa85f64-5717-4562-b3fc-2c963f66afa6" - certificateStatus: "RECEIVED" - locationBpns: - - "BPNS000000000003" + header: + messageId: "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72" + context: "CompanyCertificateManagement-CCMAPI-Status:1.0.0" + sentDateTime: "2024-10-07T10:15:00Z" + senderBpn: "BPNL0000000001AB" + receiverBpn: "BPNL0000000002CD" + version: "3.1.0" + senderFeedbackUrl: "https://domain.tld/path/to/edc/api/v1/dsp" + content: + certificateId: "3fa85f64-5717-4562-b3fc-2c963f66afa6" + certificateStatus: "RECEIVED" + locationBpns: + - "BPNS000000000003" CertificateAcceptedStatus: summary: Consumer accepts received certificate value: - certificateId: "3fa85f64-5717-4562-b3fc-2c963f66afa6" - certificateStatus: "ACCEPTED" - locationBpns: - - "BPNS000000000003" + header: + messageId: "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72" + context: "CompanyCertificateManagement-CCMAPI-Status:1.0.0" + sentDateTime: "2024-10-07T10:15:00Z" + senderBpn: "BPNL0000000001AB" + receiverBpn: "BPNL0000000002CD" + version: "3.1.0" + senderFeedbackUrl: "https://domain.tld/path/to/edc/api/v1/dsp" + content: + certificateId: "3fa85f64-5717-4562-b3fc-2c963f66afa6" + certificateStatus: "ACCEPTED" + locationBpns: + - "BPNS000000000003" CertificateRejectedStatus: summary: Consumer rejected received certificate value: - certificateId: "3fa85f64-5717-4562-b3fc-2c963f66afa6" - certificateStatus: "REJECTED" - locationBpns: - - "BPNS000000000003" - certificateErrors: - - message: "We do not process certificates on Sunday" - - message: "Can not provide certicate for requested locations" - locationErrors: - - bpn: "BPNS000000000003" - locationErrors: - - message: "Site BPNS000000000003 is unknown" + header: + messageId: "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72" + context: "CompanyCertificateManagement-CCMAPI-Status:1.0.0" + sentDateTime: "2024-10-07T10:15:00Z" + senderBpn: "BPNL0000000001AB" + receiverBpn: "BPNL0000000002CD" + version: "3.1.0" + senderFeedbackUrl: "https://domain.tld/path/to/edc/api/v1/dsp" + content: + certificateId: "3fa85f64-5717-4562-b3fc-2c963f66afa6" + certificateStatus: "REJECTED" + locationBpns: + - "BPNS000000000003" + certificateErrors: + - message: "We do not process certificates on Sunday" + - message: "Can not provide certificate for requested locations" + locationErrors: + - bpn: "BPNS000000000003" + locationErrors: + - message: "Site BPNS000000000003 is unknown" paths: /certificates/{certificateId}: @@ -646,9 +684,9 @@ paths: '501': description: The certificate consumer currently does not support processing of notifications - /certificate-status: + /certificate-feedback: post: - summary: Certificate consumer sends feedback on the status of a consumed certificate to the certificate provider + summary: Certificate consumer sends feedback on the validation status of a consumed certificate to the certificate provider tags: - Certificate Provider requestBody: @@ -656,7 +694,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CertificateStatus' + $ref: '#/components/schemas/CertificateFeedback' examples: Received: $ref: '#/components/examples/CertificateReceivedStatus' From 352a89eb3c2d5d6f19396d556394509b7d1c7681 Mon Sep 17 00:00:00 2001 From: Nico Koprowski Date: Tue, 12 May 2026 13:14:02 +0800 Subject: [PATCH 05/14] docs(0135): change mentions from notification API to management API --- .../CX-0135-CompanyCertificateManagement.md | 22 +++++++++---------- .../assets/openapi-spec.yaml | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md b/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md index a6fc347146..e12a512c53 100644 --- a/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md +++ b/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md @@ -569,7 +569,7 @@ The following section details how the Certificate Management API asset should be Please note the depicted examples show `@id` fields with random example UUIDs. Every dataspace participant may use their individual random uuid. -##### 2.1.4.1 Notification API +##### 2.1.4.1 Company Certificate Management API > *This section is normative* @@ -580,28 +580,28 @@ In turn, the Certificate Consumer **MAY** offer an asset to expose an API for th The property [[type]](http://purl.org/dc/terms/type) **MUST** reference the name of the certificate management API as defined in the Catena-X taxonomy published under [[taxonomy]](https://w3id.org/catenax/taxonomy). -| **Type** | **Subject** | **Version** | **Description** | -|----------------|-----------------------------------------------------|-------------|-----------------| -| cx-taxo:CCMAPI | cx-taxo:CompanyCertificateManagementNotificationApi | 3.0 | Offers *Certificate Management API* for [retrieving certificates](#2111-retrieve-certificate-metadata), [searching certificates](#2113-search-certificates), [requesting](#2114-company-certificate-request) and [pushing](#2115-company-certificate-push) certificates, as well as sending [feedback](#2116-company-certificate-feedback) on the status for provided certificates. | +| **Type** | **Subject** | **Version** | **Description** | +|----------------|-----------------------------------------|-------------|-----------------| +| cx-taxo:CCMAPI | cx-taxo:CompanyCertificateManagementApi | 3.0 | Offers *Certificate Management API* for [retrieving certificates](#2111-retrieve-certificate-metadata), [searching certificates](#2113-search-certificates), [requesting](#2114-company-certificate-request) and [pushing](#2115-company-certificate-push) certificates, as well as sending [feedback](#2116-company-certificate-feedback) on the status for provided certificates. | There **MUST** only be one unique asset per API (subject and version) across all connectors of one BPNL. *Example*: it is possible to have these assets available next to one-another: -- ```{ "dct:subject": { "@id": "cx-taxo:CompanyCertificateManagementNotificationApi" }, "cx-common:version": "3.0" }```, -- ```{ "dct:subject": { "@id": "cx-taxo:CompanyCertificateManagementNotificationApi" }, "cx-common:version": "2.0" }``` +- ```{ "dct:subject": { "@id": "cx-taxo:CompanyCertificateManagementApi" }, "cx-common:version": "3.0" }```, +- ```{ "dct:subject": { "@id": "cx-taxo:CompanyCertificateManagementApi" }, "cx-common:version": "2.0" }``` since they either differ in the value of the version or the subject. But it would not be possible to have two of the same subject and the same version. *Example that is not allowed:* -- ```{"dct:subject": { "@id": "cx-taxo:CompanyCertificateManagementNotificationApi" }, "cx-common:version": "3.0" }```, -- ```{"dct:subject": { "@id": "cx-taxo:CompanyCertificateManagementNotificationApi" }, "cx-common:version": "3.0" }``` +- ```{"dct:subject": { "@id": "cx-taxo:CompanyCertificateManagementApi" }, "cx-common:version": "3.0" }```, +- ```{"dct:subject": { "@id": "cx-taxo:CompanyCertificateManagementApi" }, "cx-common:version": "3.0" }``` It doesn't matter if the assets are offered in one or in different connectors, as long as they belong to the same BPNL this is not allowed. -**Example Certificate Notification API** +**Example Company Certificate Management API Asset** ```json { @@ -612,7 +612,7 @@ It doesn't matter if the assets are offered in one or in different connectors, a "@id": "cx-taxo:CCMAPI" }, "dct:subject": { - "@id": "cx-taxo:CompanyCertificateManagementNotificationApi" + "@id": "cx-taxo:CompanyCertificateManagementApi" }, "dct:description": "Offers Certificate Management API for retrieving, searching, requesting and pushing certificates, as well as sending feedback on the status for provided certificates.", "cx-common:version": "3.0" @@ -666,7 +666,7 @@ Business Application Provider: ![PUSH Scenarios](assets/certificate-push.svg) The Certificate PUSH Diagram describes the lifecycle notification flow from a Backend Certificate Provider to a Backend Certificate Consumer via EDC (Eclipse Data Connector) components. -The process starts with a contract agreement for a Notification Asset, followed by the provider sending a push notification (containing the `certificateId` and a `status` of CREATED, MODIFIED, or DELETED) to the Consumer's endpoint. +The process starts with a contract agreement for a Certificate Management API asset, followed by the provider sending a push notification (containing the `certificateId` and a `status` of CREATED, MODIFIED, or DELETED) to the Consumer's endpoint. The Certificate Consumer then uses the pull mechanism to retrieve the certificate data, and finalizes the workflow by generating a feedback message sent to the provider. ##### 2.1.5.2 PULL Mechanism diff --git a/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml b/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml index f0702b82c4..5078c9e34c 100644 --- a/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml +++ b/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml @@ -1,7 +1,7 @@ openapi: 3.1.0 info: title: Company Certificate Notification API - description: APIs for requesting and accepting company certificates. Note that the Certificate Provider APIs and the Certificate Consumer APIs are available via different EDC assets. + description: APIs for requesting and accepting company certificates version: 1.0.0 tags: - name: Certificate Provider From 8900eca74cce7cc5ecc3fb52609b7265f05120c8 Mon Sep 17 00:00:00 2001 From: Nico Koprowski Date: Wed, 13 May 2026 10:24:50 +0800 Subject: [PATCH 06/14] docs(0135): add language code to schema metadata --- .../assets/openapi-spec.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml b/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml index 5078c9e34c..7b3d6020a5 100644 --- a/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml +++ b/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml @@ -393,6 +393,12 @@ components: description: The document ID as stored by the data service provider examples: - "doc-3fa85f64-5717-4562-b3fc-2c963f66afa6" + language: + type: string + description: The language of the certificate document as an ISO 639-1 two-letter language code (e.g. "en" for English, "de" for German) + pattern: "^[a-z]{2}$" + examples: + - "en" CertificateSearchRequest: type: object From 0409421847d1011da4afb22e2a22fd57648a136f Mon Sep 17 00:00:00 2001 From: Nico Koprowski Date: Wed, 13 May 2026 10:28:23 +0800 Subject: [PATCH 07/14] docs(0135): add "UNDER_CERTIFICATION" as request status response --- .../assets/openapi-spec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml b/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml index 7b3d6020a5..2598b06c21 100644 --- a/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml +++ b/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml @@ -124,7 +124,7 @@ components: requestStatus: type: string description: The processing status in which the certificate request process is currently in - const: "IN_PROGRESS" + enum: [IN_PROGRESS, UNDER_CERTIFICATION] CertificateRequestCompletedResponse: type: object From ca14b0454a48b016579e9c4caafc42c8a7a6f79c Mon Sep 17 00:00:00 2001 From: Nico Koprowski Date: Wed, 13 May 2026 10:41:12 +0800 Subject: [PATCH 08/14] docs(0135): remove redundant API specifications in the standardization document --- .../CX-0135-CompanyCertificateManagement.md | 369 +----------------- .../assets/openapi-spec.yaml | 10 + 2 files changed, 14 insertions(+), 365 deletions(-) diff --git a/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md b/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md index e12a512c53..de70db2e1c 100644 --- a/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md +++ b/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md @@ -133,134 +133,20 @@ The Certificate Consumer retrieves the metadata of a certificate by its ID from `GET /certificates/{certificateId}` -###### 2.1.1.1.1 HTTP Response Codes - -| HTTP Code | Description | -|-----------|--------------------------------| -| 200 | Certificate metadata returned. | -| 404 | Certificate not found. | -| 500 | Internal Server Error. | - -###### 2.1.1.1.2 HTTP Response Body for HTTP Code 200 - -The response contains the full certificate metadata including all associated document IDs. -Use `GET /certificates/{certificateId}/documents/{documentId}` to retrieve each document as a binary PDF. - -```json -{ - "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", - "certifiedBpn": "BPNL0000000002CD", - "type": { - "certificateType": "ISO9001", - "certificateVersion": "2015" - }, - "registrationNumber": "12 198 54182 TMS", - "areaOfApplication": "Development, Marketing and Sales", - "locations": [ - { - "locationBpn": "BPNS000000000002", - "areaOfApplication": "Development, Marketing and Sales" - } - ], - "validFrom": "2023-01-25", - "validUntil": "2026-01-24", - "issuer": { - "issuerName": "TUEV Sued", - "issuerBpn": "BPNL0000000001AB" - }, - "trustLevel": "high", - "validator": { - "validatorName": "Data service provider X", - "validatorBpn": "BPNL0000000003EF" - }, - "uploader": "BPNL0000000001AB", - "documents": [ - "doc-3fa85f64-5717-4562-b3fc-2c963f66afa6" - ] -} -``` - ##### 2.1.1.2 Retrieve Certificate Document The Certificate Consumer retrieves a specific certificate document by its ID. The document is returned as a binary PDF. -Document IDs are listed in the `documents` array of the certificate metadata (see [2.1.1.1 Retrieve Certificate Metadata](#2111-retrieve-certificate-metadata)). +Document IDs are listed in the `documents` array of the certificate metadata. `GET /certificates/{certificateId}/documents/{documentId}` -###### 2.1.1.2.1 HTTP Response Codes - -| HTTP Code | Description | -|-----------|--------------------------------------| -| 200 | Binary PDF document returned. | -| 404 | Certificate or document not found. | -| 500 | Internal Server Error. | - ##### 2.1.1.3 Search Certificates The Certificate Consumer searches for certificates using BPN filters. -All filter fields are optional and combined with AND logic. -Omitting all filters returns all accessible certificates. `POST /certificate-search` -Query parameters: - -| Parameter | Description | Default | -|------------|--------------------------------------|---------| -| `page` | Zero-based page number | 0 | -| `pageSize` | Number of results per page (max 100) | 10 | - -```json -{ - "legalEntityBpns": [ - "BPNL0000000002CD" - ], - "siteBpns": [ - "BPNS000000000002" - ], - "addressBpns": [ - "BPNA000000000001" - ] -} -``` - -###### 2.1.1.3.1 HTTP Response Codes - -| HTTP Code | Description | -|-----------|---------------------------------------------| -| 200 | Paginated list of matching certificates. | -| 400 | Request malformed and can not be processed. | -| 500 | Internal Server Error. | - -###### 2.1.1.3.2 HTTP Response Body for HTTP Code 200 - -```json -{ - "totalElements": 42, - "totalPages": 5, - "page": 0, - "pageSize": 10, - "content": [ - { - "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", - "certifiedBpn": "BPNL0000000002CD", - "type": { - "certificateType": "ISO9001", - "certificateVersion": "2015" - }, - "registrationNumber": "12 198 54182 TMS", - "validFrom": "2023-01-25", - "validUntil": "2026-01-24", - "trustLevel": "high", - "documents": [ - "doc-3fa85f64-5717-4562-b3fc-2c963f66afa6" - ] - } - ] -} -``` - ##### 2.1.1.4 Company Certificate Request The Certificate Consumer requests a specific certificate from the Certificate Provider. @@ -269,91 +155,15 @@ The Certificate Consumer requests a specific certificate from the Certificate Pr `POST /certificate-request` -```json -{ - "certifiedBpn": "BPNL00000003AYRE", - "certificateType": "iso9001", - "locationBpns": [ - "BPNA000000000001", - "BPNA000000000002", - "BPNS000000000003" - ] -} -``` - > **`locationBpns` explanation**: > When a certificate is requested for multiple locations specified in `locationBpns`, the returned certificate's `enclosedSites` attribute may not cover all requested locations. > However, it should include at least one of the specified locations. -###### 2.1.1.4.1 HTTP Response Codes - -| HTTP Code | Description | -|-----------|---------------------------------------------------------------------------| -| 200 | OK. Certificate request processing completed (detailed response in body). | -| 202 | Certificate request accepted and in processing. | -| 400 | Request malformed. | -| 500 | Internal Server Error. | - > **EDC Behavior**: > At the moment (standard release 25.09), the open-source EDC will always proxy a `500` internal server error when it encounters a `4xx` or `5xx` HTTP response code from the API. > This means that in the case of a malformed request, while the API should return a `400` status code, the final EDC response that the consumer receives will be `500`. > Until a future EDC update changes this behavior to proxy all status codes without changes, applications will need to be able to deal with this technical reality. -The detailed response bodies are described in 2.1.1.4.2 and following. -HTTP Status Codes 202, 400 and 500 do not come with a response body. - -###### 2.1.1.4.2 HTTP Response Body for HTTP Code 202 - -Case: Certificate Request Accepted and In Processing. - -```json -{ - "requestStatus": "IN_PROGRESS" -} -``` - -###### 2.1.1.4.3 HTTP Response Body for HTTP Code 200, Status: COMPLETED - -Finished Processing and Certificate available. -The `certificateId` can be used to retrieve the certificate via `GET /certificates/{certificateId}`. - -```json -{ - "requestStatus": "COMPLETED", - "certificateId": "3b4edc05-e214-47a1-b0c2-1d831cdd9ba0" -} -``` - -###### 2.1.1.4.4 HTTP Response Body for HTTP Code 200, Status: REJECTED - -Finished Processing and Certificate Request Rejected. -The request errors and location errors SHOULD contain all encountered problems in detail. -The error message is free text. - -```json -{ - "requestStatus": "REJECTED", - "requestErrors": [ - { - "message": "We do not process certificates on Sunday" - }, - { - "message": "Can not provide certificate for requested locations" - } - ], - "locationErrors": [ - { - "bpn": "BPNS000000000003", - "locationErrors": [ - { - "message": "Site BPNS000000000003 is unknown" - } - ] - } - ] -} -``` - ##### 2.1.1.5 Company Certificate Push The Certificate Provider notifies the Certificate Consumer about a lifecycle change for a certificate. @@ -361,190 +171,19 @@ The Certificate Consumer can use the `certificateId` to retrieve the certificate `POST /certificate-notification` -```json -{ - "header": { - "messageId": "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72", - "context": "CompanyCertificateManagement-CCMAPI-Push:1.0.0", - "sentDateTime": "2024-10-07T10:15:00Z", - "senderBpn": "BPNL0000000001AB", - "receiverBpn": "BPNL0000000002CD", - "version": "3.1.0", - "senderFeedbackUrl": "https://domain.tld/path/to/edc/api/v1/dsp" - }, - "content": { - "certificateId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "status": "CREATED" - } -} -``` - -The `status` field indicates the type of lifecycle change: - -| Status | Description | -|----------|--------------------------------------------------------------------| -| CREATED | A new certificate has been created and is available for retrieval. | -| MODIFIED | An existing certificate has been updated. | -| DELETED | A certificate has been removed and is no longer available. | - The `senderFeedbackUrl` specifies where the Certificate Provider expects feedback on the certificate status from the Certificate Consumer. The expected value **MUST** be a concrete path to the version 1 dataspace protocol endpoint, where a data offer for an asset of type `cx-taxo:CCMAPI` **MUST** be available for the Certificate Consumer. -###### 2.1.1.5.1 HTTP Response Codes - -| HTTP Code | Description | -|-----------|---------------------------------------------------------------------------------------| -| 200 | Notification processed successfully. | -| 500 | Internal Server Error. | -| 501 | The Certificate Consumer currently does not support processing of push notifications. | - ##### 2.1.1.6 Company Certificate Feedback -`POST /certificate-feedback` - -This API is used by the Certificate Consumer to provide feedback on the validation status to the Certificate Provider, either accepting or rejecting the provided certificate. +The Certificate Consumer sends feedback on the validation status to the Certificate Provider, either accepting or rejecting the provided certificate. This applies regardless of whether the certificate was [pulled](#2152-pull-mechanism) or [pushed](#2151-push-mechanism). -###### 2.1.1.6.1 Company Certificate Feedback: Received - -Certificate has been received by Certificate Consumer and validation is in progress. - -```json -{ - "header": { - "messageId": "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72", - "context": "CompanyCertificateManagement-CCMAPI-Status:1.0.0", - "sentDateTime": "2024-10-07T10:15:00Z", - "senderBpn": "BPNL0000000001AB", - "receiverBpn": "BPNL0000000002CD", - "version": "3.1.0", - "senderFeedbackUrl": "https://domain.tld/path/to/edc/api/v1/dsp" - }, - "content": { - "certificateId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "certificateStatus": "RECEIVED", - "locationBpns": [ - "BPNS000000000001", - "BPNS000000000002", - "BPNS000000000003", - "BPNA000000000001", - "BPNA000000000002", - "BPNA000000000003" - ] - } -} -``` - -###### 2.1.1.6.2 Company Certificate Feedback: Accepted +`POST /certificate-feedback` -Certificate is accepted. The `certificateId` **MUST** match the `certificateId` communicated by the Certificate Provider. -The `locationBpns` can be a mix of sites and addresses. - -```json -{ - "header": { - "messageId": "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72", - "context": "CompanyCertificateManagement-CCMAPI-Status:1.0.0", - "sentDateTime": "2024-10-07T10:15:00Z", - "senderBpn": "BPNL0000000001AB", - "receiverBpn": "BPNL0000000002CD", - "version": "3.1.0", - "senderFeedbackUrl": "https://domain.tld/path/to/edc/api/v1/dsp" - }, - "content": { - "certificateId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "certificateStatus": "ACCEPTED", - "locationBpns": [ - "BPNS000000000001", - "BPNS000000000002", - "BPNS000000000003", - "BPNA000000000001", - "BPNA000000000002", - "BPNA000000000003" - ] - } -} -``` - -###### 2.1.1.6.3 Company Certificate Feedback: Rejected - -Certificate is rejected by the Certificate Consumer with one or multiple reasons. - -```json -{ - "header": { - "messageId": "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72", - "context": "CompanyCertificateManagement-CCMAPI-Status:1.0.0", - "sentDateTime": "2024-10-07T10:15:00Z", - "senderBpn": "BPNL0000000001AB", - "receiverBpn": "BPNL0000000002CD", - "version": "3.1.0", - "senderFeedbackUrl": "https://domain.tld/path/to/edc/api/v1/dsp" - }, - "content": { - "certificateId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", - "certificateStatus": "REJECTED", - "certificateErrors": [ - { - "message": "We do not process certificates on Sunday" - }, - { - "message": "Certificate has expired in 2024" - }, - { - "message": "Certificate was revoked" - }, - { - "message": "Unexpected data format" - }, - { - "message": "Unexpected language expected English, received Mandarin" - }, - { - "message": "Expected PDF, received JPG" - }, - { - "message": "Unknown BPNL000000000000" - } - ], - "locationBpns": [ - "BPNS000000000001", - "BPNS000000000002", - "BPNS000000000003", - "BPNA000000000001", - "BPNA000000000002", - "BPNA000000000003" - ], - "locationErrors": [ - { - "bpn": "BPNS000000000002", - "locationErrors": [ - { - "message": "Site BPNS000000000002 has been Rejected" - } - ] - }, - { - "bpn": "BPNS000000000003", - "locationErrors": [ - { - "message": "Site BPNS000000000003 is missing" - } - ] - } - ] - } -} -``` - -###### 2.1.1.6.4 HTTP Response Codes - -| HTTP Code | Description | -|-----------|------------------------| -| 200 | Status updated. | -| 500 | Internal Server Error. | +The `locationBpns` can be a mix of BPNS sites and BPNA addresses. #### 2.1.2 ERROR HANDLING diff --git a/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml b/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml index 2598b06c21..329bcd4e9c 100644 --- a/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml +++ b/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml @@ -203,6 +203,11 @@ components: format: uuid status: type: string + description: | + The type of lifecycle change for the certificate: + - `CREATED`: A new certificate has been created and is available for retrieval. + - `MODIFIED`: An existing certificate has been updated. + - `DELETED`: A certificate has been removed and is no longer available. enum: [CREATED, MODIFIED, DELETED] Error: @@ -247,6 +252,11 @@ components: format: uuid certificateStatus: type: string + description: | + The validation status reported by the Certificate Consumer: + - `RECEIVED`: Certificate has been received and validation is in progress. + - `ACCEPTED`: Certificate has been accepted by the Certificate Consumer. + - `REJECTED`: Certificate has been rejected by the Certificate Consumer. enum: [ACCEPTED, REJECTED, RECEIVED] certificateErrors: type: array From aef91b7cebbc64fcea8973cd66f154fa9c8c693f Mon Sep 17 00:00:00 2001 From: Nico Koprowski Date: Wed, 13 May 2026 10:48:22 +0800 Subject: [PATCH 09/14] docs(0135): add API schema diagram --- .../CX-0135-CompanyCertificateManagement.md | 166 ++++++++++++++++++ 1 file changed, 166 insertions(+) diff --git a/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md b/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md index de70db2e1c..a9fbf067ab 100644 --- a/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md +++ b/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md @@ -115,6 +115,172 @@ Use case to notify about certificate lifecycle changes (creation, modification, This section introduces the certificate management API which is further detailed in the corresponding [OpenAPI specification](assets/openapi-spec.yaml). +The following class diagram provides an overview of the API schema model: + +```mermaid +classDiagram + namespace Certificate_Data_Model { + class CertificateRetrievalMetadata { + +string certificateId + +string certifiedBpn + +string registrationNumber + +string areaOfApplication + +date validFrom + +date validUntil + +TrustLevel trustLevel + +string uploader + +string[] documents + +string language + } + class CertificateType { + +string certificateType + +string certificateVersion + } + class LocationBpn { + +string locationBpn + +string areaOfApplication + } + class CertificateIssuer { + +string issuerName + +string issuerBpn + } + class CertificateValidator { + +string validatorName + +string validatorBpn + } + class TrustLevel { + <> + none + low + high + trusted + } + } + + namespace Search { + class CertificateSearchRequest { + +string[] legalEntityBpns + +string[] siteBpns + +string[] addressBpns + } + class PaginatedCertificateList { + +int totalElements + +int totalPages + +int page + +int pageSize + } + } + + namespace Certificate_Request { + class CertificateRequest { + +string certifiedBpn + +string certificateType + +string[] locationBpns + } + class CertificateRequestFinishedResponse { + <> + } + class CertificateRequestInProgressResponse { + +RequestStatus requestStatus + } + class CertificateRequestCompletedResponse { + +string requestStatus + +string certificateId + } + class CertificateRequestRejectedResponse { + +string requestStatus + } + class RequestStatus { + <> + IN_PROGRESS + UNDER_CERTIFICATION + } + } + + namespace Push_and_Feedback { + class Header { + +string messageId + +string context + +datetime sentDateTime + +string senderBpn + +string receiverBpn + +string relatedMessageId + +string version + } + class FeedbackUrlHeader { + +string senderFeedbackUrl + } + class CertificatePush + class CertificateFeedback + class CertificatePushContent { + +string certificateId + +PushStatus status + } + class CertificateStatus { + +string certificateId + +FeedbackStatus certificateStatus + +string[] locationBpns + } + class PushStatus { + <> + CREATED + MODIFIED + DELETED + } + class FeedbackStatus { + <> + RECEIVED + ACCEPTED + REJECTED + } + } + + namespace Errors { + class Error { + +string message + } + class LocationErrorCollection { + +string bpn + } + class LocationError { + +string message + } + } + + %% Inheritance + FeedbackUrlHeader --|> Header + + %% Certificate Data Model + CertificateRetrievalMetadata "1" *-- "1" CertificateType : type + CertificateRetrievalMetadata "1" *-- "0..*" LocationBpn : locations + CertificateRetrievalMetadata "1" *-- "0..1" CertificateIssuer : issuer + CertificateRetrievalMetadata "1" *-- "0..1" CertificateValidator : validator + CertificateRetrievalMetadata --> TrustLevel + + %% Search + PaginatedCertificateList "1" *-- "0..*" CertificateRetrievalMetadata : content + + %% Certificate Request + CertificateRequestFinishedResponse <|-- CertificateRequestCompletedResponse + CertificateRequestFinishedResponse <|-- CertificateRequestRejectedResponse + CertificateRequestInProgressResponse --> RequestStatus + CertificateRequestRejectedResponse "1" *-- "1..*" Error : requestErrors + CertificateRequestRejectedResponse "1" *-- "0..*" LocationErrorCollection : locationErrors + + %% Push and Feedback messages + CertificatePush "1" *-- "1" FeedbackUrlHeader : header + CertificatePush "1" *-- "1" CertificatePushContent : content + CertificateFeedback "1" *-- "1" FeedbackUrlHeader : header + CertificateFeedback "1" *-- "1" CertificateStatus : content + CertificatePushContent --> PushStatus + CertificateStatus --> FeedbackStatus + CertificateStatus "1" *-- "0..*" Error : certificateErrors + CertificateStatus "1" *-- "0..*" LocationErrorCollection : locationErrors + + %% Errors + LocationErrorCollection "1" *-- "0..*" LocationError : locationErrors +``` + #### 2.1.1 API endpoints and resources > [!WARNING] From 7d0261e2c489547d8414477576223a6ccb1ab04e Mon Sep 17 00:00:00 2001 From: Nico Koprowski Date: Wed, 3 Jun 2026 11:33:24 +0800 Subject: [PATCH 10/14] docs(0135): add document metadata schema with language and media type referenced by certificate metadata --- .../assets/openapi-spec.yaml | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml b/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml index 329bcd4e9c..0351dd3e8b 100644 --- a/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml +++ b/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml @@ -334,6 +334,29 @@ components: examples: - "BPNL0000000003EF" + CertificateDocument: + type: object + required: + - documentId + properties: + documentId: + type: string + description: The document ID as stored by the data service provider + examples: + - "doc-3fa85f64-5717-4562-b3fc-2c963f66afa6" + language: + type: string + description: The language of the document as an ISO 639-1 two-letter language code (e.g. "en" for English, "de" for German) + pattern: "^[a-z]{2}$" + examples: + - "en" + mediaType: + type: string + description: The IANA media type of the document + examples: + - "application/pdf" + - "image/png" + CertificateRetrievalMetadata: type: object required: @@ -397,18 +420,9 @@ components: - "BPNL0000000001AB" documents: type: array - description: IDs of one or more documents associated with the certificate (e.g. main certificate and annexes). Use GET /certificates/{certificateId}/documents/{documentId} to retrieve each document. + description: One or more documents associated with the certificate (e.g. main certificate and annexes). Use GET /certificates/{certificateId}/documents/{documentId} to retrieve each document. items: - type: string - description: The document ID as stored by the data service provider - examples: - - "doc-3fa85f64-5717-4562-b3fc-2c963f66afa6" - language: - type: string - description: The language of the certificate document as an ISO 639-1 two-letter language code (e.g. "en" for English, "de" for German) - pattern: "^[a-z]{2}$" - examples: - - "en" + $ref: '#/components/schemas/CertificateDocument' CertificateSearchRequest: type: object From 3a8385a03be66a32d50e888ed1dd72528822ce05 Mon Sep 17 00:00:00 2001 From: Nico Koprowski Date: Wed, 3 Jun 2026 11:38:32 +0800 Subject: [PATCH 11/14] docs(0135): update API spec overview in standards document --- .../CX-0135-CompanyCertificateManagement.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md b/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md index a9fbf067ab..00f5606e5c 100644 --- a/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md +++ b/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md @@ -129,8 +129,11 @@ classDiagram +date validUntil +TrustLevel trustLevel +string uploader - +string[] documents + } + class CertificateDocument { + +string documentId +string language + +string mediaType } class CertificateType { +string certificateType @@ -255,6 +258,7 @@ classDiagram CertificateRetrievalMetadata "1" *-- "0..*" LocationBpn : locations CertificateRetrievalMetadata "1" *-- "0..1" CertificateIssuer : issuer CertificateRetrievalMetadata "1" *-- "0..1" CertificateValidator : validator + CertificateRetrievalMetadata "1" *-- "0..*" CertificateDocument : documents CertificateRetrievalMetadata --> TrustLevel %% Search From 08a74f9c39378a55d6db28da5908083d67bbcfd4 Mon Sep 17 00:00:00 2001 From: Nico Koprowski Date: Wed, 3 Jun 2026 11:47:07 +0800 Subject: [PATCH 12/14] docs(0135): fix lint issues --- .../CX-0135-CompanyCertificateManagement.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md b/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md index 00f5606e5c..d76a33ea0f 100644 --- a/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md +++ b/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement.md @@ -296,7 +296,6 @@ classDiagram > A future change is required in that regard, especially when considering the deprecation of the v1 DSP endpoint in favor of an upcoming EDC `.well-known` endpoint that supports multiple DSP versions. > This attribute will be deprecated in future releases and it will no longer be possible to use it for specifying the endpoint to receive feedback on. - ##### 2.1.1.1 Retrieve Certificate Metadata The Certificate Consumer retrieves the metadata of a certificate by its ID from the Certificate Provider. @@ -446,7 +445,6 @@ It doesn't matter if the assets are offered in one or in different connectors, a > The **API assets** are identified by the combination of `dct:subject` and `cx-common:version`. > When searching the EDC catalog for a specific API, make use of those properties in the catalog filter. - #### 2.1.5 MESSAGE FLOW EXPECTATIONS Certificate Provider & Certificate Consumer: From 3ea8ea5a65c2d11319fb12a7ac90bada5374152c Mon Sep 17 00:00:00 2001 From: Jim Marino Date: Sun, 14 Jun 2026 10:02:19 +0200 Subject: [PATCH 13/14] feat: add CloudEvents CCM proposal merged with PR #318 (combined + merge notes) --- .../CX-0000-CloudEventsFoundation-combined.md | 362 ++++ .../CX-0000-CloudEventsFoundation-jim.md | 362 ++++ ...nyCertificateManagement-CloudEvents-jim.md | 1279 +++++++++++++ ...5-CompanyCertificateManagement-combined.md | 1582 +++++++++++++++++ ...ompanyCertificateManagement-merge-notes.md | 240 +++ ...te-consumer-notification-api-combined.yaml | 561 ++++++ ...ificate-consumer-notification-api-jim.yaml | 559 ++++++ .../certificate-provider-api-combined.yaml | 965 ++++++++++ .../certificate-provider-api-jim.yaml | 825 +++++++++ 9 files changed, 6735 insertions(+) create mode 100644 docs/standards/CX-0135-CompanyCertificateManagement/CX-0000-CloudEventsFoundation-combined.md create mode 100644 docs/standards/CX-0135-CompanyCertificateManagement/CX-0000-CloudEventsFoundation-jim.md create mode 100644 docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement-CloudEvents-jim.md create mode 100644 docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement-combined.md create mode 100644 docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement-merge-notes.md create mode 100644 docs/standards/CX-0135-CompanyCertificateManagement/certificate-consumer-notification-api-combined.yaml create mode 100644 docs/standards/CX-0135-CompanyCertificateManagement/certificate-consumer-notification-api-jim.yaml create mode 100644 docs/standards/CX-0135-CompanyCertificateManagement/certificate-provider-api-combined.yaml create mode 100644 docs/standards/CX-0135-CompanyCertificateManagement/certificate-provider-api-jim.yaml diff --git a/docs/standards/CX-0135-CompanyCertificateManagement/CX-0000-CloudEventsFoundation-combined.md b/docs/standards/CX-0135-CompanyCertificateManagement/CX-0000-CloudEventsFoundation-combined.md new file mode 100644 index 0000000000..bacd494d66 --- /dev/null +++ b/docs/standards/CX-0135-CompanyCertificateManagement/CX-0000-CloudEventsFoundation-combined.md @@ -0,0 +1,362 @@ +# CX-0000 Notification Foundation (CX-151?) + +## Abstract + +This standard defines a protocol-agnostic foundation for event-based data exchange built on the CloudEvents +specification. It provides the base event structure and mechanisms that can be used across multiple Catena-X use cases +and domains. + +## 1 Introduction + +This standard establishes the foundational layer for event-based communication, based on the CloudEvents v1+ +specification. Note that implementations should periodically upgrade support for minor (backward-compatible) versions of +that specification. It defines core event structures, attribute requirements, and extension mechanisms without binding +to specific transport protocols. + +### 1.1 Audience & Scope + +This specification covers: + +- Base event structure and attributes +- Type system and data encoding +- Extension mechanisms +- Protocol-agnostic design principles + +### 1.2 Conformance and Proof of Conformity + +> *This section is normative* + +The keywords **MAY**, **MUST**, **MUST NOT**, **OPTIONAL**, **RECOMMENDED**, **REQUIRED**, **SHOULD**, and **SHOULD NOT** +in this document are to be interpreted as described in BCP 14 [RFC2119](#rfc2119) [RFC8174](#rfc8174) when, and only +when, they appear in all capitals, as shown here. + +To prove conformity: + +- Events **MUST** conform to the CloudEvents v1+ specification +- Events **MUST** be serializable in JSON format +- Events **MUST** include all REQUIRED attributes as defined in section 2.1 +- JSON schemas for event formats **MUST** be provided for validation + +### 1.3 Terminology + +> *This section is non-normative* + +**Event**: A data record expressing an occurrence and its context + +**Producer**: The system or process that creates events + +**Consumer**: The system or process that receives and processes events + +**Source**: An identifier for the context in which an event occurred + +**Type**: A string describing the nature of the event + +**CloudEvents**: The CNCF specification for describing events in a common format + +## 2 Event Specification + +> *This section is normative* + +### 2.1 Base Event Structure + +All events conforming to this standard **MUST** be valid CloudEvents v1.0.2 events [CloudEvents](#cloudevents). + +#### 2.1.1 Required Attributes + +The following CloudEvents attributes are **REQUIRED**: + +| Attribute | Type | Description | +|---------------|---------------|---------------------------------------------------------------------------------------------------------------| +| `id` | String | Unique identifier for the event. Producers MUST ensure that `source` + `id` is unique for each distinct event | +| `source` | URI-reference | The source DID | +| `specversion` | String | CloudEvents specification version. MUST be "1.0" | +| `type` | String | Event type identifier. MUST be a non-empty string. SHOULD use reverse-DNS notation | + +#### 2.1.2 Required Extension Attributes + +| Attribute | Type | Description | +|-------------|--------|----------------| +| `sourcebpn` | String | The source BPN | + +#### 2.1.3 Recommended Attributes + +The following CloudEvents attributes are **RECOMMENDED**: + +| Attribute | Type | Description | +|-------------------|-----------|-----------------------------------------------------------| +| `time` | Timestamp | [RFC3339](#rfc3339) timestamp of when the event occurred | +| `datacontenttype` | String | Media type of the data payload (e.g., "application/json") | +| `dataschema` | URI | URI identifying the schema of the data payload | + +If `datacontenttype` is not specified, the event **MUST** be interpreted as `application/json`. + +#### 2.1.4 Optional Attributes + +| Attribute | Type | Description | +|--------------------|--------|---------------------------------------------------------| +| `subject` | String | Subject of the event in the context of the event source | +| `relatedmessageid` | String | A related message id | + +### 2.2 Identifier Requirements + +Event IDs **MUST** be unique within the scope of a `source`: + +- Combination of `source` + `id` **MUST** uniquely identify an event +- Duplicate events (e.g., retries) **MUST** reuse the same `source` and `id` +- Event IDs **MUST** be UUIDs as defined by [RFC9562](#rfc9562) + +> NOTE: Consumers **MUST NOT** assume which UUID version a given `id` uses. The `id` is to be treated as an opaque +> identifier and used only for equality comparison; its internal structure (version, timestamp, or any other embedded +> data) **MUST NOT** be interpreted. + +### 2.3 Event Types + +#### 2.3.1 Type Naming Convention + +> ISSUE: We should consider using the `org.tractus-x` domain instead of `org.catena-x` for event types. This may +> make the event types and specification more generic for reuse across other dataspaces. + +Event types **MUST** follow reverse-DNS notation where the domain is `org.catena-x`: + +`...` + +The `domain` and `use case` **MUST** be in lowercase. + +The `specifier` **MUST** be in Camel Case, for example: + +``` +org.catena-x.pcf.NotificationEvent.v1 +``` + +#### 2.3.2 Type Versioning + +Version information **MUST** be encoded as follows: + +`v` + +for example `v1`. Minor/patch versions **MUST NOT** be in the type name + +### 2.4 Event Data + +#### 2.4.1 Data Payload + +The `data` attribute **MAY** contain domain-specific information of a valid JSON type (object, array, string, number, +boolean, null). The `data` attribute **SHOULD** have a schema defined via the `dataschema` attribute. + +#### 2.4.2 Data Content Type + +When `data` is present, `datacontenttype` **SHOULD** be specified. If specified, it **MUST** be a valid media type as +defined by [RFC2046](#rfc2046). If `data` is present and `datacontenttype` is not specified, implementation **MUST** default to +interpreting the data payload as "application/json". If `data_base64` is present and `datacontenttype` is not specified, +implementation **MUST** default to interpreting the data payload as "application/octet-stream". + +### 2.5 Extension Attributes + +#### 2.5.1 Defining Extensions + +Custom attributes **MAY** be added following CloudEvents naming rules: + +- The attribute **MUST** consist of lowercase letters (a-z) or digits (0–9) +- The attribute **MUST** be at least one character, **SHOULD NOT** exceed 20 characters +- The attribute **SHOULD** start with a letter +- The attribute **MUST NOT** use the name `data`, `data_base64`, or any CloudEvents reserved attribute names + +#### 2.5.2 Extension Attribute Types + +Extension attributes **MUST** use CloudEvents type system: + +- Boolean +- Integer +- String +- Binary +- URI +- URI-reference +- Timestamp + +## 3 Data Formats + +> *This section is normative* + +### 3.1 JSON Format + +All implementations **MUST** support JSON serialization per the CloudEvents JSON format specification +[CloudEvents-JSON](#cloudevents-json). + +#### 3.2 JSON Schema + +JSON Schema for base CloudEvents structure using non-binary data: + +```json +{ + "$schema": "https://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "specversion", + "type", + "source", + "id" + ], + "properties": { + "specversion": { + "type": "string", + "const": "1.0" + }, + "type": { + "type": "string", + "minLength": 1 + }, + "source": { + "type": "string", + "format": "uri-reference", + "minLength": 1 + }, + "id": { + "type": "string", + "minLength": 1 + }, + "time": { + "type": "string", + "format": "date-time" + }, + "datacontenttype": { + "type": "string" + }, + "dataschema": { + "type": "string", + "format": "uri" + }, + "subject": { + "type": "string", + "minLength": 1 + }, + "data": {} + }, + "additionalProperties": true +} +``` + +## 4 HTTPS Binding + +CloudEvents **MUST** follow the HTTP Protocol Binding for CloudEvents [CloudEvents-HTTP](#cloudevents-http). + +When using HTTPS and non-binary content, events **MUST** be serialized in JSON format using the JSON structured mode. + +Batch events **MAY** be used. + +### 4.1 Security and Authorization + +All endpoints **MUST** use HTTPS, HTTP over TLS 1.2 or higher. Authorization **MUST** adhere to the Data Plane Signaling +Token Refresh Protocol [DPS-TokenRefresh](#dps-token-refresh). + +If a client is not authorized for an endpoint request, the API **MUST** return `401 Unauthorized` or `404 Not Found`. + +## 5 Examples + +> *This section is non-normative* + +### 5.1 Single Event + +```json +{ + "specversion": "1.0", + "type": "org.catena-x.pcf.NotificationEvent.v1", + "id": "e678a546-8bb7-4491-8765-046fa39ac76c", + "source": "did:web:example.com", + "sourcebpn": "BPNL0000000000AA", + "time": "2025-02-27T12:34:56.789Z", + "datacontenttype": "application/json", + "dataschema": "https://example.com/schemas/status.json", + "subject": "part/12345", + "data": { + "partId": "12345", + "status": "ready" + } +} +``` + +### 5.2 Batch Event + +```json +[ + { + "specversion": "1.0", + "type": "org.catena-x.pcf.NotificationEvent.v1", + "id": "e678a546-8bb7-4491-8765-046fa39ac76c", + "source": "did:web:example.com", + "sourcebpn": "BPNL0000000000AA", + "time": "2025-02-27T12:34:56.789Z", + "datacontenttype": "application/json", + "dataschema": "https://example.com/schemas/status.json", + "subject": "part/12345", + "data": { + "partId": "12345", + "status": "ready" + } + }, + { + "specversion": "1.0", + "type": "org.catena-x.pcf.NotificationEvent.v1", + "id": "2b51c8ed-2f06-4220-a454-52bd3d1b4797", + "source": "did:web:example.com", + "sourcebpn": "BPNL0000000000AA", + "time": "2025-02-27T12:34:56.789Z", + "datacontenttype": "application/json", + "dataschema": "https://example.com/schemas/status.json", + "subject": "part/67890", + "data": { + "partId": "67890", + "status": "updated" + } + } +] +``` + +## 6 References + +### 6.1 Normative References + + +**[CloudEvents]** Cloud Native Computing Foundation, "CloudEvents 1.0.2 — Core Specification", +. + + +**[CloudEvents-HTTP]** Cloud Native Computing Foundation, "HTTP Protocol Binding for CloudEvents 1.0.2", +. + + +**[CloudEvents-JSON]** Cloud Native Computing Foundation, "JSON Event Format for CloudEvents 1.0.2", +. + + +**[DPS-TokenRefresh]** Eclipse Data Plane Signaling Working Group, "Data Plane Signaling Token Refresh Protocol", +. + + +**[RFC2046]** Freed, N. and Borenstein, N., "Multipurpose Internet Mail Extensions (MIME) Part Two: Media Types", +RFC 2046, November 1996, . + + +**[RFC2119]** Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997, +. + + +**[RFC3339]** Klyne, G. and Newman, C., "Date and Time on the Internet: Timestamps", RFC 3339, July 2002, +. + + +**[RFC3986]** Berners-Lee, T., Fielding, R., and Masinter, L., "Uniform Resource Identifier (URI): Generic Syntax", +STD 66, RFC 3986, January 2005, . + + +**[RFC8174]** Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, May 2017, +. + + +**[RFC9562]** Davis, K., Peabody, B., and Leach, P., "Universally Unique IDentifiers (UUIDs)", RFC 9562, May 2024, +. + +### 6.2 Non-Normative References + + +**[CloudEvents-Primer]** Cloud Native Computing Foundation, "CloudEvents Primer", +. diff --git a/docs/standards/CX-0135-CompanyCertificateManagement/CX-0000-CloudEventsFoundation-jim.md b/docs/standards/CX-0135-CompanyCertificateManagement/CX-0000-CloudEventsFoundation-jim.md new file mode 100644 index 0000000000..bacd494d66 --- /dev/null +++ b/docs/standards/CX-0135-CompanyCertificateManagement/CX-0000-CloudEventsFoundation-jim.md @@ -0,0 +1,362 @@ +# CX-0000 Notification Foundation (CX-151?) + +## Abstract + +This standard defines a protocol-agnostic foundation for event-based data exchange built on the CloudEvents +specification. It provides the base event structure and mechanisms that can be used across multiple Catena-X use cases +and domains. + +## 1 Introduction + +This standard establishes the foundational layer for event-based communication, based on the CloudEvents v1+ +specification. Note that implementations should periodically upgrade support for minor (backward-compatible) versions of +that specification. It defines core event structures, attribute requirements, and extension mechanisms without binding +to specific transport protocols. + +### 1.1 Audience & Scope + +This specification covers: + +- Base event structure and attributes +- Type system and data encoding +- Extension mechanisms +- Protocol-agnostic design principles + +### 1.2 Conformance and Proof of Conformity + +> *This section is normative* + +The keywords **MAY**, **MUST**, **MUST NOT**, **OPTIONAL**, **RECOMMENDED**, **REQUIRED**, **SHOULD**, and **SHOULD NOT** +in this document are to be interpreted as described in BCP 14 [RFC2119](#rfc2119) [RFC8174](#rfc8174) when, and only +when, they appear in all capitals, as shown here. + +To prove conformity: + +- Events **MUST** conform to the CloudEvents v1+ specification +- Events **MUST** be serializable in JSON format +- Events **MUST** include all REQUIRED attributes as defined in section 2.1 +- JSON schemas for event formats **MUST** be provided for validation + +### 1.3 Terminology + +> *This section is non-normative* + +**Event**: A data record expressing an occurrence and its context + +**Producer**: The system or process that creates events + +**Consumer**: The system or process that receives and processes events + +**Source**: An identifier for the context in which an event occurred + +**Type**: A string describing the nature of the event + +**CloudEvents**: The CNCF specification for describing events in a common format + +## 2 Event Specification + +> *This section is normative* + +### 2.1 Base Event Structure + +All events conforming to this standard **MUST** be valid CloudEvents v1.0.2 events [CloudEvents](#cloudevents). + +#### 2.1.1 Required Attributes + +The following CloudEvents attributes are **REQUIRED**: + +| Attribute | Type | Description | +|---------------|---------------|---------------------------------------------------------------------------------------------------------------| +| `id` | String | Unique identifier for the event. Producers MUST ensure that `source` + `id` is unique for each distinct event | +| `source` | URI-reference | The source DID | +| `specversion` | String | CloudEvents specification version. MUST be "1.0" | +| `type` | String | Event type identifier. MUST be a non-empty string. SHOULD use reverse-DNS notation | + +#### 2.1.2 Required Extension Attributes + +| Attribute | Type | Description | +|-------------|--------|----------------| +| `sourcebpn` | String | The source BPN | + +#### 2.1.3 Recommended Attributes + +The following CloudEvents attributes are **RECOMMENDED**: + +| Attribute | Type | Description | +|-------------------|-----------|-----------------------------------------------------------| +| `time` | Timestamp | [RFC3339](#rfc3339) timestamp of when the event occurred | +| `datacontenttype` | String | Media type of the data payload (e.g., "application/json") | +| `dataschema` | URI | URI identifying the schema of the data payload | + +If `datacontenttype` is not specified, the event **MUST** be interpreted as `application/json`. + +#### 2.1.4 Optional Attributes + +| Attribute | Type | Description | +|--------------------|--------|---------------------------------------------------------| +| `subject` | String | Subject of the event in the context of the event source | +| `relatedmessageid` | String | A related message id | + +### 2.2 Identifier Requirements + +Event IDs **MUST** be unique within the scope of a `source`: + +- Combination of `source` + `id` **MUST** uniquely identify an event +- Duplicate events (e.g., retries) **MUST** reuse the same `source` and `id` +- Event IDs **MUST** be UUIDs as defined by [RFC9562](#rfc9562) + +> NOTE: Consumers **MUST NOT** assume which UUID version a given `id` uses. The `id` is to be treated as an opaque +> identifier and used only for equality comparison; its internal structure (version, timestamp, or any other embedded +> data) **MUST NOT** be interpreted. + +### 2.3 Event Types + +#### 2.3.1 Type Naming Convention + +> ISSUE: We should consider using the `org.tractus-x` domain instead of `org.catena-x` for event types. This may +> make the event types and specification more generic for reuse across other dataspaces. + +Event types **MUST** follow reverse-DNS notation where the domain is `org.catena-x`: + +`...` + +The `domain` and `use case` **MUST** be in lowercase. + +The `specifier` **MUST** be in Camel Case, for example: + +``` +org.catena-x.pcf.NotificationEvent.v1 +``` + +#### 2.3.2 Type Versioning + +Version information **MUST** be encoded as follows: + +`v` + +for example `v1`. Minor/patch versions **MUST NOT** be in the type name + +### 2.4 Event Data + +#### 2.4.1 Data Payload + +The `data` attribute **MAY** contain domain-specific information of a valid JSON type (object, array, string, number, +boolean, null). The `data` attribute **SHOULD** have a schema defined via the `dataschema` attribute. + +#### 2.4.2 Data Content Type + +When `data` is present, `datacontenttype` **SHOULD** be specified. If specified, it **MUST** be a valid media type as +defined by [RFC2046](#rfc2046). If `data` is present and `datacontenttype` is not specified, implementation **MUST** default to +interpreting the data payload as "application/json". If `data_base64` is present and `datacontenttype` is not specified, +implementation **MUST** default to interpreting the data payload as "application/octet-stream". + +### 2.5 Extension Attributes + +#### 2.5.1 Defining Extensions + +Custom attributes **MAY** be added following CloudEvents naming rules: + +- The attribute **MUST** consist of lowercase letters (a-z) or digits (0–9) +- The attribute **MUST** be at least one character, **SHOULD NOT** exceed 20 characters +- The attribute **SHOULD** start with a letter +- The attribute **MUST NOT** use the name `data`, `data_base64`, or any CloudEvents reserved attribute names + +#### 2.5.2 Extension Attribute Types + +Extension attributes **MUST** use CloudEvents type system: + +- Boolean +- Integer +- String +- Binary +- URI +- URI-reference +- Timestamp + +## 3 Data Formats + +> *This section is normative* + +### 3.1 JSON Format + +All implementations **MUST** support JSON serialization per the CloudEvents JSON format specification +[CloudEvents-JSON](#cloudevents-json). + +#### 3.2 JSON Schema + +JSON Schema for base CloudEvents structure using non-binary data: + +```json +{ + "$schema": "https://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "specversion", + "type", + "source", + "id" + ], + "properties": { + "specversion": { + "type": "string", + "const": "1.0" + }, + "type": { + "type": "string", + "minLength": 1 + }, + "source": { + "type": "string", + "format": "uri-reference", + "minLength": 1 + }, + "id": { + "type": "string", + "minLength": 1 + }, + "time": { + "type": "string", + "format": "date-time" + }, + "datacontenttype": { + "type": "string" + }, + "dataschema": { + "type": "string", + "format": "uri" + }, + "subject": { + "type": "string", + "minLength": 1 + }, + "data": {} + }, + "additionalProperties": true +} +``` + +## 4 HTTPS Binding + +CloudEvents **MUST** follow the HTTP Protocol Binding for CloudEvents [CloudEvents-HTTP](#cloudevents-http). + +When using HTTPS and non-binary content, events **MUST** be serialized in JSON format using the JSON structured mode. + +Batch events **MAY** be used. + +### 4.1 Security and Authorization + +All endpoints **MUST** use HTTPS, HTTP over TLS 1.2 or higher. Authorization **MUST** adhere to the Data Plane Signaling +Token Refresh Protocol [DPS-TokenRefresh](#dps-token-refresh). + +If a client is not authorized for an endpoint request, the API **MUST** return `401 Unauthorized` or `404 Not Found`. + +## 5 Examples + +> *This section is non-normative* + +### 5.1 Single Event + +```json +{ + "specversion": "1.0", + "type": "org.catena-x.pcf.NotificationEvent.v1", + "id": "e678a546-8bb7-4491-8765-046fa39ac76c", + "source": "did:web:example.com", + "sourcebpn": "BPNL0000000000AA", + "time": "2025-02-27T12:34:56.789Z", + "datacontenttype": "application/json", + "dataschema": "https://example.com/schemas/status.json", + "subject": "part/12345", + "data": { + "partId": "12345", + "status": "ready" + } +} +``` + +### 5.2 Batch Event + +```json +[ + { + "specversion": "1.0", + "type": "org.catena-x.pcf.NotificationEvent.v1", + "id": "e678a546-8bb7-4491-8765-046fa39ac76c", + "source": "did:web:example.com", + "sourcebpn": "BPNL0000000000AA", + "time": "2025-02-27T12:34:56.789Z", + "datacontenttype": "application/json", + "dataschema": "https://example.com/schemas/status.json", + "subject": "part/12345", + "data": { + "partId": "12345", + "status": "ready" + } + }, + { + "specversion": "1.0", + "type": "org.catena-x.pcf.NotificationEvent.v1", + "id": "2b51c8ed-2f06-4220-a454-52bd3d1b4797", + "source": "did:web:example.com", + "sourcebpn": "BPNL0000000000AA", + "time": "2025-02-27T12:34:56.789Z", + "datacontenttype": "application/json", + "dataschema": "https://example.com/schemas/status.json", + "subject": "part/67890", + "data": { + "partId": "67890", + "status": "updated" + } + } +] +``` + +## 6 References + +### 6.1 Normative References + + +**[CloudEvents]** Cloud Native Computing Foundation, "CloudEvents 1.0.2 — Core Specification", +. + + +**[CloudEvents-HTTP]** Cloud Native Computing Foundation, "HTTP Protocol Binding for CloudEvents 1.0.2", +. + + +**[CloudEvents-JSON]** Cloud Native Computing Foundation, "JSON Event Format for CloudEvents 1.0.2", +. + + +**[DPS-TokenRefresh]** Eclipse Data Plane Signaling Working Group, "Data Plane Signaling Token Refresh Protocol", +. + + +**[RFC2046]** Freed, N. and Borenstein, N., "Multipurpose Internet Mail Extensions (MIME) Part Two: Media Types", +RFC 2046, November 1996, . + + +**[RFC2119]** Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997, +. + + +**[RFC3339]** Klyne, G. and Newman, C., "Date and Time on the Internet: Timestamps", RFC 3339, July 2002, +. + + +**[RFC3986]** Berners-Lee, T., Fielding, R., and Masinter, L., "Uniform Resource Identifier (URI): Generic Syntax", +STD 66, RFC 3986, January 2005, . + + +**[RFC8174]** Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, May 2017, +. + + +**[RFC9562]** Davis, K., Peabody, B., and Leach, P., "Universally Unique IDentifiers (UUIDs)", RFC 9562, May 2024, +. + +### 6.2 Non-Normative References + + +**[CloudEvents-Primer]** Cloud Native Computing Foundation, "CloudEvents Primer", +. diff --git a/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement-CloudEvents-jim.md b/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement-CloudEvents-jim.md new file mode 100644 index 0000000000..f2bdf9e753 --- /dev/null +++ b/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement-CloudEvents-jim.md @@ -0,0 +1,1279 @@ +# CX-0135 Business Partner Company Certificate Management (CCM) + +## 1 Introduction + +This specification builds upon [CX-0151](#cx-0151) to define a Company Certificate Management (CCM) wire protocol for +exchanging company certificate data between Catena-X participants. + +### 1.1 Conformance and Proof Of Conformity + +The keywords **MAY**, **MUST**, **MUST NOT**, **OPTIONAL**, **RECOMMENDED**, **REQUIRED**, **SHOULD**, and **SHOULD +NOT** in this document are to be interpreted as described in BCP 14 [RFC2119](#rfc2119) [RFC8174](#rfc8174) when, and +only when they appear in all capitals, as shown here. + +## 2 Model + +> *This section is NOT normative* + +This section describes the conceptual model underpinning the specification wire protocol. It is background material: the +model introduces the entities and lifecycle that the normative sections build upon, but defines no normative +requirements of its own. + +The model consists of two concepts: + +- The `Certificate Exchange` ([Section 2.1](#21-certificate-exchange)) — a single, correlatable interaction in which one + certificate is delivered from a Certificate Provider to a Certificate Consumer and its outcome is reported back. A + `Certificate Exchange` has its own identity and a well-defined lifecycle. +- The `Certificate Lifecycle` ([Section 2.2](#22-certificate-lifecycle)) — the independent lifecycle of a certificate as + an artifact (its creation, modification, and revocation). + +### 2.1 Certificate Exchange + +A `Certificate Exchange` represents one end-to-end interaction between a Certificate Provider and a Certificate Consumer +involving the delivery of a single certificate from the time the interaction is started until it reaches a terminal +outcome. + +#### 2.1.1 Identity and Correlation + +A `Certificate Exchange` is identified by an **`exchangeId`** assigned when it is started. The `exchangeId` is the +correlation handle for the entire interaction and is distinct from the identifier of any individual message or event +(for example, a CloudEvent `id` + `source`). + +An exchange concerns a specific certificate, a (`certificateId`, `version`) pair, and is conducted with a +`counterparty`, the authenticated Certificate Consumer. + +A `Certificate Exchange` may be opened by the Certificate Consumer or Certificate Provider: + +- **Consumer-initiated (pull):** the Certificate Consumer opens the exchange by requesting a certificate. The + Certificate Provider assigns the `exchangeId` (together with the `certificateId` and `version`) and returns them in + the response. Every request opens an exchange — including one the provider declines synchronously, which opens an + exchange that terminates immediately at `DECLINED`; the identifiers are still returned so the outcome remains + correlatable. +- **Provider-initiated (push):** the Certificate Provider opens the exchange when a certificate is already available, + assigning an `exchangeId` and carrying it — with the `certificateId` and `version` — in the notification. + +**Idempotency.** Each `Certificate Exchange` has a unique `exchangeId`. A message that repeats an `exchangeId` refers to +the same exchange rather than opening a new one. Multiple exchanges **MAY** concern the same certificate and +`counterparty`. + +Acceptance feedback is correlated to the exchange by its `exchangeId` for both flows. + +A re-attempt after a terminal outcome — for example, re-evaluating a `REJECTED` or `ERRORED` certificate — is a new +`Certificate Exchange`, with a new `exchangeId`, for the same certificate. + +#### 2.1.2 Phases and Ownership + +A `Certificate Exchange` progresses through two sequential phases, each owned by one party: + +1. **Fulfillment (Provider-owned):** The Certificate Provider works to make the certificate available. +2. **Acceptance (Consumer-owned):** The Certificate Consumer retrieves and processes the certificate and reports the + outcome. + +The phases never overlap: the `Acceptance` phase begins only once `Fulfillment` has made the certificate available. +Provider-owned and Consumer-owned states use deliberately disjoint vocabulary, so the owner of a state is unambiguous. +Each phase can end in a negative decision or a business error: `DECLINED`/`REJECTED` are decisions (the provider +declines the request, or the consumer does not accept the certificate), while `FAILED`/`ERRORED` indicate a business +error such as an invalid certificate. These error states represent business-level problems only; technical and transport +failures (for example, connectivity errors or timeouts) are not modeled as `Certificate Exchange` states and are handled +at the transport layer. + +#### 2.1.3 State Machine + +The states of a `Certificate Exchange` are defined below and use the past tense: + +```mermaid +stateDiagram-v2 + [*] --> REQUESTED + REQUESTED --> ACKNOWLEDGED: provider accepts & starts preparing + REQUESTED --> DECLINED: provider declines the request + ACKNOWLEDGED --> CERTIFICATION_REQUESTED: submitted to external authority + ACKNOWLEDGED --> FULFILLED: certificate available (already held) + ACKNOWLEDGED --> FAILED: cannot produce a valid certificate + CERTIFICATION_REQUESTED --> FULFILLED: certified & available + CERTIFICATION_REQUESTED --> DECLINED: authority declines + CERTIFICATION_REQUESTED --> FAILED: certification invalid + FULFILLED --> RETRIEVED: consumer reports retrieval (OPTIONAL) + FULFILLED --> ACCEPTED: consumer accepts + FULFILLED --> REJECTED: consumer does not accept content + FULFILLED --> ERRORED: certificate invalid + RETRIEVED --> ACCEPTED: consumer accepts + RETRIEVED --> REJECTED: consumer does not accept content + RETRIEVED --> ERRORED: certificate invalid + DECLINED --> [*] + FAILED --> [*] + ACCEPTED --> [*] + REJECTED --> [*] + ERRORED --> [*] +``` + +| State | Phase / Owner | Terminal | Description | +|---------------------------|------------------------|----------|-------------------------------------------------------------------------------------------------------------------| +| `REQUESTED` | Fulfillment / Provider | No | The exchange was opened by the consumer; the provider has not yet acted. *(Consumer-initiated exchanges only.)* | +| `ACKNOWLEDGED` | Fulfillment / Provider | No | The provider accepted the request and began preparing the certificate. | +| `CERTIFICATION_REQUESTED` | Fulfillment / Provider | No | The provider submitted the request to an external certification authority and is awaiting issuance. *(Optional.)* | +| `FULFILLED` | Fulfillment / Provider | No | The certificate is prepared and available for retrieval. Hand-off point. | +| `DECLINED` | Fulfillment / Provider | Yes | The provider declined the request (a business decision; e.g. the certificate type is not offered). | +| `FAILED` | Fulfillment / Provider | Yes | The provider could not produce a valid certificate (a business error; e.g. the certificate is invalid). | +| `RETRIEVED` | Acceptance / Consumer | No | The consumer fetched the certificate and is processing it. *(Optional — see the note below.)* | +| `ACCEPTED` | Acceptance / Consumer | Yes | The consumer accepted the certificate. | +| `REJECTED` | Acceptance / Consumer | Yes | The consumer did not accept the certificate content (a business decision). | +| `ERRORED` | Acceptance / Consumer | Yes | The consumer found the certificate to be in error (a business error; e.g. the certificate is invalid). | + +A provider-initiated (push) exchange enters the lifecycle directly at `FULFILLED`. There is no request, so `REQUESTED`, +`ACKNOWLEDGED` and `CERTIFICATION_REQUESTED` are never visited. The Acceptance phase is identical for both pull and +push. + +`RETRIEVED` is **OPTIONAL**. It is a non-terminal acknowledgment that the consumer has fetched the certificate and is +evaluating it; the consumer **MAY** report it as a delivery receipt but is not required to. An exchange therefore reaches +a terminal acceptance state either by way of `RETRIEVED` (`FULFILLED → RETRIEVED → {ACCEPTED, REJECTED, ERRORED}`) or +directly from `FULFILLED` (`FULFILLED → {ACCEPTED, REJECTED, ERRORED}`). The terminal acceptance verdicts remain +Consumer-owned in both cases. + +`REQUESTED` is instantaneous and internal to the Certificate Provider; it is never reported on the wire. The first +Fulfillment status a Certificate Consumer observes is the one carried in the request response (see +[Section 4.4.1](#441-certificate-request)), which is `ACKNOWLEDGED` or a later state. A provider that can satisfy a +request without intermediate steps **MAY** report a later Fulfillment state directly — for example `FULFILLED` when the +certificate is already held, or `CERTIFICATION_REQUESTED` when the request is immediately forwarded to an external +authority — having passed through the earlier states instantaneously. A reported `status` therefore reflects the +exchange's current state, not necessarily a single transition from the one before it. + +#### 2.1.4 Terminal States and Immutability + +A `Certificate Exchange` is single-shot. The five terminal states — `DECLINED`, `FAILED`, `ACCEPTED`, `REJECTED`, and +`ERRORED` — conclude the exchange permanently. A terminal exchange is never reopened, reused, or transitioned further. + +#### 2.1.5 Relationship to the Certificate Lifecycle + +A `Certificate Exchange` governs the *delivery* of a certificate, not the certificate itself. Modifications, updates, +and revocations of a certificate are changes to the certificate artifact and are **not** transitions of a +`Certificate Exchange`. In particular, a change to a certificate that has already been delivered does not reopen or +alter the (possibly terminal) exchange that delivered it. A modification revises the certificate in place under the same +`certificateId` and does not open a new `Certificate Exchange`. The certificate's own lifecycle is described in +[Section 2.2](#22-certificate-lifecycle). + +### 2.2 Certificate Lifecycle + +The `Certificate Lifecycle` tracks a certificate as an artifact over time, independently of how many times it is +delivered. Whereas a `Certificate Exchange` is single-shot and concerns one delivery interaction, a certificate is +long-lived: it may be published, revised, and eventually withdrawn. + +#### 2.2.1 Identity and Versioning + +A certificate is identified by a **`certificateId`** that is stable for the life of the certificate. A modification does +not create a new identifier; instead, the certificate carries a **`version`**: + +- `CREATED` makes the first `version` of the certificate available for retrieval. +- Each `MODIFIED` publishes a new `version` under the same `certificateId`, superseding the previous one. The latest + `version` is authoritative. +- A certificate is therefore identified by the pair (`certificateId`, `version`). A `Certificate Exchange` delivers one + specific `version`. + +The `certificateId` and the initial `version` number may be assigned before the certificate is published. When a +Certificate Provider accepts a consumer's request and produces the certificate only later (see +[Section 2.1.1](#211-identity-and-correlation)), the identifier is allocated at acceptance so that an in-progress +`Certificate Exchange` can reference its certificate. Publication (`CREATED`) then occurs when the certificate becomes +available. + +A certificate covers a **fixed set of locations** (`locationBpns`) — the BPNs it applies to. The location set is a +static property of the certificate; a certificate that would cover a different set of locations is a distinct +certificate. + +#### 2.2.2 State Machine + +The states use the past tense and describe the *publication* lifecycle of the certificate. They are independent of the +certificate's validity (see [Section 2.2.3](#223-validity-as-a-separate-dimension)). + +```mermaid +stateDiagram-v2 + [*] --> CREATED + CREATED --> MODIFIED: new version published + MODIFIED --> MODIFIED: subsequent version published + CREATED --> WITHDRAWN: provider withdraws the certificate + MODIFIED --> WITHDRAWN: provider withdraws the certificate + WITHDRAWN --> [*] +``` + +| State | Terminal | Description | +|-------------|----------|---------------------------------------------------------------------------------------------------------------------------| +| `CREATED` | No | The certificate was first published under a new `certificateId`, establishing its initial `version`. | +| `MODIFIED` | No | A new `version` of the certificate was published under the same `certificateId`; the `version` is incremented. May recur. | +| `WITHDRAWN` | Yes | The provider withdrew (removed) the certificate; it is no longer available. Terminal. | + +#### 2.2.3 Validity as a Separate Dimension + +A certificate's validity is an independent dimension derived from its `validFrom` and `validUntil` dates, not from the +publication states defined above. A given `version` is *active* within its validity window and *expired* afterward, and +this status changes with the passage of time alone — no lifecycle transition occurs. The two dimensions are orthogonal: +a certificate may be `WITHDRAWN` while still within its validity window, or remain `CREATED`/`MODIFIED`after it has +expired. + +#### 2.2.4 Relationship to the `Certificate Exchange` + +Of the publication events, only `CREATED` may open a new `Certificate Exchange` — pushed by the provider, or discovered +and requested by the consumer — to deliver the certificate. `MODIFIED` and `WITHDRAWN` do not open an exchange: a +modification revises the certificate in place under the same `certificateId` without initiating a new delivery, and a +withdrawal ends the certificate's availability. Consistent with +[Section 2.1.5](#215-relationship-to-the-certificate-lifecycle), a lifecycle transition never alters an existing +`Certificate Exchange`. These transitions are communicated to Certificate Consumers as certificate lifecycle events (see +[Section 4.3.1](#431-certificate-lifecycle-events)). + +## 3 Data Plane Wire Protocol + +> *This section is NOT normative* + +This specification defines the following agent systems: + +- **Certificate Consumer Control Plane (Cert CCP)**: The [DSP](#dsp) Control Plane operated by the participant that + consumes certificates. +- **Certificate Consumer Data Plane (Cert CDP)**: The [DSP](#dsp) Data Plane operated by the participant that consumes + certificates. +- **Certificate Provider Control Plane (Cert PCP)**: The [DSP](#dsp) Control Plane operated by the participant that + provides certificates. +- **Certificate Provider Data Plane (Cert PDP)**: The [DSP](#dsp) Data Plane operated by the participant that provides + certificates. + +The wire transmission protocol supports an optional consumer endpoint for providers to send notifications of certificate +availability. Provider endpoints offer mechanisms for requesting, querying, retrieving, and reporting the status of +certificates. The endpoints are discoverable and made accessible via a [DSP Catalog](#dsp-catalog). + +```mermaid +sequenceDiagram + participant CDP as Cert CDP + participant CCP as Cert CCP + participant PCP as Cert PCP + participant PDP as Cert PDP + + rect rgb(208, 208, 209, .5) + PCP --> CCP: 1. Negotiate event API contract (DSP) + PDP ->> CDP: 2. Certificate lifecycle event (Created / Modified / Withdrawn) + end + CCP --> PCP: 3. Negotiate or reuse cert API contract (DSP) + CDP ->> PDP: 4. Request cert (returns exchangeId + certificateId + version) + CDP ->> PDP: 5. Poll fulfillment status (ACKNOWLEDGED, CERTIFICATION_REQUESTED, FULFILLED, DECLINED, FAILED) + PDP ->> CDP: 5a. (optional) Push fulfillment status to Notification API + CDP ->> PDP: 6. Retrieve cert + CDP ->> PDP: 7. Acceptance status (RETRIEVED, ACCEPTED, REJECTED, ERRORED) +``` + +### 3.1 Certificate Notification + +A Certificate Consumer may optionally make a [Certificate Consumer Notification API](#43-certificate-consumer-notification-api) +available to Certificate Providers. Through it, a provider may notify the consumer of certificate lifecycle changes +(creation, modification, and withdrawal) and may push the fulfillment status of an open consumer-initiated exchange +instead of requiring the consumer to poll (see [Section 4.3.2](#432-certificate-fulfillment-notification)). This API +functions as a DSP Data Plane, which Certificate Providers gain access to via the DSP protocol. Upon receiving a +lifecycle notification with `status` `CREATED` or `MODIFIED`, the Certificate Consumer may retrieve the certificate +using the [Certificate Provider API](#44-certificate-provider-api). + +### 3.2 Certificate Retrieval + +A Certificate Consumer retrieves a certificate using the [Certificate Provider API](#44-certificate-provider-api). This API functions as a DSP Data +Plane, which Certificate Consumers gain access to via the DSP protocol. The Certificate Consumer may request a +certificate, poll the fulfillment status of a request, query for certificates, get a certificate's metadata, retrieve +its individual documents, and post acceptance status updates. + +## 4 Data Plane Wire Protocol APIs + +> *This section is normative* + +### 4.1 Base URL and Transport Security + +The notation indicates the base URL for all HTTPS endpoints. For example, if the base URL is api.example.com, the +URL https:///certificates/123 will map to https//api.example.com/certificates/123. + +All endpoints **MUST** use HTTPS, HTTP over TLS 1.2 or higher. + +### 4.2 Authorization + +Authorization **MUST** adhere to [CX-0000 Section 4](./CX-0000-CloudEventsFoundation-jim.md#4-https-binding). + +### 4.3 Certificate Consumer Notification API + +The Certificate Consumer Notification API enables a Certificate Consumer to receive notifications from Certificate +Providers: certificate lifecycle events (see [Section 4.3.1](#431-certificate-lifecycle-events)) and fulfillment-status +updates for its open exchanges (see [Section 4.3.2](#432-certificate-fulfillment-notification)). This API is optional; a Certificate +Consumer that does not wish to receive notifications is not required to implement it. A Certificate Consumer that does +implement it **MUST** adhere to the normative requirements defined in this section. + +#### 4.3.1 Certificate Lifecycle Events + +The Certificate Consumer **MUST** expose the following endpoint to receive certificate lifecycle events. These events +are sent by the Certificate Provider to notify the Certificate Consumer of changes to a certificate over its lifecycle +(see [Section 2.2](#22-certificate-lifecycle)). + +| | | +|------------------|------------------------------------------------------| +| **HTTP Method**: | POST | +| **URL Path** | `POST /certificate-notifications` | +| **Content Type** | `application/cloudevents+json` | +| **Request** | `CertificateLifecycleStatus` (single event or batch) | +| **Response** | `HTTP 204` with empty body or error | + +The endpoint accepts a single CloudEvent ([CloudEvents](#cloudevents)) or a batch (a JSON array) as defined in +[CX-0000](#cx-0000), whose HTTP binding follows [CloudEvents-HTTP](#cloudevents-http). + +**Event Type**: `org.catena-x.ccm.CertificateLifecycleStatus.v1` + +The Certificate Consumer **MUST** dispatch on the `data.status` value, which maps to a state of the +`Certificate Lifecycle`: + +| `status` | Lifecycle State | Opens an Exchange | Description | +|-------------|-----------------|-------------------|------------------------------------------------------------------------------------------------------------------------------| +| `CREATED` | `CREATED` | Yes | A certificate has been published and is available for retrieval. | +| `MODIFIED` | `MODIFIED` | No | A new `version` of an existing certificate has been published; the Certificate Consumer **MAY** retrieve the latest version. | +| `WITHDRAWN` | `WITHDRAWN` | No | The certificate has been withdrawn and is no longer available. | + +Only the `CREATED` status opens a `Certificate Exchange` (see +[Section 2.2.4](#224-relationship-to-the-certificate-exchange)); `MODIFIED` and `WITHDRAWN` are notifications that do +not open an exchange. The two state machines meet here: a `CREATED` lifecycle event opens a provider-initiated exchange +that enters directly at the `FULFILLED` exchange state (see [Section 2.1.3](#213-state-machine)) — both express that the +certificate is now available, from the artifact and delivery viewpoints respectively. + +The `data` payload derives from the common certificate-status base payload and constrains `status` to the lifecycle +values above. It contains the following properties: + +| Property | Type | Required | Description | +|-------------------|------------------|-------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `exchangeId` | String | CONDITIONAL | Identifier of the `Certificate Exchange` opened by this event. Present when `status` is `CREATED`; absent for `MODIFIED` and `WITHDRAWN`. Distinct from the CloudEvent `id`. | +| `certificateId` | String | MANDATORY | The unique identifier of the certificate, used to retrieve the certificate via `GET /certificates/{id}`. | +| `version` | Integer | MANDATORY | The version of the certificate. Together with `certificateId` it identifies the specific certificate (see [Section 2.2.1](#221-identity-and-versioning)). | +| `status` | String | MANDATORY | The lifecycle status. **MUST** be one of `CREATED`, `MODIFIED`, or `WITHDRAWN`. | +| `datasetId` | String | MANDATORY | The identifier of the DSP dataset under which the certificate is exposed in the Certificate Provider's catalog. | +| `certificateType` | String | MANDATORY | An opaque string identifying the certificate type (for example, `ISO9001`). | +| `validFrom` | String | CONDITIONAL | The inclusive start date of the validity period, an [ISO 8601](#iso8601) full-date (`YYYY-MM-DD`). **MUST** be present when `status` is `CREATED` or `MODIFIED`; **MAY** be omitted when `status` is `WITHDRAWN`. | +| `validUntil` | String | CONDITIONAL | The inclusive end date of the validity period, an [ISO 8601](#iso8601) full-date (`YYYY-MM-DD`). Same presence rules as `validFrom`. | +| `locationBpns` | Array of Strings | OPTIONAL | The Business Partner Numbers (BPNs) of the sites or addresses the certificate applies to. If omitted, the certificate applies to the issuing legal entity as a whole. | + +The following are non-normative examples. + +**Status: CREATED** + +```json +{ + "specversion": "1.0", + "type": "org.catena-x.ccm.CertificateLifecycleStatus.v1", + "source": "urn:bpn:BPNL0000000001AB", + "subject": "BPNL0000000002CD", + "id": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d", + "time": "2025-05-04T07:00:00Z", + "datacontenttype": "application/json", + "dataschema": "https://w3id.org/catenax/schemas/ccm/certificate-lifecycle-status.json", + "data": { + "exchangeId": "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44", + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "version": 1, + "status": "CREATED", + "datasetId": "dataset-ccm-cert-abc123", + "certificateType": "ISO9001", + "validFrom": "2023-01-25", + "validUntil": "2026-01-24", + "locationBpns": [ + "BPNS00000003AYRE", + "BPNA000000000001" + ] + } +} +``` + +**Status: MODIFIED** + +```json +{ + "specversion": "1.0", + "type": "org.catena-x.ccm.CertificateLifecycleStatus.v1", + "source": "urn:bpn:BPNL0000000001AB", + "subject": "BPNL0000000002CD", + "id": "b2c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e", + "time": "2025-08-01T07:00:00Z", + "datacontenttype": "application/json", + "dataschema": "https://w3id.org/catenax/schemas/ccm/certificate-lifecycle-status.json", + "data": { + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "version": 2, + "status": "MODIFIED", + "datasetId": "dataset-ccm-cert-abc123", + "certificateType": "ISO9001", + "validFrom": "2023-01-25", + "validUntil": "2027-01-24", + "locationBpns": [ + "BPNS00000003AYRE", + "BPNA000000000001" + ] + } +} +``` + +**Status: WITHDRAWN** + +```json +{ + "specversion": "1.0", + "type": "org.catena-x.ccm.CertificateLifecycleStatus.v1", + "source": "urn:bpn:BPNL0000000001AB", + "subject": "BPNL0000000002CD", + "id": "c3d4e5f6-7a8b-9c0d-1e2f-3a4b5c6d7e8f", + "time": "2025-09-01T07:00:00Z", + "datacontenttype": "application/json", + "dataschema": "https://w3id.org/catenax/schemas/ccm/certificate-lifecycle-status.json", + "data": { + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "version": 2, + "status": "WITHDRAWN", + "datasetId": "dataset-ccm-cert-abc123", + "certificateType": "ISO9001" + } +} +``` + +**Batch** + +Multiple events **MAY** be delivered in a single request as a JSON array: + +```json +[ + { + "specversion": "1.0", + "type": "org.catena-x.ccm.CertificateLifecycleStatus.v1", + "source": "urn:bpn:BPNL0000000001AB", + "subject": "BPNL0000000002CD", + "id": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d", + "time": "2025-05-04T07:00:00Z", + "datacontenttype": "application/json", + "dataschema": "https://w3id.org/catenax/schemas/ccm/certificate-lifecycle-status.json", + "data": { + "exchangeId": "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44", + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "version": 1, + "status": "CREATED", + "datasetId": "dataset-ccm-cert-abc123", + "certificateType": "ISO9001", + "validFrom": "2023-01-25", + "validUntil": "2026-01-24" + } + }, + { + "specversion": "1.0", + "type": "org.catena-x.ccm.CertificateLifecycleStatus.v1", + "source": "urn:bpn:BPNL0000000001AB", + "subject": "BPNL0000000002CD", + "id": "d4e5f6a7-8b9c-0d1e-2f3a-4b5c6d7e8f90", + "time": "2025-09-01T07:00:00Z", + "datacontenttype": "application/json", + "dataschema": "https://w3id.org/catenax/schemas/ccm/certificate-lifecycle-status.json", + "data": { + "certificateId": "cert-770e8400-e29b-41d4-a716-446655449999", + "version": 3, + "status": "WITHDRAWN", + "datasetId": "dataset-ccm-cert-xyz789", + "certificateType": "ISO14001" + } + } +] +``` + +#### 4.3.2 Certificate Fulfillment Notification + +When a Certificate Consumer exposes this Notification API, a Certificate Provider **MAY** push the Fulfillment status of +an open consumer-initiated exchange (see [Section 4.4.1](#441-certificate-request)) to the consumer instead of requiring +it to poll (see [Section 4.4.2](#442-certificate-request-status)). This is the push counterpart of that poll and is +particularly useful for long-running fulfillment, for example while a certificate is `CERTIFICATION_REQUESTED`. + +The event is delivered to the same `POST /certificate-notifications` endpoint defined in +[Section 4.3.1](#431-certificate-lifecycle-events) and is distinguished by its CloudEvents `type`. It is correlated to the +exchange by the `exchangeId` carried in `data`; the `exchangeId` is the one the Certificate Provider returned when the +exchange was opened (see [Section 4.4.1](#441-certificate-request)). + +This notification reports only the Fulfillment phase. It is independent of the certificate's lifecycle status: a +`FULFILLED` notification reports that the requested exchange has been fulfilled and the certificate is available for +retrieval, whereas a lifecycle `CREATED` event (see [Section 4.3.1](#431-certificate-lifecycle-events)) reports a +provider-initiated exchange. A Certificate Provider **MUST NOT** use a lifecycle event to report the outcome of a +consumer-initiated request. + +**Event Type**: `org.catena-x.ccm.CertificateFulfillmentStatus.v1` + +The `data` payload contains the following properties: + +| Property | Type | Required | Description | +|-----------------|-----------------|-------------|-----------------------------------------------------------------------------------------------------------------------------| +| `exchangeId` | String | MANDATORY | Identifier of the `Certificate Exchange` whose Fulfillment status is reported. Distinct from the CloudEvent `id`. | +| `certificateId` | String | MANDATORY | The unique identifier of the certificate the exchange concerns. | +| `status` | String | MANDATORY | The Fulfillment status. **MUST** be one of `ACKNOWLEDGED`, `CERTIFICATION_REQUESTED`, `FULFILLED`, `DECLINED`, or `FAILED`. | +| `errors` | Array of Object | CONDITIONAL | A list of error details. **MUST** be present and non-empty when `status` is `DECLINED` or `FAILED`. | + +A Certificate Provider that pushes Fulfillment status **SHOULD** send at least the terminal outcomes (`FULFILLED`, +`DECLINED`, `FAILED`); intermediate states **MAY** also be pushed. Each entry in the `errors` array contains the +properties defined in [Section 4.4.5](#445-certificate-acceptance-events). + +The following are non-normative examples. + +**Status: FULFILLED** + +```json +{ + "specversion": "1.0", + "type": "org.catena-x.ccm.CertificateFulfillmentStatus.v1", + "source": "urn:bpn:BPNL0000000001AB", + "subject": "BPNL0000000002CD", + "id": "f0e1d2c3-b4a5-6789-0abc-def012345678", + "time": "2025-05-04T07:30:00Z", + "datacontenttype": "application/json", + "dataschema": "https://w3id.org/catenax/schemas/ccm/certificate-fulfillment-status.json", + "data": { + "exchangeId": "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44", + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "status": "FULFILLED" + } +} +``` + +**Status: DECLINED** + +```json +{ + "specversion": "1.0", + "type": "org.catena-x.ccm.CertificateFulfillmentStatus.v1", + "source": "urn:bpn:BPNL0000000001AB", + "subject": "BPNL0000000002CD", + "id": "a1c2e3f4-5b6d-7e8f-9a0b-1c2d3e4f5a6b", + "time": "2025-05-04T07:30:00Z", + "datacontenttype": "application/json", + "dataschema": "https://w3id.org/catenax/schemas/ccm/certificate-fulfillment-status.json", + "data": { + "exchangeId": "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44", + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "status": "DECLINED", + "errors": [ + { + "message": "Certificate type not offered for the requested location" + } + ] + } +} +``` + +#### 4.3.3 Certificate Acceptance Status Query + +The Certificate Consumer **MUST** expose the following endpoint to allow a Certificate Provider to query the current +acceptance status of a `Certificate Exchange` it opened. + +| | | +|------------------|-----------------------------------------------------| +| **HTTP Method**: | GET | +| **URL Path** | `GET /certificate-acceptance-status/{id}` | +| **Content Type** | `application/json` | +| **Response** | `CertificateAcceptanceStatusResponse` or `HTTP 404` | + +The `{id}` path parameter **MUST** be the `exchangeId` assigned by the Certificate Provider when the exchange was opened +(see [Section 2.1.1](#211-identity-and-correlation)). If the `exchangeId` is unknown to the Certificate Consumer, the +endpoint **MUST** respond with `HTTP 404`. + +If the Certificate Consumer has not yet concluded processing, the endpoint **MAY** return a +`CertificateAcceptanceStatusResponse` whose `status` is `RETRIEVED`. + +The response body **MUST** be a JSON object representing the current acceptance status of the exchange. The `status` +property conveys the Acceptance-phase state, including the non-terminal value `RETRIEVED` indicating that the +certificate has been retrieved but acceptance has not yet concluded. The `errors` follow the same rules as the +corresponding acceptance event in [Section 4.4.5](#445-certificate-acceptance-events). + +| Property | Type | Required | Description | +|-----------------|-----------------|-----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `exchangeId` | String | MANDATORY | The identifier of the `Certificate Exchange`. Matches the `{id}` path parameter of the request. | +| `certificateId` | String | MANDATORY | The unique identifier of the certificate the status applies to. | +| `status` | String | MANDATORY | The current acceptance status. **MUST** be one of `RETRIEVED`, `ACCEPTED`, `REJECTED`, or `ERRORED`. | +| `errors` | Array of Object | OPTIONAL | A list of error details. **MUST NOT** be present when `status` is `RETRIEVED` or `ACCEPTED`. **MUST** be present and non-empty when `status` is `REJECTED` or `ERRORED`. | + +Each entry in the `errors` array contains the same properties as defined in +[Section 4.4.5](#445-certificate-acceptance-events): + +| Property | Type | Required | Description | +|-------------|--------|-----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `message` | String | MANDATORY | A human-readable description of the error. | +| `specifier` | String | OPTIONAL | An identifier scoping the error to a particular element of the certificate, such as a site BPN. If omitted, the error applies to the certificate as a whole. | + +The following are non-normative examples of acceptance status responses: + +**Status: RETRIEVED** + +```json +{ + "exchangeId": "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44", + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "status": "RETRIEVED" +} +``` + +**Status: ACCEPTED** + +```json +{ + "exchangeId": "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44", + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "status": "ACCEPTED" +} +``` + +**Status: REJECTED** + +```json +{ + "exchangeId": "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44", + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "status": "REJECTED", + "errors": [ + { + "message": "Certificate has expired" + }, + { + "specifier": "BPNS000000000002", + "message": "Site BPNS000000000002 has been Rejected" + } + ] +} +``` + +#### 4.3.4 OpenAPI Specification + +The Certificate Consumer Notification API is formally defined by the OpenAPI document +[certificate-consumer-notification-api-jim.yaml](./certificate-consumer-notification-api-jim.yaml). + +#### 4.3.5 DSP Dataset Representation + +The Certificate Consumer **MUST** expose a dataset according to the Dataspace Protocol (DSP) specification in their +catalog. This dataset **MUST** include the following properties: + +| | | +|-----------------|-------------------------------------------------------------------------------------------------------------------------------------------| +| **conformsTo**: | The Dublin Core [conformsTo](#dcmi-conformsto) property. The value must be set to `https://w3id.org/catenax/certificate-notification-api` | + +The catalog distribution **MUST** have its `format` value set to `https://w3id.org/dps/http-pull`. + +> TODO: Note this section is dependent on the following issues: +> - [DPS-99](#dps-99) + +The following is a non-normative example of a consumer catalog dataset: + +```json +{ + "@id": "urn:uuid:3dd1add8-4d2d-569e-d634-8394a8836a88", + "@type": "Dataset", + "hasPolicy": [], + "conformsTo": "https://w3id.org/catenax/certificate-notification-api", + "distribution": [ + { + "@type": "Distribution", + "format": "https://w3id.org/dps/http-pull", + "accessService": "urn:uuid:4aa2dcc8-4d2d-569e-d634-8394a8834d77" + } + ] +} +``` + +### 4.4 Certificate Provider API + +> *This section is normative* + +The Certificate Provider API enables Certificate Providers to accept certificate requests, report request fulfillment +status, serve certificate data, answer certificate queries, and receive acceptance status from Certificate Consumers. + +#### 4.4.1 Certificate Request + +The Certificate Provider **MUST** expose the following endpoint to allow a Certificate Consumer to request a +certificate. A request opens a consumer-initiated `Certificate Exchange` (see [Section 2.1](#21-certificate-exchange)). + +| | | +|------------------|-------------------------------------------------------| +| **HTTP Method**: | POST | +| **URL Path** | `POST /certificate-requests` | +| **Content Type** | `application/json` | +| **Request** | `CertificateRequest` | +| **Response** | `HTTP 202` with `CertificateRequestResponse` or error | + +The request body **MUST** be a JSON object containing the following properties: + +| Property | Type | Required | Description | +|-------------------|------------------|-----------|--------------------------------------------------------------------------------------------------------------------------------------------| +| `certificateType` | String | MANDATORY | An opaque string identifying the certificate type to be requested (for example, `ISO9001`). | +| `locationBpns` | Array of Strings | OPTIONAL | The Business Partner Numbers (BPNs) of the sites or addresses the request applies to. If omitted, the request applies to the legal entity. | + +When the Certificate Provider accepts the request, it **MUST** assign an `exchangeId`, `certificateId`, and `version` +and return them in the response, so the Certificate Consumer can correlate the exchange, poll its fulfillment status, +and later retrieve the certificate. The response body **MUST** be a JSON object containing the following properties: + +| Property | Type | Required | Description | +|-----------------|-----------------|-----------|---------------------------------------------------------------------------------------------------------------------------------------| +| `exchangeId` | String | MANDATORY | The identifier assigned to the opened `Certificate Exchange`. Used to poll status ([Section 4.4.2](#442-certificate-request-status)). | +| `certificateId` | String | MANDATORY | The identifier assigned to the requested certificate. Used to retrieve it via `GET /certificates/{id}`. | +| `version` | Integer | MANDATORY | The version assigned to the requested certificate. | +| `status` | String | MANDATORY | The fulfillment status of the request. **MUST** be one of `ACKNOWLEDGED`, `CERTIFICATION_REQUESTED`, `FULFILLED`, or `DECLINED`. | +| `errors` | Array of Object | OPTIONAL | A list of error details. **MUST** be present and non-empty when `status` is `DECLINED`. | + +The `status` reflects the Fulfillment phase of the exchange (see [Section 2.1.3](#213-state-machine)). A Certificate +Provider that already holds the requested certificate **MAY** return `FULFILLED` immediately. A Certificate Provider +that will not satisfy the request **MAY** return `DECLINED` with errors, or respond with `HTTP 400` if the request is +malformed. Each entry in the `errors` array contains the properties defined in +[Section 4.4.5](#445-certificate-acceptance-events). + +The following is a non-normative example of a certificate request: + +```json +{ + "certificateType": "ISO9001", + "locationBpns": [ + "BPNS00000003AYRE" + ] +} +``` + +The following is a non-normative example of a response (`HTTP 202`): + +```json +{ + "exchangeId": "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44", + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "version": 1, + "status": "ACKNOWLEDGED" +} +``` + +#### 4.4.2 Certificate Request Status + +The Certificate Provider **MUST** expose the following endpoint to allow a Certificate Consumer to poll the fulfillment +status of a request previously submitted via [Section 4.4.1](#441-certificate-request). + +| | | +|------------------|------------------------------------------| +| **HTTP Method**: | GET | +| **URL Path** | `GET /certificate-requests/{id}` | +| **Content Type** | `application/json` | +| **Response** | `CertificateRequestStatus` or `HTTP 404` | + +The `{id}` path parameter **MUST** be the `exchangeId` returned by the Certificate Provider in the response to the +certificate request. If the `exchangeId` is unknown to the Certificate Provider, the endpoint **MUST** respond with +`HTTP 404`. + +The response body **MUST** be a JSON object containing the following properties: + +| Property | Type | Required | Description | +|-----------------|-----------------|-----------|-------------------------------------------------------------------------------------------------------------------------------------| +| `exchangeId` | String | MANDATORY | The identifier of the `Certificate Exchange`. Matches the `{id}` path parameter of the request. | +| `certificateId` | String | MANDATORY | The unique identifier of the requested certificate. | +| `version` | Integer | MANDATORY | The version of the requested certificate. | +| `status` | String | MANDATORY | The current fulfillment status. **MUST** be one of `ACKNOWLEDGED`, `CERTIFICATION_REQUESTED`, `FULFILLED`, `DECLINED`, or `FAILED`. | +| `errors` | Array of Object | OPTIONAL | A list of error details. **MUST** be present and non-empty when `status` is `DECLINED` or `FAILED`. | + +When the `status` is `FULFILLED`, the certificate is available and **MAY** be retrieved via +[Section 4.4.3](#443-certificate-retrieval). This endpoint reports only the Fulfillment phase; the Certificate +Consumer's acceptance outcome is reported separately via [Section 4.4.5](#445-certificate-acceptance-events). + +Polling is not the only way to learn the Fulfillment status. If the Certificate Consumer exposes the Certificate +Consumer Notification API, a Certificate Provider **MAY** instead push these status updates to it as they occur (see +[Section 4.3.2](#432-certificate-fulfillment-notification)), in which case the consumer need not poll. The two mechanisms +are equivalent and carry the same `exchangeId` and `status`. + +The following are non-normative examples of request status responses: + +**Status: ACKNOWLEDGED** + +```json +{ + "exchangeId": "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44", + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "version": 1, + "status": "ACKNOWLEDGED" +} +``` + +**Status: FULFILLED** + +```json +{ + "exchangeId": "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44", + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "version": 1, + "status": "FULFILLED" +} +``` + +**Status: DECLINED** + +```json +{ + "exchangeId": "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44", + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "version": 1, + "status": "DECLINED", + "errors": [ + { + "message": "Certificate type not offered for the requested location" + } + ] +} +``` + +#### 4.4.3 Certificate Retrieval + +The Certificate Provider **MUST** expose the following endpoint to retrieve certificate metadata. + +| | | +|------------------|-------------------------------------------| +| **HTTP Method**: | GET | +| **URL Path** | `GET /certificates/{id}` | +| **Query** | `version` (OPTIONAL) — defaults to latest | +| **Content Type** | `application/json` | +| **Response** | `HTTP 200` with metadata JSON or error | + +The response is a JSON object ([RFC8259](#rfc8259)) describing the certificate. The certificate is metadata only; the +binary documents are **not** included. Each associated document is listed by reference in the `documents` array and +retrieved independently via [Section 4.4.4](#444-document-retrieval). + +If an error occurs, the Certificate Provider **MUST** set the `Content-Type` to `application/json` and return an error +body encoded as `application/json` ([RFC8259](#rfc8259)). + +By default, the endpoint returns the latest `version`. A consumer **MAY** request a specific version with the `version` +query parameter (for example, `GET /certificates/{id}?version=2`) — for instance, to retrieve the exact version a +`Certificate Exchange` concerns. The Certificate Provider **MUST** be able to return any version that is still +referenced by a non-terminal `Certificate Exchange`; whether other superseded versions remain retrievable is at the +provider's discretion. + +Each version references a set of documents through the `documents` array. Document identity is independent of the +certificate version: a `documentId` is stable and its content is immutable. A document that does not change across +versions is therefore referenced by the same `documentId` in each version that includes it. + +The metadata is a JSON object containing the following properties: + +| Property | Type | Required | Description | +|-------------------|------------------|-----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `certificateId` | String | MANDATORY | The unique identifier of the certificate. Matches the `{id}` path parameter of the request. | +| `version` | Integer | MANDATORY | The version returned — the version requested via the `version` query parameter, or the latest version if none was specified. | +| `certificateType` | String | MANDATORY | An opaque string identifying the certificate type (for example, `ISO9001`). | +| `validFrom` | String | MANDATORY | The inclusive start date of the certificate validity period, formatted as an [ISO 8601](#iso8601) full-date (`YYYY-MM-DD`). | +| `validUntil` | String | MANDATORY | The inclusive end date of the certificate validity period, formatted as an [ISO 8601](#iso8601) full-date (`YYYY-MM-DD`). | +| `locationBpns` | Array of Strings | OPTIONAL | The Business Partner Numbers (BPNs) of the sites or addresses the certificate applies to. If omitted, the certificate applies to the issuing legal entity as a whole. | +| `documents` | Array of Objects | OPTIONAL | The documents associated with this certificate version, each a `CertificateDocument` object (see below). If omitted or empty, the version has no associated documents. | + +Each `CertificateDocument` object contains the following properties: + +| Property | Type | Required | Description | +|--------------|--------|-----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `documentId` | String | MANDATORY | The opaque, version-independent identifier of the document, used to retrieve it via `GET /documents/{id}` (see [Section 4.4.4](#444-document-retrieval)). | +| `mediaType` | String | MANDATORY | The IANA media type of the document binary (for example, `application/pdf` or `image/png`). | + +The following is a non-normative example of a response: + +```json +{ + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "version": 1, + "certificateType": "ISO9001", + "validFrom": "2023-01-25", + "validUntil": "2026-01-24", + "locationBpns": [ + "BPNS00000003AYRE", + "BPNA000000000001" + ], + "documents": [ + { + "documentId": "doc-3fa85f64-5717-4562-b3fc-2c963f66afa6", + "mediaType": "application/pdf" + } + ] +} +``` + +#### 4.4.4 Document Retrieval + +The Certificate Provider **MUST** expose the following endpoint to retrieve a certificate document by its identifier. + +| | | +|------------------|----------------------------------------------| +| **HTTP Method**: | GET | +| **URL Path** | `GET /documents/{id}` | +| **Content Type** | The document's `mediaType` | +| **Response** | `HTTP 200` with the document binary or error | + +A document is identified by an opaque `documentId` that is independent of any certificate version (see +[Section 4.4.3](#443-certificate-retrieval)). Because a document's identity and content are stable, the same document +**MAY** be referenced by multiple certificate versions, and the endpoint therefore requires no `version` parameter. + +The response body is the document binary, served directly with the `Content-Type` set to the document's `mediaType` +(for example, `application/pdf`) as advertised in the certificate metadata +([Section 4.4.3](#443-certificate-retrieval)). + +If an error occurs, the Certificate Provider **MUST** set the `Content-Type` to `application/json` and return an error +body encoded as `application/json` ([RFC8259](#rfc8259)). + +The following is a non-normative example of a response: + +```http +HTTP/1.1 200 OK +Content-Type: application/pdf + +[binary document data] +``` + +#### 4.4.5 Certificate Acceptance Events + +The Certificate Provider **MUST** expose the following endpoint to receive certificate acceptance status from the +Certificate Consumer. The status is delivered as a `CertificateAcceptanceStatus` CloudEvent. + +| | | +|------------------|-------------------------------------------------------| +| **HTTP Method**: | POST | +| **URL Path** | `POST /certificate-acceptance-notifications` | +| **Content Type** | `application/cloudevents+json` | +| **Request** | `CertificateAcceptanceStatus` (single event or batch) | +| **Response** | `HTTP 204` with empty body or error | + +The endpoint accepts a single CloudEvent ([CloudEvents](#cloudevents)) or a batch (a JSON array) as defined in +[CX-0000](#cx-0000), whose HTTP binding follows [CloudEvents-HTTP](#cloudevents-http). + +**Event Type**: `org.catena-x.ccm.CertificateAcceptanceStatus.v1` + +The Certificate Provider **MUST** dispatch on the `data.status` value, which is a state of the Acceptance phase (see +[Section 2.1.3](#213-state-machine)). The event is correlated to its `Certificate Exchange` by the `exchangeId` carried +in `data` (see [Section 2.1.1](#211-identity-and-correlation)). + +Acceptance status **MUST** reference an existing `Certificate Exchange`. Acceptance is the Consumer-owned phase of an +exchange, so it can only be reported when an exchange is present — one opened by a consumer request +([Section 4.4.1](#441-certificate-request)) or by a provider notification +([Section 4.3.1](#431-certificate-lifecycle-events)). Merely retrieving a certificate (for example, after +a [query](#446-certificate-query)) does **not** establish an exchange and therefore does not permit acceptance feedback. +If the `exchangeId` is unknown to the Certificate Provider, it **MUST** reject the event with `HTTP 404`. + +Reporting `RETRIEVED` is **OPTIONAL** (see [Section 2.1.3](#213-state-machine)). It is a non-terminal delivery +acknowledgment; a Certificate Consumer **MAY** report it after fetching the certificate, or **MAY** proceed directly to +a terminal acceptance status (`ACCEPTED`, `REJECTED`, or `ERRORED`) without first reporting `RETRIEVED`. A Certificate +Provider **MUST NOT** require a prior `RETRIEVED` event as a precondition for accepting a terminal acceptance status. + +The `data` payload derives from the common certificate-status base payload and constrains `status` to the acceptance +values below. It contains the following properties: + +| Property | Type | Required | Description | +|-----------------|-----------------|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `exchangeId` | String | MANDATORY | Identifier of the `Certificate Exchange` this event reports on. Distinct from the CloudEvent `id`. | +| `certificateId` | String | MANDATORY | The unique identifier of the certificate the status applies to. | +| `status` | String | MANDATORY | The acceptance status of the certificate. **MUST** be one of `RETRIEVED`, `ACCEPTED`, `REJECTED`, or `ERRORED`. | +| `errors` | Array of Object | CONDITIONAL | A list of error details. **MUST NOT** be present when `status` is `RETRIEVED` or `ACCEPTED`. **MUST** be present and non-empty when `status` is `REJECTED` or `ERRORED`. | + +Each entry in the `errors` array contains the following properties: + +| Property | Type | Required | Description | +|-------------|--------|-----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `message` | String | MANDATORY | A human-readable description of the error. | +| `specifier` | String | OPTIONAL | An identifier scoping the error to a particular element of the certificate, such as a site BPN. If omitted, the error applies to the certificate as a whole. | + +The following are non-normative examples. + +**Status: RETRIEVED** + +```json +{ + "specversion": "1.0", + "type": "org.catena-x.ccm.CertificateAcceptanceStatus.v1", + "source": "urn:bpn:BPNL0000000002CD", + "subject": "BPNL0000000001AB", + "id": "e9f8d7c6-b5a4-9382-7160-5a4b3c2d1e0f", + "time": "2025-05-04T08:00:00Z", + "datacontenttype": "application/json", + "dataschema": "https://w3id.org/catenax/schemas/ccm/certificate-acceptance-status.json", + "data": { + "exchangeId": "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44", + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "status": "RETRIEVED" + } +} +``` + +**Status: ACCEPTED** + +```json +{ + "specversion": "1.0", + "type": "org.catena-x.ccm.CertificateAcceptanceStatus.v1", + "source": "urn:bpn:BPNL0000000002CD", + "subject": "BPNL0000000001AB", + "id": "a7b8c9d0-e1f2-3a4b-5c6d-7e8f9a0b1c2d", + "time": "2025-05-04T09:00:00Z", + "datacontenttype": "application/json", + "dataschema": "https://w3id.org/catenax/schemas/ccm/certificate-acceptance-status.json", + "data": { + "exchangeId": "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44", + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "status": "ACCEPTED" + } +} +``` + +**Status: REJECTED** + +```json +{ + "specversion": "1.0", + "type": "org.catena-x.ccm.CertificateAcceptanceStatus.v1", + "source": "urn:bpn:BPNL0000000002CD", + "subject": "BPNL0000000001AB", + "id": "f1a2b3c4-d5e6-7f8a-9b0c-1d2e3f4a5b6c", + "time": "2025-05-04T09:00:00Z", + "datacontenttype": "application/json", + "dataschema": "https://w3id.org/catenax/schemas/ccm/certificate-acceptance-status.json", + "data": { + "exchangeId": "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44", + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "status": "REJECTED", + "errors": [ + { + "message": "Certificate has expired" + }, + { + "message": "Unexpected data format" + }, + { + "specifier": "BPNS000000000002", + "message": "Site BPNS000000000002 has been Rejected" + } + ] + } +} +``` + +**Status: ERRORED** + +```json +{ + "specversion": "1.0", + "type": "org.catena-x.ccm.CertificateAcceptanceStatus.v1", + "source": "urn:bpn:BPNL0000000002CD", + "subject": "BPNL0000000001AB", + "id": "c8d7e6f5-a4b3-2109-8765-432109fedcba", + "time": "2025-05-04T08:30:00Z", + "datacontenttype": "application/json", + "dataschema": "https://w3id.org/catenax/schemas/ccm/certificate-acceptance-status.json", + "data": { + "exchangeId": "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44", + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "status": "ERRORED", + "errors": [ + { + "message": "Certificate document is malformed and cannot be validated" + } + ] + } +} +``` + +#### 4.4.6 Certificate Query + +The Certificate Provider **MUST** expose the following endpoint to query certificates. + +| | | +|------------------|----------------------------| +| **HTTP Method**: | POST | +| **URL Path** | `POST /certificates/search` | +| **Content Type** | `application/json` | +| **Request** | `CertificateQuery` | +| **Response** | `CertificateQueryResponse` | + +The request body **MUST** be a JSON object containing the query criteria. The `limit` property **MAY** be included to +indicate the maximum number of results to return per page. If omitted, the Certificate Provider **MAY** apply a default +limit. + +The `CertificateQuery` object contains the following properties: + +| Property | Type | Required | Description | +|-------------------|---------|-----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `certificateType` | String | MANDATORY | An opaque string identifying the certificate type to be matched (for example, `ISO9001`). | +| `from` | String | OPTIONAL | The inclusive lower bound of the certificate validity range, formatted as an [ISO 8601](#iso8601) full-date (`YYYY-MM-DD`). Only certificates whose `validFrom` is on or after this date are returned. | +| `to` | String | OPTIONAL | The inclusive upper bound of the certificate validity range, formatted as an [ISO 8601](#iso8601) full-date (`YYYY-MM-DD`). Only certificates whose `validUntil` is on or before this date are returned. | +| `limit` | Integer | OPTIONAL | The maximum number of results to return in a single page. If omitted, the Certificate Provider **MAY** apply a default limit. | + +The following is a non-normative example of a certificate query: + +```json +{ + "certificateType": "ISO9001", + "from": "2023-01-25", + "to": "2026-01-24", + "limit": 50 +} +``` + +The response body **MUST** be a JSON array of `CertificateQueryResponse` objects containing match results. Each object +contains the following properties: + +| Property | Type | Required | Description | +|-------------------|------------------|-----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `certificateId` | String | MANDATORY | The unique identifier of the certificate, used to retrieve the certificate via `GET /certificates/{id}`. | +| `version` | Integer | MANDATORY | The latest version of the certificate. | +| `datasetId` | String | MANDATORY | The identifier of the DSP dataset under which the certificate is exposed in the Certificate Provider's catalog. | +| `certificateType` | String | MANDATORY | An opaque string identifying the certificate type (for example, `ISO9001`). | +| `validFrom` | String | MANDATORY | The inclusive start date of the certificate validity period, formatted as an [ISO 8601](#iso8601) full-date (`YYYY-MM-DD`). | +| `validUntil` | String | MANDATORY | The inclusive end date of the certificate validity period, formatted as an [ISO 8601](#iso8601) full-date (`YYYY-MM-DD`). | +| `locationBpns` | Array of Strings | OPTIONAL | The Business Partner Numbers (BPNs) of the sites or addresses the certificate applies to. If omitted, the certificate applies to the issuing legal entity as a whole. | +| `documents` | Array of Objects | OPTIONAL | The documents associated with the returned certificate version, each a `CertificateDocument` object as defined in [Section 4.4.3](#443-certificate-retrieval). Retrieved via `GET /documents/{id}`. | + +The following is a non-normative example of a certificate query response containing an array of +`CertificateQueryResponse` objects: + +```json +[ + { + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "version": 1, + "datasetId": "dataset-ccm-cert-abc123", + "certificateType": "ISO9001", + "validFrom": "2023-01-25", + "validUntil": "2026-01-24", + "locationBpns": [ + "BPNS00000003AYRE", + "BPNA000000000001" + ], + "documents": [ + { + "documentId": "doc-3fa85f64-5717-4562-b3fc-2c963f66afa6", + "mediaType": "application/pdf" + } + ] + } +] +``` + +##### 4.4.6.1 Pagination + +Implementations **MAY** paginate the results of a query. When results are paginated, pagination data **MUST** be +conveyed using Web Linking as defined in [RFC8288](#rfc8288) via the HTTP `Link` response header. Only the `next` +and `prev` link relation types **MUST** be supported; implementations **MAY** include additional relation types such as +`first` and `last`. + +Each value in the `Link` header **MUST** be enclosed in angle brackets and **MUST** include a `rel` parameter +identifying the link relation type. When more than one link is present, values **MUST** be comma-separated as defined +in [RFC8288, Section 3](#rfc8288). The target URL **MUST** refer to the +`POST /certificates/search` endpoint of the Certificate Provider. The content and structure of the URL (including any +query parameters or opaque page tokens used to encode pagination state) are not defined by this specification and +clients **MUST** treat them as opaque. + +To retrieve the next or previous page, a client **MUST** issue a `POST` request to the URL provided in the corresponding +`Link` header value, using the same request body as the original query. The `Link` header **MUST NOT** be included in +responses that are not paginated, and a `next` or `prev` link **MUST** be omitted when no further results are available +in that direction. + +The following is a non-normative example of a paginated response: + +```http +HTTP/1.1 200 OK +Content-Type: application/json +Link: ; rel="next", + ; rel="prev" + +[ + { + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "version": 1, + "datasetId": "dataset-ccm-cert-abc123", + "certificateType": "ISO9001", + "validFrom": "2023-01-25", + "validUntil": "2026-01-24", + "locationBpns": [ + "BPNS00000003AYRE", + "BPNA000000000001" + ] + } +] +``` + +#### 4.4.7 OpenAPI Specification + +The Certificate Provider API is formally defined by the OpenAPI document +[certificate-provider-api-jim.yaml](./certificate-provider-api-jim.yaml). + +#### 4.4.8 DSP Dataset Representation + +The Certificate Provider **MUST** expose a dataset according to the Dataspace Protocol (DSP) specification in their +catalog. This dataset **MUST** include the following properties: + +| | | +|-----------------|------------------------------------------------------------------------------------------------------------------------------| +| **conformsTo**: | The Dublin Core [conformsTo](#dcmi-conformsto) property. The value must be set to `https://w3id.org/catenax/certificate-api` | + +The catalog distribution **MUST** have its `format` value set to `https://w3id.org/dps/http-pull`. + +> TODO: Note this section is dependent on the following issues: +> - [DPS-98](#dps-98) +> - [DPS-99](#dps-99) + +The following is a non-normative example of a provider catalog dataset: + +```json +{ + "@id": "urn:uuid:3dd1add8-4d2d-569e-d634-8394a8836a88", + "@type": "Dataset", + "hasPolicy": [], + "conformsTo": "https://w3id.org/catenax/certificate-api", + "distribution": [ + { + "@type": "Distribution", + "format": "https://w3id.org/dps/http-pull", + "accessService": "urn:uuid:4aa2dcc8-4d2d-569e-d634-8394a8834d77" + } + ] +} +``` + +## 5 References + +### 5.1 Normative References + + +**[CX-0000]** Catena-X, "CloudEventsFoundation", +[CX-0000-CloudEventsFoundation.md](./CX-0000-CloudEventsFoundation-jim.md). + + +**[CX-0151]** Catena-X, "Industry Core: Part Type and Part Instance". + + +**[CloudEvents]** Cloud Native Computing Foundation, "CloudEvents 1.0.2 — Core Specification", +. + + +**[CloudEvents-HTTP]** Cloud Native Computing Foundation, "HTTP Protocol Binding for CloudEvents 1.0.2", +. + + +**[DCMI-conformsTo]** Dublin Core Metadata Initiative, "DCMI Metadata Terms — conformsTo", +. + + +**[DSP]** Eclipse Dataspace Working Group, "Dataspace Protocol 2025-1", +. + + +**[ISO8601]** International Organization for Standardization, "ISO 8601-1:2019, Date and time — Representations for +information interchange — Part 1: Basic rules", . + + +**[RFC2119]** Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997, +. + + +**[RFC8174]** Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, May 2017, +. + + +**[RFC8259]** Bray, T., Ed., "The JavaScript Object Notation (JSON) Data Interchange Format", STD 90, RFC 8259, December +2017, . + + +**[RFC8288]** Nottingham, M., "Web Linking", RFC 8288, October 2017, . + +### 5.2 Non-Normative References + + +**[DSP-Catalog]** Eclipse Dataspace Working Group, "Dataspace Protocol 2025-1 — Catalog Protocol", +. + + +**[DPS-98]** Eclipse Data Plane Signaling, "Issue #98", +. + + +**[DPS-99]** Eclipse Data Plane Signaling, "Issue #99", +. diff --git a/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement-combined.md b/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement-combined.md new file mode 100644 index 0000000000..f1b1876ff3 --- /dev/null +++ b/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement-combined.md @@ -0,0 +1,1582 @@ +--- +tags: + - CAT/Value Added Services +--- + +# CX-0135 Business Partner Company Certificate Management (CCM) v0.1.0 + +## ABSTRACT + +In the world of business, company certificates are often mandatory for conducting transactions between two companies. +However, the process of provisioning, maintaining, and validating these certificates can be a major challenge. For +example, if a company has 100 customers, it may need to provide its company certificates in 100 different ways and +maintain them at 100 different points. + +To address this, this standard defines a standardized but generic data model and a wire protocol for company +certificates. It allows Catena-X participants to provide, request, accept, or reject company certificates across the +dataspace, exchanging certificate data as [CloudEvents](#cloudevents) over the [Dataspace Protocol](#dsp). + +## FOR WHOM IS THE STANDARD DESIGNED + +This standard is relevant to the following parties: + +- Data Provider and Consumer +- Business Application Provider + +It applies to business application providers who aim to offer a solution for managing and exchanging company +certificates and returning them to customers, and to Data Providers and Consumers who manage and exchange certificates +through such a business application. + +> **Context regarding the naming of involved parties:** +> The Catena-X operating model and the [Dataspace Protocol](#dsp) use the roles `Data Consumer` and `Data Provider`, +> defined in terms of which party offers and consumes a dataset. In some cases these align with the direction of +> certificate flow, but — particularly for the certificate push mechanism — a mismatch can occur. To avoid ambiguity, +> this standard uses the terms `Certificate Provider` and `Certificate Consumer` to designate the business entities, +> independently of the data-plane roles they assume for a given interaction. + +## 1 Introduction + +This specification builds upon [CX-0151](#cx-0151) to define a Company Certificate Management (CCM) wire protocol for +exchanging company certificate data between Catena-X participants. + +### 1.1 Conformance and Proof Of Conformity + +The keywords **MAY**, **MUST**, **MUST NOT**, **OPTIONAL**, **RECOMMENDED**, **REQUIRED**, **SHOULD**, and **SHOULD +NOT** in this document are to be interpreted as described in BCP 14 [RFC2119](#rfc2119) [RFC8174](#rfc8174) when, and +only when they appear in all capitals, as shown here. + +## 2 Model + +> *This section is NOT normative* + +This section describes the conceptual model underpinning the specification wire protocol. It is background material: the +model introduces the entities and lifecycle that the normative sections build upon, but defines no normative +requirements of its own. + +The model consists of two concepts: + +- The `Certificate Exchange` ([Section 2.1](#21-certificate-exchange)) — a single, correlatable interaction in which one + certificate is delivered from a Certificate Provider to a Certificate Consumer and its outcome is reported back. A + `Certificate Exchange` has its own identity and a well-defined lifecycle. +- The `Certificate Lifecycle` ([Section 2.2](#22-certificate-lifecycle)) — the independent lifecycle of a certificate as + an artifact (its creation, modification, and revocation). + +### 2.1 Certificate Exchange + +A `Certificate Exchange` represents one end-to-end interaction between a Certificate Provider and a Certificate Consumer +involving the delivery of a single certificate from the time the interaction is started until it reaches a terminal +outcome. + +#### 2.1.1 Identity and Correlation + +A `Certificate Exchange` is identified by an **`exchangeId`** assigned when it is started. The `exchangeId` is the +correlation handle for the entire interaction and is distinct from the identifier of any individual message or event +(for example, a CloudEvent `id` + `source`). + +An exchange concerns a specific certificate, a (`certificateId`, `version`) pair, and is conducted with a +`counterparty`, the authenticated Certificate Consumer. + +A `Certificate Exchange` may be opened by the Certificate Consumer or Certificate Provider: + +- **Consumer-initiated (pull):** the Certificate Consumer opens the exchange by requesting a certificate. The + Certificate Provider assigns the `exchangeId` (together with the `certificateId` and `version`) and returns them in + the response. Every request opens an exchange — including one the provider declines synchronously, which opens an + exchange that terminates immediately at `DECLINED`; the identifiers are still returned so the outcome remains + correlatable. +- **Provider-initiated (push):** the Certificate Provider opens the exchange when a certificate is already available, + assigning an `exchangeId` and carrying it — with the `certificateId` and `version` — in the notification. + +**Idempotency.** Each `Certificate Exchange` has a unique `exchangeId`. A message that repeats an `exchangeId` refers to +the same exchange rather than opening a new one. Multiple exchanges **MAY** concern the same certificate and +`counterparty`. + +Acceptance feedback is correlated to the exchange by its `exchangeId` for both flows. + +A re-attempt after a terminal outcome — for example, re-evaluating a `REJECTED` or `ERRORED` certificate — is a new +`Certificate Exchange`, with a new `exchangeId`, for the same certificate. + +#### 2.1.2 Phases and Ownership + +A `Certificate Exchange` progresses through two sequential phases, each owned by one party: + +1. **Fulfillment (Provider-owned):** The Certificate Provider works to make the certificate available. +2. **Acceptance (Consumer-owned):** The Certificate Consumer retrieves and processes the certificate and reports the + outcome. + +The phases never overlap: the `Acceptance` phase begins only once `Fulfillment` has made the certificate available. +Provider-owned and Consumer-owned states use deliberately disjoint vocabulary, so the owner of a state is unambiguous. +Each phase can end in a negative decision or a business error: `DECLINED`/`REJECTED` are decisions (the provider +declines the request, or the consumer does not accept the certificate), while `FAILED`/`ERRORED` indicate a business +error such as an invalid certificate. These error states represent business-level problems only; technical and transport +failures (for example, connectivity errors or timeouts) are not modeled as `Certificate Exchange` states and are handled +at the transport layer. + +#### 2.1.3 State Machine + +The states of a `Certificate Exchange` are defined below and use the past tense: + +```mermaid +stateDiagram-v2 + [*] --> REQUESTED + REQUESTED --> ACKNOWLEDGED: provider accepts & starts preparing + REQUESTED --> DECLINED: provider declines the request + ACKNOWLEDGED --> CERTIFICATION_REQUESTED: submitted to external authority + ACKNOWLEDGED --> FULFILLED: certificate available (already held) + ACKNOWLEDGED --> FAILED: cannot produce a valid certificate + CERTIFICATION_REQUESTED --> FULFILLED: certified & available + CERTIFICATION_REQUESTED --> DECLINED: authority declines + CERTIFICATION_REQUESTED --> FAILED: certification invalid + FULFILLED --> RETRIEVED: consumer reports retrieval (OPTIONAL) + FULFILLED --> ACCEPTED: consumer accepts + FULFILLED --> REJECTED: consumer does not accept content + FULFILLED --> ERRORED: certificate invalid + RETRIEVED --> ACCEPTED: consumer accepts + RETRIEVED --> REJECTED: consumer does not accept content + RETRIEVED --> ERRORED: certificate invalid + DECLINED --> [*] + FAILED --> [*] + ACCEPTED --> [*] + REJECTED --> [*] + ERRORED --> [*] +``` + +| State | Phase / Owner | Terminal | Description | +|---------------------------|------------------------|----------|-------------------------------------------------------------------------------------------------------------------| +| `REQUESTED` | Fulfillment / Provider | No | The exchange was opened by the consumer; the provider has not yet acted. *(Consumer-initiated exchanges only.)* | +| `ACKNOWLEDGED` | Fulfillment / Provider | No | The provider accepted the request and began preparing the certificate. | +| `CERTIFICATION_REQUESTED` | Fulfillment / Provider | No | The provider submitted the request to an external certification authority and is awaiting issuance. *(Optional.)* | +| `FULFILLED` | Fulfillment / Provider | No | The certificate is prepared and available for retrieval. Hand-off point. | +| `DECLINED` | Fulfillment / Provider | Yes | The provider declined the request (a business decision; e.g. the certificate type is not offered). | +| `FAILED` | Fulfillment / Provider | Yes | The provider could not produce a valid certificate (a business error; e.g. the certificate is invalid). | +| `RETRIEVED` | Acceptance / Consumer | No | The consumer fetched the certificate and is processing it. *(Optional — see the note below.)* | +| `ACCEPTED` | Acceptance / Consumer | Yes | The consumer accepted the certificate. | +| `REJECTED` | Acceptance / Consumer | Yes | The consumer did not accept the certificate content (a business decision). | +| `ERRORED` | Acceptance / Consumer | Yes | The consumer found the certificate to be in error (a business error; e.g. the certificate is invalid). | + +A provider-initiated (push) exchange enters the lifecycle directly at `FULFILLED`. There is no request, so `REQUESTED`, +`ACKNOWLEDGED` and `CERTIFICATION_REQUESTED` are never visited. The Acceptance phase is identical for both pull and +push. + +`RETRIEVED` is **OPTIONAL**. It is a non-terminal acknowledgment that the consumer has fetched the certificate and is +evaluating it; the consumer **MAY** report it as a delivery receipt but is not required to. An exchange therefore reaches +a terminal acceptance state either by way of `RETRIEVED` (`FULFILLED → RETRIEVED → {ACCEPTED, REJECTED, ERRORED}`) or +directly from `FULFILLED` (`FULFILLED → {ACCEPTED, REJECTED, ERRORED}`). The terminal acceptance verdicts remain +Consumer-owned in both cases. + +`REQUESTED` is instantaneous and internal to the Certificate Provider; it is never reported on the wire. The first +Fulfillment status a Certificate Consumer observes is the one carried in the request response (see +[Section 4.4.1](#441-certificate-request)), which is `ACKNOWLEDGED` or a later state. A provider that can satisfy a +request without intermediate steps **MAY** report a later Fulfillment state directly — for example `FULFILLED` when the +certificate is already held, or `CERTIFICATION_REQUESTED` when the request is immediately forwarded to an external +authority — having passed through the earlier states instantaneously. A reported `status` therefore reflects the +exchange's current state, not necessarily a single transition from the one before it. + +#### 2.1.4 Terminal States and Immutability + +A `Certificate Exchange` is single-shot. The five terminal states — `DECLINED`, `FAILED`, `ACCEPTED`, `REJECTED`, and +`ERRORED` — conclude the exchange permanently. A terminal exchange is never reopened, reused, or transitioned further. + +#### 2.1.5 Relationship to the Certificate Lifecycle + +A `Certificate Exchange` governs the *delivery* of a certificate, not the certificate itself. Modifications, updates, +and revocations of a certificate are changes to the certificate artifact and are **not** transitions of a +`Certificate Exchange`. In particular, a change to a certificate that has already been delivered does not reopen or +alter the (possibly terminal) exchange that delivered it. A modification revises the certificate in place under the same +`certificateId` and does not open a new `Certificate Exchange`. The certificate's own lifecycle is described in +[Section 2.2](#22-certificate-lifecycle). + +### 2.2 Certificate Lifecycle + +The `Certificate Lifecycle` tracks a certificate as an artifact over time, independently of how many times it is +delivered. Whereas a `Certificate Exchange` is single-shot and concerns one delivery interaction, a certificate is +long-lived: it may be published, revised, and eventually withdrawn. + +#### 2.2.1 Identity and Versioning + +A certificate is identified by a **`certificateId`** that is stable for the life of the certificate. A modification does +not create a new identifier; instead, the certificate carries a **`version`**: + +- `CREATED` makes the first `version` of the certificate available for retrieval. +- Each `MODIFIED` publishes a new `version` under the same `certificateId`, superseding the previous one. The latest + `version` is authoritative. +- A certificate is therefore identified by the pair (`certificateId`, `version`). A `Certificate Exchange` delivers one + specific `version`. + +The `certificateId` and the initial `version` number may be assigned before the certificate is published. When a +Certificate Provider accepts a consumer's request and produces the certificate only later (see +[Section 2.1.1](#211-identity-and-correlation)), the identifier is allocated at acceptance so that an in-progress +`Certificate Exchange` can reference its certificate. Publication (`CREATED`) then occurs when the certificate becomes +available. + +A certificate covers a **fixed set of locations** — the sites (BPNS) and addresses (BPNA) it applies to. On the full +certificate record these are carried as structured `enclosedSites` (each with an optional per-location area of application; +see [Section 4.4.3](#443-certificate-retrieval)); lighter projections such as query results and lifecycle events carry +only the bare BPNs as `enclosedSiteBpns`. The location set is a static property of the certificate; a certificate that would +cover a different set of locations is a distinct certificate. + +#### 2.2.2 State Machine + +The states use the past tense and describe the *publication* lifecycle of the certificate. They are independent of the +certificate's validity (see [Section 2.2.3](#223-validity-as-a-separate-dimension)). + +```mermaid +stateDiagram-v2 + [*] --> CREATED + CREATED --> MODIFIED: new version published + MODIFIED --> MODIFIED: subsequent version published + CREATED --> WITHDRAWN: provider withdraws the certificate + MODIFIED --> WITHDRAWN: provider withdraws the certificate + WITHDRAWN --> [*] +``` + +| State | Terminal | Description | +|-------------|----------|---------------------------------------------------------------------------------------------------------------------------| +| `CREATED` | No | The certificate was first published under a new `certificateId`, establishing its initial `version`. | +| `MODIFIED` | No | A new `version` of the certificate was published under the same `certificateId`; the `version` is incremented. May recur. | +| `WITHDRAWN` | Yes | The provider withdrew (removed) the certificate; it is no longer available. Terminal. | + +#### 2.2.3 Validity as a Separate Dimension + +A certificate's validity is an independent dimension derived from its `validFrom` and `validUntil` dates, not from the +publication states defined above. A given `version` is *active* within its validity window and *expired* afterward, and +this status changes with the passage of time alone — no lifecycle transition occurs. The two dimensions are orthogonal: +a certificate may be `WITHDRAWN` while still within its validity window, or remain `CREATED`/`MODIFIED`after it has +expired. + +#### 2.2.4 Relationship to the `Certificate Exchange` + +Of the publication events, only `CREATED` may open a new `Certificate Exchange` — pushed by the provider, or discovered +and requested by the consumer — to deliver the certificate. `MODIFIED` and `WITHDRAWN` do not open an exchange: a +modification revises the certificate in place under the same `certificateId` without initiating a new delivery, and a +withdrawal ends the certificate's availability. Consistent with +[Section 2.1.5](#215-relationship-to-the-certificate-lifecycle), a lifecycle transition never alters an existing +`Certificate Exchange`. These transitions are communicated to Certificate Consumers as certificate lifecycle events (see +[Section 4.3.1](#431-certificate-lifecycle-events)). + +## 3 Data Plane Wire Protocol + +> *This section is NOT normative* + +This specification defines the following agent systems: + +- **Certificate Consumer Control Plane (Cert CCP)**: The [DSP](#dsp) Control Plane operated by the participant that + consumes certificates. +- **Certificate Consumer Data Plane (Cert CDP)**: The [DSP](#dsp) Data Plane operated by the participant that consumes + certificates. +- **Certificate Provider Control Plane (Cert PCP)**: The [DSP](#dsp) Control Plane operated by the participant that + provides certificates. +- **Certificate Provider Data Plane (Cert PDP)**: The [DSP](#dsp) Data Plane operated by the participant that provides + certificates. + +The wire transmission protocol supports an optional consumer endpoint for providers to send notifications of certificate +availability. Provider endpoints offer mechanisms for requesting, querying, retrieving, and reporting the status of +certificates. The endpoints are discoverable and made accessible via a [DSP Catalog](#dsp-catalog). + +```mermaid +sequenceDiagram + participant CDP as Cert CDP + participant CCP as Cert CCP + participant PCP as Cert PCP + participant PDP as Cert PDP + + rect rgb(208, 208, 209, .5) + PCP --> CCP: 1. Negotiate event API contract (DSP) + PDP ->> CDP: 2. Certificate lifecycle event (Created / Modified / Withdrawn) + end + CCP --> PCP: 3. Negotiate or reuse cert API contract (DSP) + CDP ->> PDP: 4. Request cert (returns exchangeId + certificateId + version) + CDP ->> PDP: 5. Poll fulfillment status (ACKNOWLEDGED, CERTIFICATION_REQUESTED, FULFILLED, DECLINED, FAILED) + PDP ->> CDP: 5a. (optional) Push fulfillment status to Notification API + CDP ->> PDP: 6. Retrieve cert + CDP ->> PDP: 7. Acceptance status (RETRIEVED, ACCEPTED, REJECTED, ERRORED) +``` + +### 3.1 Certificate Notification + +A Certificate Consumer may optionally make a [Certificate Consumer Notification API](#43-certificate-consumer-notification-api) +available to Certificate Providers. Through it, a provider may notify the consumer of certificate lifecycle changes +(creation, modification, and withdrawal) and may push the fulfillment status of an open consumer-initiated exchange +instead of requiring the consumer to poll (see [Section 4.3.2](#432-certificate-fulfillment-notification)). This API +functions as a DSP Data Plane, which Certificate Providers gain access to via the DSP protocol. Upon receiving a +lifecycle notification with `status` `CREATED` or `MODIFIED`, the Certificate Consumer may retrieve the certificate +using the [Certificate Provider API](#44-certificate-provider-api). + +### 3.2 Certificate Retrieval + +A Certificate Consumer retrieves a certificate using the [Certificate Provider API](#44-certificate-provider-api). This API functions as a DSP Data +Plane, which Certificate Consumers gain access to via the DSP protocol. The Certificate Consumer may request a +certificate, poll the fulfillment status of a request, query for certificates, get a certificate's metadata, retrieve +its individual documents, and post acceptance status updates. + +## 4 Data Plane Wire Protocol APIs + +> *This section is normative* + +### 4.1 Base URL and Transport Security + +The notation indicates the base URL for all HTTPS endpoints. For example, if the base URL is api.example.com, the +URL https:///certificates/123 will map to https//api.example.com/certificates/123. + +All endpoints **MUST** use HTTPS, HTTP over TLS 1.2 or higher. + +### 4.2 Authorization + +Authorization **MUST** adhere to [CX-0000 Section 4](./CX-0000-CloudEventsFoundation-combined.md#4-https-binding). + +### 4.3 Certificate Consumer Notification API + +The Certificate Consumer Notification API enables a Certificate Consumer to receive notifications from Certificate +Providers: certificate lifecycle events (see [Section 4.3.1](#431-certificate-lifecycle-events)) and fulfillment-status +updates for its open exchanges (see [Section 4.3.2](#432-certificate-fulfillment-notification)). This API is optional; a Certificate +Consumer that does not wish to receive notifications is not required to implement it. A Certificate Consumer that does +implement it **MUST** adhere to the normative requirements defined in this section. + +#### 4.3.1 Certificate Lifecycle Events + +The Certificate Consumer **MUST** expose the following endpoint to receive certificate lifecycle events. These events +are sent by the Certificate Provider to notify the Certificate Consumer of changes to a certificate over its lifecycle +(see [Section 2.2](#22-certificate-lifecycle)). + +| | | +|------------------|------------------------------------------------------| +| **HTTP Method**: | POST | +| **URL Path** | `POST /certificate-notifications` | +| **Content Type** | `application/cloudevents+json` | +| **Request** | `CertificateLifecycleStatus` (single event or batch) | +| **Response** | `HTTP 204` with empty body or error | + +The endpoint accepts a single CloudEvent ([CloudEvents](#cloudevents)) or a batch (a JSON array) as defined in +[CX-0000](#cx-0000), whose HTTP binding follows [CloudEvents-HTTP](#cloudevents-http). + +**Event Type**: `org.catena-x.ccm.CertificateLifecycleStatus.v1` + +The Certificate Consumer **MUST** dispatch on the `data.status` value, which maps to a state of the +`Certificate Lifecycle`: + +| `status` | Lifecycle State | Opens an Exchange | Description | +|-------------|-----------------|-------------------|------------------------------------------------------------------------------------------------------------------------------| +| `CREATED` | `CREATED` | Yes | A certificate has been published and is available for retrieval. | +| `MODIFIED` | `MODIFIED` | No | A new `version` of an existing certificate has been published; the Certificate Consumer **MAY** retrieve the latest version. | +| `WITHDRAWN` | `WITHDRAWN` | No | The certificate has been withdrawn and is no longer available. | + +Only the `CREATED` status opens a `Certificate Exchange` (see +[Section 2.2.4](#224-relationship-to-the-certificate-exchange)); `MODIFIED` and `WITHDRAWN` are notifications that do +not open an exchange. The two state machines meet here: a `CREATED` lifecycle event opens a provider-initiated exchange +that enters directly at the `FULFILLED` exchange state (see [Section 2.1.3](#213-state-machine)) — both express that the +certificate is now available, from the artifact and delivery viewpoints respectively. + +The `data` payload derives from the common certificate-status base payload and constrains `status` to the lifecycle +values above. It contains the following properties: + +| Property | Type | Required | Description | +|-------------------|------------------|-------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `exchangeId` | String | CONDITIONAL | Identifier of the `Certificate Exchange` opened by this event. Present when `status` is `CREATED`; absent for `MODIFIED` and `WITHDRAWN`. Distinct from the CloudEvent `id`. | +| `certificateId` | String | MANDATORY | The unique identifier of the certificate, used to retrieve the certificate via `GET /certificates/{id}`. | +| `version` | Integer | MANDATORY | The version of the certificate. Together with `certificateId` it identifies the specific certificate (see [Section 2.2.1](#221-identity-and-versioning)). | +| `status` | String | MANDATORY | The lifecycle status. **MUST** be one of `CREATED`, `MODIFIED`, or `WITHDRAWN`. | +| `datasetId` | String | MANDATORY | The identifier of the DSP dataset under which the certificate is exposed in the Certificate Provider's catalog, enabling the Certificate Consumer to locate and negotiate the certificate via the Dataspace Protocol. | +| `certificateType` | String | MANDATORY | An opaque string identifying the certificate type (for example, `ISO9001`). | +| `validFrom` | String | CONDITIONAL | The inclusive start date of the validity period, an [ISO 8601](#iso8601) full-date (`YYYY-MM-DD`). **MUST** be present when `status` is `CREATED` or `MODIFIED`; **MAY** be omitted when `status` is `WITHDRAWN`. | +| `validUntil` | String | CONDITIONAL | The inclusive end date of the validity period, an [ISO 8601](#iso8601) full-date (`YYYY-MM-DD`). Same presence rules as `validFrom`. | +| `enclosedSiteBpns` | Array of Strings | OPTIONAL | The Business Partner Numbers (BPNs) of the sites or addresses the certificate applies to. If omitted, the certificate applies to the issuing legal entity as a whole. | + +The following are non-normative examples. + +**Status: CREATED** + +```json +{ + "specversion": "1.0", + "type": "org.catena-x.ccm.CertificateLifecycleStatus.v1", + "source": "urn:bpn:BPNL0000000001AB", + "subject": "BPNL0000000002CD", + "id": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d", + "time": "2025-05-04T07:00:00Z", + "datacontenttype": "application/json", + "dataschema": "https://w3id.org/catenax/schemas/ccm/certificate-lifecycle-status.json", + "data": { + "exchangeId": "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44", + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "version": 1, + "status": "CREATED", + "datasetId": "dataset-ccm-cert-abc123", + "certificateType": "ISO9001", + "validFrom": "2023-01-25", + "validUntil": "2026-01-24", + "enclosedSiteBpns": [ + "BPNS00000003AYRE", + "BPNA000000000001" + ] + } +} +``` + +**Status: MODIFIED** + +```json +{ + "specversion": "1.0", + "type": "org.catena-x.ccm.CertificateLifecycleStatus.v1", + "source": "urn:bpn:BPNL0000000001AB", + "subject": "BPNL0000000002CD", + "id": "b2c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e", + "time": "2025-08-01T07:00:00Z", + "datacontenttype": "application/json", + "dataschema": "https://w3id.org/catenax/schemas/ccm/certificate-lifecycle-status.json", + "data": { + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "version": 2, + "status": "MODIFIED", + "datasetId": "dataset-ccm-cert-abc123", + "certificateType": "ISO9001", + "validFrom": "2023-01-25", + "validUntil": "2027-01-24", + "enclosedSiteBpns": [ + "BPNS00000003AYRE", + "BPNA000000000001" + ] + } +} +``` + +**Status: WITHDRAWN** + +```json +{ + "specversion": "1.0", + "type": "org.catena-x.ccm.CertificateLifecycleStatus.v1", + "source": "urn:bpn:BPNL0000000001AB", + "subject": "BPNL0000000002CD", + "id": "c3d4e5f6-7a8b-9c0d-1e2f-3a4b5c6d7e8f", + "time": "2025-09-01T07:00:00Z", + "datacontenttype": "application/json", + "dataschema": "https://w3id.org/catenax/schemas/ccm/certificate-lifecycle-status.json", + "data": { + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "version": 2, + "status": "WITHDRAWN", + "datasetId": "dataset-ccm-cert-abc123", + "certificateType": "ISO9001" + } +} +``` + +**Batch** + +Multiple events **MAY** be delivered in a single request as a JSON array: + +```json +[ + { + "specversion": "1.0", + "type": "org.catena-x.ccm.CertificateLifecycleStatus.v1", + "source": "urn:bpn:BPNL0000000001AB", + "subject": "BPNL0000000002CD", + "id": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d", + "time": "2025-05-04T07:00:00Z", + "datacontenttype": "application/json", + "dataschema": "https://w3id.org/catenax/schemas/ccm/certificate-lifecycle-status.json", + "data": { + "exchangeId": "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44", + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "version": 1, + "status": "CREATED", + "datasetId": "dataset-ccm-cert-abc123", + "certificateType": "ISO9001", + "validFrom": "2023-01-25", + "validUntil": "2026-01-24" + } + }, + { + "specversion": "1.0", + "type": "org.catena-x.ccm.CertificateLifecycleStatus.v1", + "source": "urn:bpn:BPNL0000000001AB", + "subject": "BPNL0000000002CD", + "id": "d4e5f6a7-8b9c-0d1e-2f3a-4b5c6d7e8f90", + "time": "2025-09-01T07:00:00Z", + "datacontenttype": "application/json", + "dataschema": "https://w3id.org/catenax/schemas/ccm/certificate-lifecycle-status.json", + "data": { + "certificateId": "cert-770e8400-e29b-41d4-a716-446655449999", + "version": 3, + "status": "WITHDRAWN", + "datasetId": "dataset-ccm-cert-xyz789", + "certificateType": "ISO14001" + } + } +] +``` + +#### 4.3.2 Certificate Fulfillment Notification + +When a Certificate Consumer exposes this Notification API, a Certificate Provider **MAY** push the Fulfillment status of +an open consumer-initiated exchange (see [Section 4.4.1](#441-certificate-request)) to the consumer instead of requiring +it to poll (see [Section 4.4.2](#442-certificate-request-status)). This is the push counterpart of that poll and is +particularly useful for long-running fulfillment, for example while a certificate is `CERTIFICATION_REQUESTED`. + +The event is delivered to the same `POST /certificate-notifications` endpoint defined in +[Section 4.3.1](#431-certificate-lifecycle-events) and is distinguished by its CloudEvents `type`. It is correlated to the +exchange by the `exchangeId` carried in `data`; the `exchangeId` is the one the Certificate Provider returned when the +exchange was opened (see [Section 4.4.1](#441-certificate-request)). + +This notification reports only the Fulfillment phase. It is independent of the certificate's lifecycle status: a +`FULFILLED` notification reports that the requested exchange has been fulfilled and the certificate is available for +retrieval, whereas a lifecycle `CREATED` event (see [Section 4.3.1](#431-certificate-lifecycle-events)) reports a +provider-initiated exchange. A Certificate Provider **MUST NOT** use a lifecycle event to report the outcome of a +consumer-initiated request. + +**Event Type**: `org.catena-x.ccm.CertificateFulfillmentStatus.v1` + +The `data` payload contains the following properties: + +| Property | Type | Required | Description | +|-----------------|-----------------|-------------|-----------------------------------------------------------------------------------------------------------------------------| +| `exchangeId` | String | MANDATORY | Identifier of the `Certificate Exchange` whose Fulfillment status is reported. Distinct from the CloudEvent `id`. | +| `certificateId` | String | MANDATORY | The unique identifier of the certificate the exchange concerns. | +| `status` | String | MANDATORY | The Fulfillment status. **MUST** be one of `ACKNOWLEDGED`, `CERTIFICATION_REQUESTED`, `FULFILLED`, `DECLINED`, or `FAILED`. | +| `errors` | Array of Object | CONDITIONAL | A list of error details. **MUST** be present and non-empty when `status` is `DECLINED` or `FAILED`. | + +A Certificate Provider that pushes Fulfillment status **SHOULD** send at least the terminal outcomes (`FULFILLED`, +`DECLINED`, `FAILED`); intermediate states **MAY** also be pushed. Each entry in the `errors` array contains the +properties defined in [Section 4.4.5](#445-certificate-acceptance-events). + +The following are non-normative examples. + +**Status: FULFILLED** + +```json +{ + "specversion": "1.0", + "type": "org.catena-x.ccm.CertificateFulfillmentStatus.v1", + "source": "urn:bpn:BPNL0000000001AB", + "subject": "BPNL0000000002CD", + "id": "f0e1d2c3-b4a5-6789-0abc-def012345678", + "time": "2025-05-04T07:30:00Z", + "datacontenttype": "application/json", + "dataschema": "https://w3id.org/catenax/schemas/ccm/certificate-fulfillment-status.json", + "data": { + "exchangeId": "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44", + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "status": "FULFILLED" + } +} +``` + +**Status: DECLINED** + +```json +{ + "specversion": "1.0", + "type": "org.catena-x.ccm.CertificateFulfillmentStatus.v1", + "source": "urn:bpn:BPNL0000000001AB", + "subject": "BPNL0000000002CD", + "id": "a1c2e3f4-5b6d-7e8f-9a0b-1c2d3e4f5a6b", + "time": "2025-05-04T07:30:00Z", + "datacontenttype": "application/json", + "dataschema": "https://w3id.org/catenax/schemas/ccm/certificate-fulfillment-status.json", + "data": { + "exchangeId": "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44", + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "status": "DECLINED", + "errors": [ + { + "message": "Certificate type not offered for the requested location" + } + ] + } +} +``` + +#### 4.3.3 Certificate Acceptance Status Query + +The Certificate Consumer **MUST** expose the following endpoint to allow a Certificate Provider to query the current +acceptance status of a `Certificate Exchange` it opened. + +| | | +|------------------|-----------------------------------------------------| +| **HTTP Method**: | GET | +| **URL Path** | `GET /certificate-acceptance-status/{id}` | +| **Content Type** | `application/json` | +| **Response** | `CertificateAcceptanceStatusResponse` or `HTTP 404` | + +The `{id}` path parameter **MUST** be the `exchangeId` assigned by the Certificate Provider when the exchange was opened +(see [Section 2.1.1](#211-identity-and-correlation)). If the `exchangeId` is unknown to the Certificate Consumer, the +endpoint **MUST** respond with `HTTP 404`. + +If the Certificate Consumer has not yet concluded processing, the endpoint **MAY** return a +`CertificateAcceptanceStatusResponse` whose `status` is `RETRIEVED`. + +The response body **MUST** be a JSON object representing the current acceptance status of the exchange. The `status` +property conveys the Acceptance-phase state, including the non-terminal value `RETRIEVED` indicating that the +certificate has been retrieved but acceptance has not yet concluded. The `errors` follow the same rules as the +corresponding acceptance event in [Section 4.4.5](#445-certificate-acceptance-events). + +| Property | Type | Required | Description | +|-----------------|-----------------|-----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `exchangeId` | String | MANDATORY | The identifier of the `Certificate Exchange`. Matches the `{id}` path parameter of the request. | +| `certificateId` | String | MANDATORY | The unique identifier of the certificate the status applies to. | +| `status` | String | MANDATORY | The current acceptance status. **MUST** be one of `RETRIEVED`, `ACCEPTED`, `REJECTED`, or `ERRORED`. | +| `errors` | Array of Object | OPTIONAL | A list of error details. **MUST NOT** be present when `status` is `RETRIEVED` or `ACCEPTED`. **MUST** be present and non-empty when `status` is `REJECTED` or `ERRORED`. | + +Each entry in the `errors` array contains the same properties as defined in +[Section 4.4.5](#445-certificate-acceptance-events): + +| Property | Type | Required | Description | +|-------------|--------|-----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `message` | String | MANDATORY | A human-readable description of the error. | +| `specifier` | String | OPTIONAL | An identifier scoping the error to a particular element of the certificate, such as a site BPN. If omitted, the error applies to the certificate as a whole. | + +The following are non-normative examples of acceptance status responses: + +**Status: RETRIEVED** + +```json +{ + "exchangeId": "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44", + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "status": "RETRIEVED" +} +``` + +**Status: ACCEPTED** + +```json +{ + "exchangeId": "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44", + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "status": "ACCEPTED" +} +``` + +**Status: REJECTED** + +```json +{ + "exchangeId": "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44", + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "status": "REJECTED", + "errors": [ + { + "message": "Certificate has expired" + }, + { + "specifier": "BPNS000000000002", + "message": "Site BPNS000000000002 has been Rejected" + } + ] +} +``` + +#### 4.3.4 OpenAPI Specification + +The Certificate Consumer Notification API is formally defined by the OpenAPI document +[certificate-consumer-notification-api-combined.yaml](./certificate-consumer-notification-api-combined.yaml). + +#### 4.3.5 DSP Dataset Representation + +The Certificate Consumer **MUST** expose a dataset according to the Dataspace Protocol (DSP) specification in their +catalog. This dataset **MUST** include the following properties: + +| | | +|-----------------|-------------------------------------------------------------------------------------------------------------------------------------------| +| **conformsTo**: | The Dublin Core [conformsTo](#dcmi-conformsto) property. The value must be set to `https://w3id.org/catenax/certificate-notification-api` | + +The catalog distribution **MUST** have its `format` value set to `https://w3id.org/dps/http-pull`. + +> TODO: Note this section is dependent on the following issues: +> - [DPS-99](#dps-99) + +The following is a non-normative example of a consumer catalog dataset: + +```json +{ + "@id": "urn:uuid:3dd1add8-4d2d-569e-d634-8394a8836a88", + "@type": "Dataset", + "hasPolicy": [], + "conformsTo": "https://w3id.org/catenax/certificate-notification-api", + "distribution": [ + { + "@type": "Distribution", + "format": "https://w3id.org/dps/http-pull", + "accessService": "urn:uuid:4aa2dcc8-4d2d-569e-d634-8394a8834d77" + } + ] +} +``` + +### 4.4 Certificate Provider API + +> *This section is normative* + +The Certificate Provider API enables Certificate Providers to accept certificate requests, report request fulfillment +status, serve certificate data, answer certificate queries, and receive acceptance status from Certificate Consumers. + +#### 4.4.1 Certificate Request + +The Certificate Provider **MUST** expose the following endpoint to allow a Certificate Consumer to request a +certificate. A request opens a consumer-initiated `Certificate Exchange` (see [Section 2.1](#21-certificate-exchange)). + +| | | +|------------------|-------------------------------------------------------| +| **HTTP Method**: | POST | +| **URL Path** | `POST /certificate-requests` | +| **Content Type** | `application/json` | +| **Request** | `CertificateRequest` | +| **Response** | `HTTP 202` with `CertificateRequestResponse` or error | + +The request body **MUST** be a JSON object containing the following properties: + +| Property | Type | Required | Description | +|-------------------|------------------|-----------|--------------------------------------------------------------------------------------------------------------------------------------------| +| `certificateType` | String | MANDATORY | An opaque string identifying the certificate type to be requested (for example, `ISO9001`). | +| `enclosedSiteBpns` | Array of Strings | OPTIONAL | The Business Partner Numbers (BPNs) of the sites or addresses the request applies to. If omitted, the request applies to the legal entity. | + +When the Certificate Provider accepts the request, it **MUST** assign an `exchangeId`, `certificateId`, and `version` +and return them in the response, so the Certificate Consumer can correlate the exchange, poll its fulfillment status, +and later retrieve the certificate. The response body **MUST** be a JSON object containing the following properties: + +| Property | Type | Required | Description | +|-----------------|-----------------|-----------|---------------------------------------------------------------------------------------------------------------------------------------| +| `exchangeId` | String | MANDATORY | The identifier assigned to the opened `Certificate Exchange`. Used to poll status ([Section 4.4.2](#442-certificate-request-status)). | +| `certificateId` | String | MANDATORY | The identifier assigned to the requested certificate. Used to retrieve it via `GET /certificates/{id}`. | +| `version` | Integer | MANDATORY | The version assigned to the requested certificate. | +| `status` | String | MANDATORY | The fulfillment status of the request. **MUST** be one of `ACKNOWLEDGED`, `CERTIFICATION_REQUESTED`, `FULFILLED`, or `DECLINED`. | +| `errors` | Array of Object | OPTIONAL | A list of error details. **MUST** be present and non-empty when `status` is `DECLINED`. | + +The `status` reflects the Fulfillment phase of the exchange (see [Section 2.1.3](#213-state-machine)). A Certificate +Provider that already holds the requested certificate **MAY** return `FULFILLED` immediately. A Certificate Provider +that will not satisfy the request **MAY** return `DECLINED` with errors, or respond with `HTTP 400` if the request is +malformed. Each entry in the `errors` array contains the properties defined in +[Section 4.4.5](#445-certificate-acceptance-events). + +The following is a non-normative example of a certificate request: + +```json +{ + "certificateType": "ISO9001", + "enclosedSiteBpns": [ + "BPNS00000003AYRE" + ] +} +``` + +The following is a non-normative example of a response (`HTTP 202`): + +```json +{ + "exchangeId": "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44", + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "version": 1, + "status": "ACKNOWLEDGED" +} +``` + +#### 4.4.2 Certificate Request Status + +The Certificate Provider **MUST** expose the following endpoint to allow a Certificate Consumer to poll the fulfillment +status of a request previously submitted via [Section 4.4.1](#441-certificate-request). + +| | | +|------------------|------------------------------------------| +| **HTTP Method**: | GET | +| **URL Path** | `GET /certificate-requests/{id}` | +| **Content Type** | `application/json` | +| **Response** | `CertificateRequestStatus` or `HTTP 404` | + +The `{id}` path parameter **MUST** be the `exchangeId` returned by the Certificate Provider in the response to the +certificate request. If the `exchangeId` is unknown to the Certificate Provider, the endpoint **MUST** respond with +`HTTP 404`. + +The response body **MUST** be a JSON object containing the following properties: + +| Property | Type | Required | Description | +|-----------------|-----------------|-----------|-------------------------------------------------------------------------------------------------------------------------------------| +| `exchangeId` | String | MANDATORY | The identifier of the `Certificate Exchange`. Matches the `{id}` path parameter of the request. | +| `certificateId` | String | MANDATORY | The unique identifier of the requested certificate. | +| `version` | Integer | MANDATORY | The version of the requested certificate. | +| `status` | String | MANDATORY | The current fulfillment status. **MUST** be one of `ACKNOWLEDGED`, `CERTIFICATION_REQUESTED`, `FULFILLED`, `DECLINED`, or `FAILED`. | +| `errors` | Array of Object | OPTIONAL | A list of error details. **MUST** be present and non-empty when `status` is `DECLINED` or `FAILED`. | + +When the `status` is `FULFILLED`, the certificate is available and **MAY** be retrieved via +[Section 4.4.3](#443-certificate-retrieval). This endpoint reports only the Fulfillment phase; the Certificate +Consumer's acceptance outcome is reported separately via [Section 4.4.5](#445-certificate-acceptance-events). + +Polling is not the only way to learn the Fulfillment status. If the Certificate Consumer exposes the Certificate +Consumer Notification API, a Certificate Provider **MAY** instead push these status updates to it as they occur (see +[Section 4.3.2](#432-certificate-fulfillment-notification)), in which case the consumer need not poll. The two mechanisms +are equivalent and carry the same `exchangeId` and `status`. + +The following are non-normative examples of request status responses: + +**Status: ACKNOWLEDGED** + +```json +{ + "exchangeId": "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44", + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "version": 1, + "status": "ACKNOWLEDGED" +} +``` + +**Status: FULFILLED** + +```json +{ + "exchangeId": "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44", + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "version": 1, + "status": "FULFILLED" +} +``` + +**Status: DECLINED** + +```json +{ + "exchangeId": "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44", + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "version": 1, + "status": "DECLINED", + "errors": [ + { + "message": "Certificate type not offered for the requested location" + } + ] +} +``` + +#### 4.4.3 Certificate Retrieval + +The Certificate Provider **MUST** expose the following endpoint to retrieve certificate metadata. + +| | | +|------------------|-------------------------------------------| +| **HTTP Method**: | GET | +| **URL Path** | `GET /certificates/{id}` | +| **Query** | `version` (OPTIONAL) — defaults to latest | +| **Content Type** | `application/json` | +| **Response** | `HTTP 200` with metadata JSON or error | + +The response is a JSON object ([RFC8259](#rfc8259)) describing the certificate. The certificate is metadata only; the +binary documents are **not** included. Each associated document is listed by reference in the `documents` array and +retrieved independently via [Section 4.4.4](#444-document-retrieval). + +If an error occurs, the Certificate Provider **MUST** set the `Content-Type` to `application/json` and return an error +body encoded as `application/json` ([RFC8259](#rfc8259)). + +By default, the endpoint returns the latest `version`. A consumer **MAY** request a specific version with the `version` +query parameter (for example, `GET /certificates/{id}?version=2`) — for instance, to retrieve the exact version a +`Certificate Exchange` concerns. The Certificate Provider **MUST** be able to return any version that is still +referenced by a non-terminal `Certificate Exchange`; whether other superseded versions remain retrievable is at the +provider's discretion. + +Each version references a set of documents through the `documents` array. Document identity is independent of the +certificate version: a `documentId` is stable and its content is immutable. A document that does not change across +versions is therefore referenced by the same `documentId` in each version that includes it. + +The metadata is a JSON object containing the following properties: + +| Property | Type | Required | Description | +|--------------------------|------------------|-----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `certificateId` | String | MANDATORY | The unique identifier of the certificate. Matches the `{id}` path parameter of the request. | +| `version` | Integer | MANDATORY | The version returned — the version requested via the `version` query parameter, or the latest version if none was specified. | +| `certificateType` | String | MANDATORY | An opaque string identifying the certificate type (for example, `ISO9001`). See [Section 5.4](#54-certificate-types). | +| `certificateTypeVersion` | String | OPTIONAL | The version of the certificate type itself (for example, `2015` for ISO 9001:2015). Distinct from the certificate's `version`. | +| `certifiedBpn` | String | MANDATORY | The BPNL of the certified legal entity on which the certificate is issued. | +| `registrationNumber` | String | MANDATORY | The certificate's registration number at the issuing body. | +| `validFrom` | String | MANDATORY | The inclusive start date of the certificate validity period, formatted as an [ISO 8601](#iso8601) full-date (`YYYY-MM-DD`). | +| `validUntil` | String | MANDATORY | The inclusive end date of the certificate validity period, formatted as an [ISO 8601](#iso8601) full-date (`YYYY-MM-DD`). | +| `trustLevel` | String | MANDATORY | The trust level of the certificate. **MUST** be one of `none`, `low`, `medium`, `high`, or `trusted` (see [Section 5.5](#55-certificate-metadata-attributes)). | +| `areaOfApplication` | String | OPTIONAL | Free-text detail of the areas or application types the certificate is valid for overall. Individual locations **MAY** narrow this via `Location.areaOfApplication`. | +| `enclosedSites` | Array of Objects | OPTIONAL | The sites or addresses the certificate applies to, each an `EnclosedSite` object (see below). If omitted or empty, the certificate applies to the issuing legal entity as a whole. | +| `issuer` | Object | OPTIONAL | The authority that issued the certificate, a `CertificateIssuer` object (see below). | +| `validator` | Object | OPTIONAL | The party that can validate the certificate information, a `CertificateValidator` object (see below). || `documents` | Array of Objects | OPTIONAL | The documents associated with this certificate version, each a `CertificateDocument` object (see below). If omitted or empty, the version has no associated documents. | + +Each `EnclosedSite` object contains the following properties: + +| Property | Type | Required | Description | +|---------------------|--------|-----------|--------------------------------------------------------------------------------------------------------------------| +| `bpn` | String | MANDATORY | The Business Partner Number of the site (BPNS) or address (BPNA) the certificate covers. | +| `areaOfApplication` | String | OPTIONAL | Free-text detail of the areas or application types the certificate is valid for at this specific location. | + +Each `CertificateIssuer` object contains the following properties: + +| Property | Type | Required | Description | +|--------------|--------|-----------|---------------------------------------------------------------------------| +| `issuerName` | String | MANDATORY | The name of the issuing authority (for example, `TÜV Süd`). | +| `issuerBpn` | String | OPTIONAL | The BPNL of the issuing authority, if it is a Catena-X business partner. | + +Each `CertificateValidator` object contains the following properties: + +| Property | Type | Required | Description | +|-----------------|--------|-----------|-------------------------------------------------------------------------------------------------------------------------| +| `validatorName` | String | MANDATORY | The name of the validator. | +| `validatorBpn` | String | OPTIONAL | The BPNL of the validator. **MAY** be used as a free-text identifier where a BPNL is not available. | + +Each `CertificateDocument` object contains the following properties: + +| Property | Type | Required | Description | +|--------------|--------|-----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `documentId` | String | MANDATORY | The opaque, version-independent identifier of the document, used to retrieve it via `GET /documents/{id}` (see [Section 4.4.4](#444-document-retrieval)). | +| `language` | String | OPTIONAL | The language of the document as an [ISO 639-1](#iso6391) two-letter code (for example, `en` or `de`). Distinguishes documents that differ only by language. | +| `mediaType` | String | MANDATORY | The IANA media type of the document binary (for example, `application/pdf` or `image/png`). | + +The following is a non-normative example of a response: + +```json +{ + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "version": 1, + "certificateType": "ISO9001", + "certificateTypeVersion": "2015", + "certifiedBpn": "BPNL0000000002CD", + "registrationNumber": "12 100 4711", + "validFrom": "2023-01-25", + "validUntil": "2026-01-24", + "trustLevel": "high", + "areaOfApplication": "Production and assembly of powertrain components", + "enclosedSites": [ + { + "bpn": "BPNS00000003AYRE", + "areaOfApplication": "Welding and surface treatment" + }, + { + "bpn": "BPNA000000000001" + } + ], + "issuer": { + "issuerName": "TÜV Süd", + "issuerBpn": "BPNL0000000003EF" + }, + "validator": { + "validatorName": "TÜV Süd", + "validatorBpn": "BPNL0000000003EF" + }, "documents": [ + { + "documentId": "doc-3fa85f64-5717-4562-b3fc-2c963f66afa6", + "language": "en", + "mediaType": "application/pdf" + } + ] +} +``` + +#### 4.4.4 Document Retrieval + +The Certificate Provider **MUST** expose the following endpoint to retrieve a certificate document by its identifier. + +| | | +|------------------|----------------------------------------------| +| **HTTP Method**: | GET | +| **URL Path** | `GET /documents/{id}` | +| **Content Type** | The document's `mediaType` | +| **Response** | `HTTP 200` with the document binary or error | + +A document is identified by an opaque `documentId` that is independent of any certificate version (see +[Section 4.4.3](#443-certificate-retrieval)). Because a document's identity and content are stable, the same document +**MAY** be referenced by multiple certificate versions, and the endpoint therefore requires no `version` parameter. + +The response body is the document binary, served directly with the `Content-Type` set to the document's `mediaType` +(for example, `application/pdf`) as advertised in the certificate metadata +([Section 4.4.3](#443-certificate-retrieval)). + +If an error occurs, the Certificate Provider **MUST** set the `Content-Type` to `application/json` and return an error +body encoded as `application/json` ([RFC8259](#rfc8259)). + +The following is a non-normative example of a response: + +```http +HTTP/1.1 200 OK +Content-Type: application/pdf + +[binary document data] +``` + +#### 4.4.5 Certificate Acceptance Events + +The Certificate Provider **MUST** expose the following endpoint to receive certificate acceptance status from the +Certificate Consumer. The status is delivered as a `CertificateAcceptanceStatus` CloudEvent. + +| | | +|------------------|-------------------------------------------------------| +| **HTTP Method**: | POST | +| **URL Path** | `POST /certificate-acceptance-notifications` | +| **Content Type** | `application/cloudevents+json` | +| **Request** | `CertificateAcceptanceStatus` (single event or batch) | +| **Response** | `HTTP 204` with empty body or error | + +The endpoint accepts a single CloudEvent ([CloudEvents](#cloudevents)) or a batch (a JSON array) as defined in +[CX-0000](#cx-0000), whose HTTP binding follows [CloudEvents-HTTP](#cloudevents-http). + +**Event Type**: `org.catena-x.ccm.CertificateAcceptanceStatus.v1` + +The Certificate Provider **MUST** dispatch on the `data.status` value, which is a state of the Acceptance phase (see +[Section 2.1.3](#213-state-machine)). The event is correlated to its `Certificate Exchange` by the `exchangeId` carried +in `data` (see [Section 2.1.1](#211-identity-and-correlation)). + +Acceptance status **MUST** reference an existing `Certificate Exchange`. Acceptance is the Consumer-owned phase of an +exchange, so it can only be reported when an exchange is present — one opened by a consumer request +([Section 4.4.1](#441-certificate-request)) or by a provider notification +([Section 4.3.1](#431-certificate-lifecycle-events)). Merely retrieving a certificate (for example, after +a [query](#446-certificate-query)) does **not** establish an exchange and therefore does not permit acceptance feedback. +If the `exchangeId` is unknown to the Certificate Provider, it **MUST** reject the event with `HTTP 404`. + +Reporting `RETRIEVED` is **OPTIONAL** (see [Section 2.1.3](#213-state-machine)). It is a non-terminal delivery +acknowledgment; a Certificate Consumer **MAY** report it after fetching the certificate, or **MAY** proceed directly to +a terminal acceptance status (`ACCEPTED`, `REJECTED`, or `ERRORED`) without first reporting `RETRIEVED`. A Certificate +Provider **MUST NOT** require a prior `RETRIEVED` event as a precondition for accepting a terminal acceptance status. + +The `data` payload derives from the common certificate-status base payload and constrains `status` to the acceptance +values below. It contains the following properties: + +| Property | Type | Required | Description | +|-----------------|-----------------|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `exchangeId` | String | MANDATORY | Identifier of the `Certificate Exchange` this event reports on. Distinct from the CloudEvent `id`. | +| `certificateId` | String | MANDATORY | The unique identifier of the certificate the status applies to. | +| `status` | String | MANDATORY | The acceptance status of the certificate. **MUST** be one of `RETRIEVED`, `ACCEPTED`, `REJECTED`, or `ERRORED`. | +| `errors` | Array of Object | CONDITIONAL | A list of error details. **MUST NOT** be present when `status` is `RETRIEVED` or `ACCEPTED`. **MUST** be present and non-empty when `status` is `REJECTED` or `ERRORED`. | + +Each entry in the `errors` array contains the following properties: + +| Property | Type | Required | Description | +|-------------|--------|-----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `message` | String | MANDATORY | A human-readable description of the error. | +| `specifier` | String | OPTIONAL | An identifier scoping the error to a particular element of the certificate, such as a site BPN. If omitted, the error applies to the certificate as a whole. | + +The following are non-normative examples. + +**Status: RETRIEVED** + +```json +{ + "specversion": "1.0", + "type": "org.catena-x.ccm.CertificateAcceptanceStatus.v1", + "source": "urn:bpn:BPNL0000000002CD", + "subject": "BPNL0000000001AB", + "id": "e9f8d7c6-b5a4-9382-7160-5a4b3c2d1e0f", + "time": "2025-05-04T08:00:00Z", + "datacontenttype": "application/json", + "dataschema": "https://w3id.org/catenax/schemas/ccm/certificate-acceptance-status.json", + "data": { + "exchangeId": "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44", + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "status": "RETRIEVED" + } +} +``` + +**Status: ACCEPTED** + +```json +{ + "specversion": "1.0", + "type": "org.catena-x.ccm.CertificateAcceptanceStatus.v1", + "source": "urn:bpn:BPNL0000000002CD", + "subject": "BPNL0000000001AB", + "id": "a7b8c9d0-e1f2-3a4b-5c6d-7e8f9a0b1c2d", + "time": "2025-05-04T09:00:00Z", + "datacontenttype": "application/json", + "dataschema": "https://w3id.org/catenax/schemas/ccm/certificate-acceptance-status.json", + "data": { + "exchangeId": "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44", + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "status": "ACCEPTED" + } +} +``` + +**Status: REJECTED** + +```json +{ + "specversion": "1.0", + "type": "org.catena-x.ccm.CertificateAcceptanceStatus.v1", + "source": "urn:bpn:BPNL0000000002CD", + "subject": "BPNL0000000001AB", + "id": "f1a2b3c4-d5e6-7f8a-9b0c-1d2e3f4a5b6c", + "time": "2025-05-04T09:00:00Z", + "datacontenttype": "application/json", + "dataschema": "https://w3id.org/catenax/schemas/ccm/certificate-acceptance-status.json", + "data": { + "exchangeId": "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44", + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "status": "REJECTED", + "errors": [ + { + "message": "Certificate has expired" + }, + { + "message": "Unexpected data format" + }, + { + "specifier": "BPNS000000000002", + "message": "Site BPNS000000000002 has been Rejected" + } + ] + } +} +``` + +**Status: ERRORED** + +```json +{ + "specversion": "1.0", + "type": "org.catena-x.ccm.CertificateAcceptanceStatus.v1", + "source": "urn:bpn:BPNL0000000002CD", + "subject": "BPNL0000000001AB", + "id": "c8d7e6f5-a4b3-2109-8765-432109fedcba", + "time": "2025-05-04T08:30:00Z", + "datacontenttype": "application/json", + "dataschema": "https://w3id.org/catenax/schemas/ccm/certificate-acceptance-status.json", + "data": { + "exchangeId": "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44", + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "status": "ERRORED", + "errors": [ + { + "message": "Certificate document is malformed and cannot be validated" + } + ] + } +} +``` + +#### 4.4.6 Certificate Query + +The Certificate Provider **MUST** expose the following endpoint to query certificates. + +| | | +|------------------|----------------------------| +| **HTTP Method**: | POST | +| **URL Path** | `POST /certificates/search` | +| **Content Type** | `application/json` | +| **Request** | `CertificateQuery` | +| **Response** | `CertificateQueryResponse` | + +The request body **MUST** be a JSON object containing the query criteria. The `limit` property **MAY** be included to +indicate the maximum number of results to return per page. If omitted, the Certificate Provider **MAY** apply a default +limit. + +The `CertificateQuery` object contains the following properties. All criteria are **OPTIONAL**; a query with no criteria +returns all certificates accessible to the Certificate Consumer. Criteria of different kinds are combined with **AND** +(a certificate is returned only if it satisfies every supplied criterion). Within a single BPN list, the match is +**any-of** (a certificate matches if it covers any one of the listed BPNs). + +| Property | Type | Required | Description | +|--------------------|------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `certificateType` | String | OPTIONAL | An opaque string identifying the certificate type to be matched (for example, `ISO9001`). See [Section 5.4](#54-certificate-types). | +| `legalEntityBpns` | Array of Strings | OPTIONAL | Filter to certificates issued for these legal entities (matched against `certifiedBpn`). Each value is a BPNL. | +| `siteBpns` | Array of Strings | OPTIONAL | Filter to certificates covering these sites (matched against the `enclosedSites`). Each value is a BPNS. | +| `addressBpns` | Array of Strings | OPTIONAL | Filter to certificates covering these addresses (matched against the `enclosedSites`). Each value is a BPNA. | +| `from` | String | OPTIONAL | The inclusive lower bound of the certificate validity range, formatted as an [ISO 8601](#iso8601) full-date (`YYYY-MM-DD`). Only certificates whose `validFrom` is on or after this date are returned. | +| `to` | String | OPTIONAL | The inclusive upper bound of the certificate validity range, formatted as an [ISO 8601](#iso8601) full-date (`YYYY-MM-DD`). Only certificates whose `validUntil` is on or before this date are returned. | +| `limit` | Integer | OPTIONAL | The maximum number of results to return in a single page. If omitted, the Certificate Provider **MAY** apply a default limit. | + +The following is a non-normative example of a certificate query: + +```json +{ + "certificateType": "ISO9001", + "legalEntityBpns": [ + "BPNL0000000002CD" + ], + "siteBpns": [ + "BPNS00000003AYRE" + ], + "from": "2023-01-25", + "to": "2026-01-24", + "limit": 50 +} +``` + +The response body **MUST** be a JSON array of `CertificateQueryResponse` objects containing match results. Each object +contains the following properties: + +| Property | Type | Required | Description | +|-------------------|------------------|-----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `certificateId` | String | MANDATORY | The unique identifier of the certificate, used to retrieve the certificate via `GET /certificates/{id}`. | +| `version` | Integer | MANDATORY | The latest version of the certificate. | +| `datasetId` | String | MANDATORY | The identifier of the DSP dataset under which the certificate is exposed in the Certificate Provider's catalog, enabling the Certificate Consumer to locate and negotiate the certificate via the Dataspace Protocol. | +| `certificateType` | String | MANDATORY | An opaque string identifying the certificate type (for example, `ISO9001`). See [Section 5.4](#54-certificate-types). | +| `certificateTypeVersion` | String | OPTIONAL | The version of the certificate type itself (for example, `2015` for ISO 9001:2015). Distinct from the certificate's `version`. | +| `certifiedBpn` | String | MANDATORY | The BPNL of the certified legal entity on which the certificate is issued. | +| `registrationNumber` | String | MANDATORY | The certificate's registration number at the issuing body. | +| `validFrom` | String | MANDATORY | The inclusive start date of the certificate validity period, formatted as an [ISO 8601](#iso8601) full-date (`YYYY-MM-DD`). | +| `validUntil` | String | MANDATORY | The inclusive end date of the certificate validity period, formatted as an [ISO 8601](#iso8601) full-date (`YYYY-MM-DD`). | +| `trustLevel` | String | MANDATORY | The trust level of the certificate. **MUST** be one of `none`, `low`, `medium`, `high`, or `trusted` (see [Section 5.5](#55-certificate-metadata-attributes)). | +| `areaOfApplication` | String | OPTIONAL | Free-text detail of the areas or application types the certificate is valid for. | +| `enclosedSiteBpns` | Array of Strings | OPTIONAL | The Business Partner Numbers (BPNs) of the sites or addresses the certificate applies to. If omitted, the certificate applies to the issuing legal entity as a whole. | +| `issuer` | Object | OPTIONAL | The authority that issued the certificate, a `CertificateIssuer` object as defined in [Section 4.4.3](#443-certificate-retrieval). | +| `validator` | Object | OPTIONAL | The party that can validate the certificate, a `CertificateValidator` object as defined in [Section 4.4.3](#443-certificate-retrieval). || `documents` | Array of Objects | OPTIONAL | The documents associated with the returned certificate version, each a `CertificateDocument` object as defined in [Section 4.4.3](#443-certificate-retrieval). Retrieved via `GET /documents/{id}`. | + +The following is a non-normative example of a certificate query response containing an array of +`CertificateQueryResponse` objects: + +```json +[ + { + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "version": 1, + "datasetId": "dataset-ccm-cert-abc123", + "certificateType": "ISO9001", + "certificateTypeVersion": "2015", + "certifiedBpn": "BPNL0000000002CD", + "registrationNumber": "12 100 4711", + "validFrom": "2023-01-25", + "validUntil": "2026-01-24", + "trustLevel": "high", + "areaOfApplication": "Production and assembly of powertrain components", + "enclosedSiteBpns": [ + "BPNS00000003AYRE", + "BPNA000000000001" + ], + "issuer": { + "issuerName": "TÜV Süd", + "issuerBpn": "BPNL0000000003EF" + }, + "validator": { + "validatorName": "TÜV Süd", + "validatorBpn": "BPNL0000000003EF" + }, + "documents": [ + { + "documentId": "doc-3fa85f64-5717-4562-b3fc-2c963f66afa6", + "language": "en", + "mediaType": "application/pdf" + } + ] + } +] +``` + +##### 4.4.6.1 Pagination + +Implementations **MAY** paginate the results of a query. When results are paginated, pagination data **MUST** be +conveyed using Web Linking as defined in [RFC8288](#rfc8288) via the HTTP `Link` response header. Only the `next` +and `prev` link relation types **MUST** be supported; implementations **MAY** include additional relation types such as +`first` and `last`. + +Each value in the `Link` header **MUST** be enclosed in angle brackets and **MUST** include a `rel` parameter +identifying the link relation type. When more than one link is present, values **MUST** be comma-separated as defined +in [RFC8288, Section 3](#rfc8288). The target URL **MUST** refer to the +`POST /certificates/search` endpoint of the Certificate Provider. The content and structure of the URL (including any +query parameters or opaque page tokens used to encode pagination state) are not defined by this specification and +clients **MUST** treat them as opaque. + +To retrieve the next or previous page, a client **MUST** issue a `POST` request to the URL provided in the corresponding +`Link` header value, using the same request body as the original query. The `Link` header **MUST NOT** be included in +responses that are not paginated, and a `next` or `prev` link **MUST** be omitted when no further results are available +in that direction. + +The following is a non-normative example of a paginated response: + +```http +HTTP/1.1 200 OK +Content-Type: application/json +Link: ; rel="next", + ; rel="prev" + +[ + { + "certificateId": "cert-550e8400-e29b-41d4-a716-446655440000", + "version": 1, + "datasetId": "dataset-ccm-cert-abc123", + "certificateType": "ISO9001", + "validFrom": "2023-01-25", + "validUntil": "2026-01-24", + "enclosedSiteBpns": [ + "BPNS00000003AYRE", + "BPNA000000000001" + ] + } +] +``` + +#### 4.4.7 OpenAPI Specification + +The Certificate Provider API is formally defined by the OpenAPI document +[certificate-provider-api-combined.yaml](./certificate-provider-api-combined.yaml). + +#### 4.4.8 DSP Dataset Representation + +The Certificate Provider **MUST** expose a dataset according to the Dataspace Protocol (DSP) specification in their +catalog. This dataset **MUST** include the following properties: + +| | | +|-----------------|------------------------------------------------------------------------------------------------------------------------------| +| **conformsTo**: | The Dublin Core [conformsTo](#dcmi-conformsto) property. The value must be set to `https://w3id.org/catenax/certificate-api` | + +The catalog distribution **MUST** have its `format` value set to `https://w3id.org/dps/http-pull`. + +> TODO: Note this section is dependent on the following issues: +> - [DPS-98](#dps-98) +> - [DPS-99](#dps-99) + +The following is a non-normative example of a provider catalog dataset: + +```json +{ + "@id": "urn:uuid:3dd1add8-4d2d-569e-d634-8394a8836a88", + "@type": "Dataset", + "hasPolicy": [], + "conformsTo": "https://w3id.org/catenax/certificate-api", + "distribution": [ + { + "@type": "Distribution", + "format": "https://w3id.org/dps/http-pull", + "accessService": "urn:uuid:4aa2dcc8-4d2d-569e-d634-8394a8834d77" + } + ] +} +``` + +### 4.5 Policy Constraints and Usage Policy + +#### 4.5.1 Policy Constraints for Data Exchange + +In alignment with the commitment to data sovereignty, a framework governing the use of data within the Catena-X use +cases has been defined. As part of this framework, conventions for access policies, usage policies, and the constraints +they contain are specified in [CX-0152](#cx-0152) Policy Constraints for Data Exchange. [CX-0152](#cx-0152) **MUST** be +followed when providing services or applications for sharing or consuming data, and when sharing or consuming data, in +the Catena-X ecosystem. Which conventions are relevant for which roles named in +[FOR WHOM IS THE STANDARD DESIGNED](#for-whom-is-the-standard-designed) is specified in [CX-0152](#cx-0152) as well. + +#### 4.5.2 Usage Policy + +All dataspace offers of a participant — both the APIs defined in this standard and the certificate datasets — **MUST** +carry a usage policy following the requirements referenced in [Section 4.5.1](#451-policy-constraints-for-data-exchange). +This use case introduces the following usage purpose: + +- **`cx.ccm.base:1`** — *the legal meaning is defined in [CX-0152](#cx-0152) (see the Catena-X standard library).* + +Additional, more general usage policies **MAY** be included, but every usage policy **MUST** contain the usage purpose +above, as shown below. + +*Minimal example of a usage policy without a contract reference:* + +```json +{ + "@context": [ + "https://w3id.org/catenax/2025/9/policy/odrl.jsonld", + "https://w3id.org/catenax/2025/9/policy/context.jsonld" + ], + "@type": "Set", + "@id": "CCMAPI-usage-policy-without-contract-reference", + "permission": [ + { + "action": "use", + "constraint": { + "and": [ + { + "leftOperand": "FrameworkAgreement", + "operator": "eq", + "rightOperand": "DataExchangeGovernance:1.0" + }, + { + "leftOperand": "UsagePurpose", + "operator": "isAnyOf", + "rightOperand": "cx.ccm.base:1" + } + ] + } + } + ] +} +``` + +The constraint `{ "leftOperand": "ContractReference" }` **MUST** be included only if such a bilateral framework contract +exists: + +```json +{ + "leftOperand": "ContractReference", + "operator": "isAllOf", + "rightOperand": "x12345" +} +``` + +## 5 Terminology + +> *This section is non-normative.* + +This section summarizes the terms used throughout this standard. The authoritative definitions are given in the +conceptual model ([Section 2](#2-model)); the entries below are a quick reference and point to that section. + +#### 5.1 Certificate Provider and Certificate Consumer + +**Certificate Provider**: An entity that offers company certificates to other Catena-X participants. The Certificate +Provider exposes the [Certificate Provider API](#44-certificate-provider-api), manages certificates in its backend, +responds to certificate requests, and may notify Certificate Consumers of lifecycle changes. + +**Certificate Consumer**: An entity that requests, retrieves, and validates company certificates from Certificate +Providers. The Certificate Consumer may request certificates, report acceptance status, and — if it exposes the +[Certificate Consumer Notification API](#43-certificate-consumer-notification-api) — receive notifications. + +#### 5.2 Core Concepts + +| Term | Definition | +|------|------------| +| `Certificate Exchange` | A single, correlatable interaction delivering one certificate from a Certificate Provider to a Certificate Consumer, with its own identity and lifecycle. See [Section 2.1](#21-certificate-exchange). | +| `Certificate Lifecycle` | The independent lifecycle of a certificate as an artifact — creation, modification, and withdrawal. See [Section 2.2](#22-certificate-lifecycle). | +| `exchangeId` | The identifier assigned to a `Certificate Exchange` when it is opened; the correlation handle for the whole interaction. See [Section 2.1.1](#211-identity-and-correlation). | +| `certificateId` | The stable identifier of a certificate across its lifetime. See [Section 2.2.1](#221-identity-and-versioning). | +| `version` | The integer version of a certificate; incremented on each `MODIFIED`. A certificate is identified by the (`certificateId`, `version`) pair. See [Section 2.2.1](#221-identity-and-versioning). | +| `certificateType` | An opaque string identifying the type of certificate (for example, `ISO9001`). | +| `enclosedSites` / `enclosedSiteBpns` | The sites/addresses a certificate applies to; a fixed property of the certificate. The full certificate record uses structured `enclosedSites` (per-location area of application); query results and lifecycle events use bare `enclosedSiteBpns`. See [Section 2.2.1](#221-identity-and-versioning). | +| `documentId` | The opaque, version-independent identifier of a certificate document, retrieved via `GET /documents/{id}`. See [Section 4.4.4](#444-document-retrieval). | +| `mediaType` | The IANA media type of a document binary (for example, `application/pdf`). | + +#### 5.3 Status Vocabularies + +The Fulfillment and Acceptance phases of a `Certificate Exchange` and the publication states of a `Certificate Lifecycle` +use the disjoint status vocabularies defined in [Section 2.1.3](#213-state-machine) and +[Section 2.2.2](#222-state-machine) respectively. + +#### 5.4 Certificate Types + +The `certificateType` is an opaque string identifying the type of certificate a business partner is certified for (see +[Section 5.2](#52-core-concepts)). The model is generic and not limited to a fixed list. The following types are +commonly used: + +- **IATF 16949** (International Automotive Task Force) — quality management system requirements for the automotive industry. +- **ISO 14001** — environmental management system requirements. +- **ISO 9001** — quality management system requirements. +- **ISO 45001 / OHSAS 18001** — occupational health and safety management systems. +- **ISO/IEC 27001** — information security management system. +- **ISO 50001** — energy management system. +- **ISO/IEC 17025** — testing and calibration laboratory competence. +- **ISO 20000** — IT service management system. +- **ISO 22301** — business continuity management system. +- **AEO / CTPAT / Security Declaration** — customs and supply-chain security compliance. +- **VDA 6.4** — automotive quality management with a focus on process auditing. + +To maximize interoperability, producers and consumers **SHOULD** exchange `certificateType` values using a normalized +code derived by the following rules: + +1. Only Latin letters and digits are used. +2. All letters are lowercase. +3. No whitespace, underscores, or other special characters are used. + +Applying these rules to the list above yields: + +| Name | Code | +|---------------|-------------| +| IATF 16949 | iatf16949 | +| ISO 14001 | iso14001 | +| ISO 9001 | iso9001 | +| ISO 45001 | iso45001 | +| OHSAS 18001 | ohsas18001 | +| ISO/IEC 27001 | isoiec27001 | +| ISO 50001 | iso50001 | +| ISO/IEC 17025 | isoiec17025 | +| ISO 20000 | iso20000 | +| ISO 22301 | iso22301 | +| AEO | aeo | +| CTPAT | ctpat | +| VDA6.4 | vda64 | + +#### 5.5 Certificate Metadata Attributes + +The certificate metadata returned by [Section 4.4.3](#443-certificate-retrieval) carries the following descriptive +attributes. All BPN values are Business Partner Numbers as defined by [CX-0010](#cx-0010). + +| Attribute | Meaning | +|------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `certifiedBpn` | The BPNL of the certified legal entity — the business partner the certificate is issued on. | +| `registrationNumber` | The unique identifier of the certificate at the certification authority / issuing body. | +| `certificateTypeVersion` | The version of the certificate *type* (e.g. the standard edition such as `2015` for ISO 9001:2015). Independent of the certificate artifact's `version`. | +| `areaOfApplication` | Additional detail of the areas or application types for which the certificate is valid. | +| `issuer` | The authority that issued the certificate (for example, a certification body such as TÜV Süd). `issuerBpn` is the BPNL where the issuer is a Catena-X partner. | +| `validator` | The party that can validate the certificate information — ideally the issuing authority, but other validators (e.g. validation-service providers) are permitted. Related to `trustLevel`. `validatorBpn` defaults to a BPNL but **MAY** be free text. || `trustLevel` | The degree to which the certificate has been validated. One of: `none` (uploaded, no check), `low` (manual human check), `medium` (trusted issuer plus manual check), `high` (automated cross-check against a database, e.g. TÜV/IATF), `trusted` (provided directly by the issuer). | + +## 6 References + +### 6.1 Normative References + + +**[CX-0000]** Catena-X, "CloudEventsFoundation", +[CX-0000-CloudEventsFoundation.md](./CX-0000-CloudEventsFoundation-combined.md). + + +**[CX-0010]** Catena-X, "Business Partner Number", +. + + +**[CX-0151]** Catena-X, "Industry Core: Part Type and Part Instance". + + +**[CX-0152]** Catena-X, "Policy Constraints for Data Exchange", +. + + +**[CloudEvents]** Cloud Native Computing Foundation, "CloudEvents 1.0.2 — Core Specification", +. + + +**[CloudEvents-HTTP]** Cloud Native Computing Foundation, "HTTP Protocol Binding for CloudEvents 1.0.2", +. + + +**[DCMI-conformsTo]** Dublin Core Metadata Initiative, "DCMI Metadata Terms — conformsTo", +. + + +**[DSP]** Eclipse Dataspace Working Group, "Dataspace Protocol 2025-1", +. + + +**[ISO639-1]** International Organization for Standardization, "ISO 639-1:2002, Codes for the representation of names of +languages — Part 1: Alpha-2 code", . + + +**[ISO8601]** International Organization for Standardization, "ISO 8601-1:2019, Date and time — Representations for +information interchange — Part 1: Basic rules", . + + +**[RFC2119]** Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997, +. + + +**[RFC8174]** Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, May 2017, +. + + +**[RFC8259]** Bray, T., Ed., "The JavaScript Object Notation (JSON) Data Interchange Format", STD 90, RFC 8259, December +2017, . + + +**[RFC8288]** Nottingham, M., "Web Linking", RFC 8288, October 2017, . + +### 6.2 Non-Normative References + + +**[DSP-Catalog]** Eclipse Dataspace Working Group, "Dataspace Protocol 2025-1 — Catalog Protocol", +. + + +**[DPS-98]** Eclipse Data Plane Signaling, "Issue #98", +. + + +**[DPS-99]** Eclipse Data Plane Signaling, "Issue #99", +. + +## ANNEXES + +### FIGURES + +> *This section is non-normative.* + +not applicable + +### TABLES + +> *This section is non-normative.* + +not applicable + +## Legal + +Copyright © 2026 Catena-X Automotive Network e.V. All rights reserved. For more information, please see [Catena-X Copyright Notice](https://catenax-ev.github.io/copyright). diff --git a/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement-merge-notes.md b/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement-merge-notes.md new file mode 100644 index 0000000000..f940f9b5dc --- /dev/null +++ b/docs/standards/CX-0135-CompanyCertificateManagement/CX-0135-CompanyCertificateManagement-merge-notes.md @@ -0,0 +1,240 @@ +# CX-0135 — Merge Notes (CloudEvents proposal × PR #318) + +> *This document records how the **combined** CX-0135 documents +> (`CX-0135-CompanyCertificateManagement-combined.md` and its `-combined` OpenAPI specs) were produced by +> merging the CloudEvents-based proposal (the "jim" documents) with the harmonized push/pull proposal +> ([catenax-eV/catenax-ev.github.io#318](https://github.com/catenax-eV/catenax-ev.github.io/pull/318), the +> "original"). It explains every element of the original that was **left out** of the combined documents. + +## 1 Merge methodology + +The governing rules were: + +- **CloudEvents + JSON Schema is decisive.** The data model is expressed as inline JSON Schema in the OpenAPI specs and + CloudEvents payloads. SAMM is not used. +- **EDC is never referenced.** EDC is an implementation and is excluded; reference only to the Dataspace Protocol (DSP). +- **Data-plane requirements come from jim**. The specification should not reference EDC and the EDC data plane has been + deprecated. +- **Normative language follows RFC 2119 keyword style.** + +## 2 Incorporated + +The following were folded into the combined documents (sourced from the original or adapted to jim's model): + +| Element | Where | Notes | +|------------------------------|----------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Rich certificate metadata | §4.4.3 / §4.4.6 / OpenAPI | `certifiedBpn`, `registrationNumber`, `issuer`, `validator`, `trustLevel` (`none/low/medium/high/trusted`), `areaOfApplication`, `certificateTypeVersion` (the original's `uploader` is excluded — see §3) | +| Document `language` | §4.4.3, `CertificateDocument` | ISO 639-1 two-letter code; distinguishes same-document-different-language | +| Structured covered locations | §4.4.3, `EnclosedSite` | `enclosedSites` (objects `{ bpn, areaOfApplication }`) on the full record; flat `enclosedSiteBpns` (strings) on query results, lifecycle events, and requests | +| BPN-based search filters | §4.4.6, `CertificateQuery` | `legalEntityBpns` / `siteBpns` / `addressBpns`; `certificateType` made optional; AND across kinds, any-of within a list | +| Policy & usage policy | §4.5 | CX-0152 reference; `cx.ccm.base:1` usage purpose; ODRL example. "EDC" sanitized to "dataspace offers" | +| Certificate-type catalog | §5.4 | Type list + normalization rules + name→code table; `certificateType` remains an opaque string | +| Metadata-attribute glossary | §5.5 | Definitions of trust level, issuer, validator, registration number, etc. | +| References | §6 | Added CX-0152 and CX-0010 (BPN identifiers) | +| DSP dataset reference | §4.3.1 / §4.4.6 / OpenAPI | `datasetId` (jim-native, MANDATORY) — the DSP dataset under which the certificate is exposed; lets the consumer locate and negotiate it. Removed then reinstated | +| Presentation shell | front matter, §5, annexes, legal | Versioned title, ABSTRACT, audience, terminology, annexes, copyright — sanitized of EDC/SAMM | + +## 3 Excluded + +The items below are elements of the **original** that are intentionally **not** present in the combined documents. + +### Excluded — superseded by the CloudEvents model + +#### 1 — Page-based pagination + +**What it is (original).** Search returns a `PaginatedCertificateList` wrapper with `totalElements`, `totalPages`, +`page`, and `pageSize`; the client navigates by page index. + +Instead, follow the DSP approach with cursor pagination via the HTTP `Link` header per RFC 8288 (`next`/`prev`); page +state is an opaque token (§4.4.6.1). + +**Why excluded.** The two pagination models are mutually exclusive; RFC 8288 is the chosen mechanism based on alignment +with DSP. + +#### 2 — `RequestStatus.UNDER_CERTIFICATION` + +**What it is (original).** A request status enum value indicating the certificate is being produced by an external +authority. + +**jim's approach.** The equivalent Fulfillment state is `CERTIFICATION_REQUESTED` (§2.1.3), reported as the request +`status`. + +**Why excluded.** Naming only — jim's term covers the same condition and is aligned with events being written in the +past tense. + +#### 3 — Request-body `certifiedBpn` and the union response shape + +**What it is (original).** The request body includes `certifiedBpn` (whose certificate is wanted). The response is a +polymorphic union — `CertificateRequestInProgressResponse` / `CertificateRequestCompletedResponse` / +`CertificateRequestRejectedResponse` — with rejection split into `requestErrors` and `locationErrors`. + +**jim's approach.** The request is `{ certificateType, enclosedSiteBpns }` (the certified entity is the provider +counterparty). The response is one flat object `{ exchangeId, certificateId, version, status, errors[] }`; per-location +errors use `errors[].specifier` (§4.4.1). + +**Why excluded.** jim deliberately flattens the union into a single status-dispatched object and always returns a +correlatable `exchangeId`. + +#### 4 — `Header` / `FeedbackUrlHeader` envelope fields + +**What it is (original).** A custom message envelope with `messageId`, `context`, `sentDateTime`, `senderBpn`, +`receiverBpn`, `relatedMessageId`, `version`, and `senderFeedbackUrl`. + +**jim's approach.** Messages are CloudEvents; these concerns are carried by CloudEvents attributes (`id`, `source`, +`subject`, `time`, `type`, `relatedmessageid`, etc.) defined in CX-0000. + +**Why excluded.** The envelope is replaced wholesale by the CloudEvents binding. + +#### 5 — `PushStatus.DELETED` and `FeedbackStatus.RECEIVED` + +**What it is (original).** Lifecycle push uses `DELETED`; feedback uses `RECEIVED` (plus `ACCEPTED`/`REJECTED`). + +**jim's approach.** The lifecycle terminal is `WITHDRAWN` (§2.2.2); the acceptance non-terminal acknowledgment is +`RETRIEVED`, and jim adds `ERRORED` to distinguish a business error from a `REJECTED` decision (§2.1.3). + +**Why excluded.** Naming/semantics alignment — jim's vocabulary covers more cases (separates decision from error) and +uses past-tense states consistently. + +#### 6 — Per-location error nesting (`LocationErrorCollection` / `LocationError`) + +**What it is (original).** Errors are nested per location: a `LocationErrorCollection` keyed by `bpn` containing +`LocationError` entries. + +**jim's approach.** A flat `errors[]` array where each entry has an optional `specifier` scoping it to a BPN; if +`specifier` is omitted the error applies to the whole certificate (§4.3.3, §4.4.5). + +**Why excluded.** jim's flat-with-specifier form conveys the same information with a simpler structure. + +#### 7 — Consolidated HTTP status table incl. `501 Not Implemented` + +**What it is (original).** A single table of HTTP status codes used across the API (200, 202, 400, 404, 500, 501), +including `501 Not Implemented` for unsupported features. + +**jim's approach.** Error responses are documented per endpoint (200/202/400/404/500 appear where relevant); there is no +consolidated table and no `501` as it is not needed (The DSP catalog `conformsTo` signals what is implemented). + +**Why excluded.** jim documents errors inline; the consolidated table and `501` were not carried over. + +### Excluded by the "never reference EDC" rule + +#### 8 — `cx-taxo:CCMAPI` EDC asset structure + +**What it is (original).** The Certificate Management API is offered as an EDC asset: `dct:type`/`dct:subject`, +`cx-common:version`, a `dataAddress` (`HttpData`, proxy flags), and a rule that only one asset per (subject, version) +may exist per BPNL. + +**jim's approach.** Endpoints are exposed as DSP datasets (§4.3.5, §4.4.8) described by `conformsTo` and a `http-pull` +distribution `format` per the DSP and Data Plane Signaling Specifications, without EDC-specific asset structure. + +**Why excluded.** EDC-specific. + +#### 9 — `senderFeedbackUrl` routing mechanism + +**What it is (original).** A URL in the push payload telling the provider where to expect feedback, with a normative +note that it is a temporary measure pending EDC `.well-known` support. + +**jim's approach.** Feedback is correlated by `exchangeId` and delivered to the provider's acceptance endpoint reached +via the DSP dataset; no out-of-band feedback URL. + +**Why excluded.** EDC-coupled and superseded by the new correlation model. + +#### 10 — Message Flow Expectations and Business Application Provider obligations + +**What it is (original).** A normative bullet list of provider/consumer MUSTs (catalog exposure, access/usage policy on +the offer, etc.) and obligations on Business Application Providers. + +**jim's approach.** Conformance is expressed through the per-endpoint normative requirements in §4; there is no separate +flow-expectations list, and Business Application Provider is not a modelled role. + +**Why excluded.** The list is largely framed around EDC catalog/offer mechanics. + +#### 11 — PUSH / PULL / PUSH-then-PULL sequence diagrams + +**What it is (original).** Three SVG sequence diagrams depicting the EDC-mediated flows. + +**jim's approach.** A single Mermaid sequence diagram (§3) covering negotiation, request, fulfillment, retrieval, and +acceptance using the DSP agent roles. + +**Why excluded.** The original diagrams depict EDC components and flows and no longer applies. + +#### 12 — EDC-proxies-4xx/5xx-as-`500` behavior note + +**What it is (original).** A caveat that the open-source EDC proxies any 4xx/5xx as `500`, so clients must tolerate it. + +**jim's approach.** Not present; jim's error semantics are described directly. + +**Why excluded.** EDC-specific operational behavior. + +### Excluded — SAMM dropped + +#### 13 — SAMM aspect model + +**What it is (original).** The certificate data model is a SAMM aspect, +`urn:samm:io.catenax.business_partner_certificate:3.1.0`, authored in RDF/Turtle, released under CC-BY-4.0, with JSON +Schema / AAS / docs generated by the ESMF tooling (per CX-0003). + +**jim's approach.** The data model is authoritative inline JSON Schema in the OpenAPI specs plus CloudEvents +`dataschema` URLs. + +**Why excluded.** "CloudEvents + JSON Schema is decisive" — SAMM is not used. + +#### 14 — v3.1.0 BPNS/BPNA backward-compatibility note + +**What it is (original).** A note that the SAMM model v3.1.0 changed `enclosedSitesBpn` to accept BPNA in addition to +BPNS, breaking backward compatibility with v3.0.0. + +**jim's approach.** `enclosedSites` accept BPNS or BPNA by definition; there is no SAMM versioning to reconcile. + +**Why excluded.** Tied to SAMM model versioning, which is not used. + +### Excluded — terminology prose + +#### 15 — Remaining domain prose + +**What it is (original).** Narrative subsections on registration/issuing authority, and validity guidance (use the +issue/signature date when valid-from is absent; `31.12.9999` for no expiry). + +**jim's approach.** The combined doc includes the metadata-attribute glossary (§5.5) and certificate-type catalog +(§5.4); the remaining narrative was not carried over. + +**Why excluded.** Lower-value prose not required to define the wire protocol. + +### Excluded — references and misc + +#### 16 — Normative refs CX-0018, CX-0003; non-normative CX-0001 + +**What it is (original).** References to CX-0018 (Dataspace Connectivity / EDC connector conformance), CX-0003 (SAMM), +and CX-0001 (Participant Agent Registration). + +**jim's approach.** References DSP directly, uses JSON Schema (not SAMM), and cites CX-0000/CX-0151 (plus the +incorporated CX-0152 and CX-0010). + +**Why excluded.** CX-0018 and CX-0003 anchor EDC and SAMM respectively (both excluded); CX-0001 was not needed. + +#### 17 — "not replacing the existing publication semantic model" caveat + +**What it is (original).** A statement that the standard does not replace the existing publication semantic model. + +**jim's approach.** Not present; the CloudEvents proposal does not frame itself relative to the SAMM publication model. + +**Why excluded.** The caveat is meaningful only in the SAMM framing. + +### Excluded — redundant in this model + +#### 18 — `uploader` + +**What it is (original).** The original certificate metadata carries an `uploader` property — the BPNL of the business +partner who originally provided the certificate data (for example, a company that supplied its certificate to a +validating business application provider). + +**jim's approach.** The certificate is served by the Certificate Provider over its own API; the party that provides the +certificate data is, by definition, the participant exposing it. The provider's identity is already established by the +authenticated Dataspace Protocol interaction (and carried as the CloudEvent `source` on lifecycle and fulfillment +events). There is therefore no separate `uploader` field. + +**Why excluded.** `uploader` is redundant: in this model the uploader is always the participant (the Certificate +Provider) that exposes the certificate, so recording it as a distinct metadata property adds no information. Removed +from +`CertificateMetadata` and `CertificateQueryResponse` (and the §5.5 glossary) in both the combined Markdown and the +provider OpenAPI spec. + diff --git a/docs/standards/CX-0135-CompanyCertificateManagement/certificate-consumer-notification-api-combined.yaml b/docs/standards/CX-0135-CompanyCertificateManagement/certificate-consumer-notification-api-combined.yaml new file mode 100644 index 0000000000..50a24532a6 --- /dev/null +++ b/docs/standards/CX-0135-CompanyCertificateManagement/certificate-consumer-notification-api-combined.yaml @@ -0,0 +1,561 @@ +openapi: 3.0.3 +info: + title: Certificate Consumer Notification API + description: | + HTTP API exposed by a Certificate Consumer to receive notifications from + Certificate Providers, as defined in CX-0135 Section 4.3. + + Notifications are delivered to a single endpoint and distinguished by the + CloudEvents `type` attribute. Two event types are defined: certificate + lifecycle status events (`org.catena-x.ccm.CertificateLifecycleStatus.v1`, + Section 4.3.1) and certificate fulfillment status notifications + (`org.catena-x.ccm.CertificateFulfillmentStatus.v1`, Section 4.3.2). Each + carries a status value in `data.status`. + + All requests MUST use HTTPS (TLS 1.2 or higher). Authorization MUST adhere + to CX-0000 (CloudEventsFoundation) Section 4. + version: 1.0.0 + +servers: + - url: https://{base} + description: Certificate Consumer base URL + variables: + base: + default: api.example.com + description: Host serving the Certificate Consumer Notification API + +paths: + /certificate-notifications: + post: + summary: Receive a certificate notification event + description: | + Invoked by a Certificate Provider to deliver a notification to the + Certificate Consumer. Two event types are accepted, distinguished by the + CloudEvents `type`: a certificate lifecycle status event and a + certificate fulfillment status notification. The request body is a single + CloudEvent or a batch (JSON array) of CloudEvents as defined in CX-0000. + + For lifecycle events, only the `CREATED` status opens a Certificate + Exchange; `MODIFIED` and `WITHDRAWN` do not. Fulfillment notifications + report the Fulfillment status of an exchange the consumer opened via a + certificate request and are correlated by `exchangeId`. + operationId: postCertificateNotifications + requestBody: + required: true + content: + application/cloudevents+json: + schema: + oneOf: + - $ref: '#/components/schemas/CertificateLifecycleStatusEvent' + - $ref: '#/components/schemas/CertificateFulfillmentStatusEvent' + - type: array + items: + oneOf: + - $ref: '#/components/schemas/CertificateLifecycleStatusEvent' + - $ref: '#/components/schemas/CertificateFulfillmentStatusEvent' + examples: + created: + summary: Status CREATED + value: + specversion: "1.0" + type: org.catena-x.ccm.CertificateLifecycleStatus.v1 + source: urn:bpn:BPNL0000000001AB + subject: BPNL0000000002CD + id: a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d + time: "2025-05-04T07:00:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-lifecycle-status.json + data: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + version: 1 + status: CREATED + datasetId: dataset-ccm-cert-abc123 + certificateType: ISO9001 + validFrom: "2023-01-25" + validUntil: "2026-01-24" + enclosedSiteBpns: + - BPNS00000003AYRE + - BPNA000000000001 + modified: + summary: Status MODIFIED (new version) + value: + specversion: "1.0" + type: org.catena-x.ccm.CertificateLifecycleStatus.v1 + source: urn:bpn:BPNL0000000001AB + subject: BPNL0000000002CD + id: b2c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e + time: "2025-08-01T07:00:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-lifecycle-status.json + data: + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + version: 2 + status: MODIFIED + datasetId: dataset-ccm-cert-abc123 + certificateType: ISO9001 + validFrom: "2023-01-25" + validUntil: "2027-01-24" + enclosedSiteBpns: + - BPNS00000003AYRE + - BPNA000000000001 + withdrawn: + summary: Status WITHDRAWN (no validity) + value: + specversion: "1.0" + type: org.catena-x.ccm.CertificateLifecycleStatus.v1 + source: urn:bpn:BPNL0000000001AB + subject: BPNL0000000002CD + id: c3d4e5f6-7a8b-9c0d-1e2f-3a4b5c6d7e8f + time: "2025-09-01T07:00:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-lifecycle-status.json + data: + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + version: 2 + status: WITHDRAWN + datasetId: dataset-ccm-cert-abc123 + certificateType: ISO9001 + batch: + summary: A batch of lifecycle status events + value: + - specversion: "1.0" + type: org.catena-x.ccm.CertificateLifecycleStatus.v1 + source: urn:bpn:BPNL0000000001AB + subject: BPNL0000000002CD + id: a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d + time: "2025-05-04T07:00:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-lifecycle-status.json + data: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + version: 1 + status: CREATED + datasetId: dataset-ccm-cert-abc123 + certificateType: ISO9001 + validFrom: "2023-01-25" + validUntil: "2026-01-24" + - specversion: "1.0" + type: org.catena-x.ccm.CertificateLifecycleStatus.v1 + source: urn:bpn:BPNL0000000001AB + subject: BPNL0000000002CD + id: d4e5f6a7-8b9c-0d1e-2f3a-4b5c6d7e8f90 + time: "2025-09-01T07:00:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-lifecycle-status.json + data: + certificateId: cert-770e8400-e29b-41d4-a716-446655449999 + version: 3 + status: WITHDRAWN + datasetId: dataset-ccm-cert-xyz789 + certificateType: ISO14001 + fulfillmentFulfilled: + summary: Fulfillment status FULFILLED + value: + specversion: "1.0" + type: org.catena-x.ccm.CertificateFulfillmentStatus.v1 + source: urn:bpn:BPNL0000000001AB + subject: BPNL0000000002CD + id: f0e1d2c3-b4a5-6789-0abc-def012345678 + time: "2025-05-04T07:30:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-fulfillment-status.json + data: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + status: FULFILLED + fulfillmentDeclined: + summary: Fulfillment status DECLINED + value: + specversion: "1.0" + type: org.catena-x.ccm.CertificateFulfillmentStatus.v1 + source: urn:bpn:BPNL0000000001AB + subject: BPNL0000000002CD + id: a1c2e3f4-5b6d-7e8f-9a0b-1c2d3e4f5a6b + time: "2025-05-04T07:30:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-fulfillment-status.json + data: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + status: DECLINED + errors: + - message: Certificate type not offered for the requested location + responses: + '204': + description: Event(s) accepted; response body is empty. + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '500': + $ref: '#/components/responses/InternalServerError' + + /certificate-acceptance-status/{id}: + get: + summary: Query the acceptance status of an exchange + description: | + Invoked by a Certificate Provider to query the current acceptance + status of a Certificate Exchange it opened. The `{id}` path parameter + MUST be the `exchangeId` assigned when the exchange was opened. + operationId: getCertificateAcceptanceStatus + parameters: + - name: id + in: path + required: true + description: | + The `exchangeId` of the Certificate Exchange. + schema: + type: string + responses: + '200': + description: | + Current acceptance status of the certificate. The `status` value is + one of the Acceptance-phase states, including the non-terminal + `RETRIEVED` indicating that the certificate has been retrieved but + acceptance has not yet concluded. + content: + application/json: + schema: + $ref: '#/components/schemas/CertificateAcceptanceStatusResponse' + examples: + retrieved: + summary: Status RETRIEVED + value: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + status: RETRIEVED + accepted: + summary: Status ACCEPTED + value: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + status: ACCEPTED + rejected: + summary: Status REJECTED + value: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + status: REJECTED + errors: + - message: Certificate has expired + - specifier: BPNS000000000002 + message: Site BPNS000000000002 has been Rejected + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '500': + $ref: '#/components/responses/InternalServerError' + +components: + schemas: + CertificateLifecycleStatusEvent: + type: object + description: | + CloudEvents 1.0 envelope carrying a certificate lifecycle status + payload. The lifecycle transition is carried in `data.status`. + required: + - specversion + - type + - source + - id + - time + - data + properties: + specversion: + type: string + enum: ["1.0"] + description: The CloudEvents specification version. + type: + type: string + enum: [org.catena-x.ccm.CertificateLifecycleStatus.v1] + description: The event type identifier. + source: + type: string + format: uri + description: | + URI identifying the producing Certificate Provider. Typically a + BPN-scoped URN (for example, `urn:bpn:BPNL0000000001AB`). + subject: + type: string + description: | + The BPN of the recipient (Certificate Consumer) the event is + addressed to. + id: + type: string + description: Unique identifier of this event instance. + time: + type: string + format: date-time + description: RFC 3339 timestamp at which the event was produced. + datacontenttype: + type: string + enum: [application/json] + description: Content type of the `data` member. + dataschema: + type: string + format: uri + description: URI of the JSON Schema describing the `data` payload. + data: + $ref: '#/components/schemas/CertificateLifecycleStatus' + + CertificateStatusBase: + type: object + description: | + Common certificate-status base payload. Carries the `exchangeId` (when + the event belongs to a Certificate Exchange), the subject `certificateId`, + and a `status`. Concrete event payloads derive from this schema and + constrain `status` to the values defined for their endpoint. + required: + - certificateId + - status + properties: + exchangeId: + type: string + description: | + Identifier of the Certificate Exchange the event belongs to. Distinct + from the CloudEvent `id`. Present when the event pertains to an + exchange; subtypes may require it. + certificateId: + type: string + description: | + Unique identifier of the certificate the status applies to, usable + in `GET /certificates/{id}` on the Certificate Provider API. + status: + type: string + description: | + The status value. Subtypes constrain the permitted set of values. + + CertificateLifecycleStatus: + description: | + Certificate lifecycle status payload, derived from `CertificateStatusBase`. + `exchangeId` is present when `status` is `CREATED` (the event opens the + exchange) and absent for `MODIFIED`/`WITHDRAWN`. `validFrom` and + `validUntil` are REQUIRED when `status` is `CREATED` or `MODIFIED` and MAY + be omitted when `status` is `WITHDRAWN`. + allOf: + - $ref: '#/components/schemas/CertificateStatusBase' + - type: object + required: + - version + - datasetId + - certificateType + properties: + version: + type: integer + description: | + Version of the certificate. Together with `certificateId` it + identifies the specific certificate. + status: + type: string + enum: [CREATED, MODIFIED, WITHDRAWN] + description: | + The lifecycle status. `CREATED` opens a Certificate Exchange; + `MODIFIED` and `WITHDRAWN` do not. + datasetId: + type: string + description: | + Identifier of the DSP dataset under which the certificate is + exposed in the Certificate Provider's catalog, enabling the + consumer to locate and negotiate the certificate via the + Dataspace Protocol. + certificateType: + type: string + description: Opaque string identifying the certificate type (e.g. `ISO9001`). + validFrom: + type: string + format: date + description: | + Inclusive start date of the validity period (ISO 8601 full-date, + `YYYY-MM-DD`). Required when `status` is `CREATED` or `MODIFIED`. + validUntil: + type: string + format: date + description: | + Inclusive end date of the validity period (ISO 8601 full-date, + `YYYY-MM-DD`). Same presence rules as `validFrom`. + enclosedSiteBpns: + type: array + description: | + BPNs of the sites or addresses the certificate applies to. If + omitted, the certificate applies to the issuing legal entity as a + whole. + items: + type: string + + CertificateFulfillmentStatusEvent: + type: object + description: | + CloudEvents 1.0 envelope carrying a certificate fulfillment status + notification. The Fulfillment-phase status is carried in `data.status`. + required: + - specversion + - type + - source + - id + - time + - data + properties: + specversion: + type: string + enum: ["1.0"] + description: The CloudEvents specification version. + type: + type: string + enum: [org.catena-x.ccm.CertificateFulfillmentStatus.v1] + description: The event type identifier. + source: + type: string + format: uri + description: | + URI identifying the producing Certificate Provider. Typically a + BPN-scoped URN (for example, `urn:bpn:BPNL0000000001AB`). + subject: + type: string + description: | + The BPN of the recipient (Certificate Consumer) the event is + addressed to. + id: + type: string + description: Unique identifier of this event instance. + time: + type: string + format: date-time + description: RFC 3339 timestamp at which the event was produced. + datacontenttype: + type: string + enum: [application/json] + description: Content type of the `data` member. + dataschema: + type: string + format: uri + description: URI of the JSON Schema describing the `data` payload. + data: + $ref: '#/components/schemas/CertificateFulfillmentStatus' + + CertificateFulfillmentStatus: + description: | + Certificate fulfillment status payload, derived from + `CertificateStatusBase`. Reports the Fulfillment-phase status of a + consumer-initiated Certificate Exchange and is correlated by the + required `exchangeId`. It is the push counterpart of the + `GET /certificate-requests/{id}` poll on the Certificate Provider API. + allOf: + - $ref: '#/components/schemas/CertificateStatusBase' + - type: object + required: + - exchangeId + properties: + status: + type: string + enum: [ACKNOWLEDGED, CERTIFICATION_REQUESTED, FULFILLED, DECLINED, FAILED] + description: | + The Fulfillment status. Providers SHOULD push at least the + terminal outcomes (`FULFILLED`, `DECLINED`, `FAILED`); + intermediate states MAY also be pushed. + errors: + type: array + description: | + Error details. MUST be present and non-empty when `status` is + `DECLINED` or `FAILED`; otherwise absent. + items: + $ref: '#/components/schemas/StatusError' + + CertificateAcceptanceStatusResponse: + description: | + Current acceptance status of a Certificate Exchange on the Certificate + Consumer, derived from `CertificateStatusBase`. The `status` property + conveys the Acceptance-phase state, including the non-terminal + `RETRIEVED` value. + allOf: + - $ref: '#/components/schemas/CertificateStatusBase' + - type: object + required: + - exchangeId + properties: + status: + type: string + enum: [RETRIEVED, ACCEPTED, REJECTED, ERRORED] + description: The current acceptance status. + errors: + type: array + description: | + Error details. MUST NOT be present when `status` is `RETRIEVED` + or `ACCEPTED`. MUST be present and non-empty when `status` is + `REJECTED` or `ERRORED`. + items: + $ref: '#/components/schemas/StatusError' + + StatusError: + type: object + required: + - message + properties: + message: + type: string + description: Human-readable description of the error. + specifier: + type: string + description: | + Optional identifier scoping the error to a particular element of + the certificate, such as a site BPN. If omitted, the error + applies to the certificate as a whole. + + Error: + type: object + required: + - message + properties: + message: + type: string + description: Human-readable error description. + + responses: + BadRequest: + description: The request was malformed or failed validation. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + Unauthorized: + description: The request was not authenticated. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + Forbidden: + description: The caller is not authorized to perform this operation. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + NotFound: + description: | + The referenced certificate is unknown to the Certificate Consumer. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + InternalServerError: + description: An unexpected error occurred on the server. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + description: | + Bearer token as defined by CX-0000 Section 4 (HTTPS Binding). + +security: + - bearerAuth: [] diff --git a/docs/standards/CX-0135-CompanyCertificateManagement/certificate-consumer-notification-api-jim.yaml b/docs/standards/CX-0135-CompanyCertificateManagement/certificate-consumer-notification-api-jim.yaml new file mode 100644 index 0000000000..bc3d4736cb --- /dev/null +++ b/docs/standards/CX-0135-CompanyCertificateManagement/certificate-consumer-notification-api-jim.yaml @@ -0,0 +1,559 @@ +openapi: 3.0.3 +info: + title: Certificate Consumer Notification API + description: | + HTTP API exposed by a Certificate Consumer to receive notifications from + Certificate Providers, as defined in CX-0135 Section 4.3. + + Notifications are delivered to a single endpoint and distinguished by the + CloudEvents `type` attribute. Two event types are defined: certificate + lifecycle status events (`org.catena-x.ccm.CertificateLifecycleStatus.v1`, + Section 4.3.1) and certificate fulfillment status notifications + (`org.catena-x.ccm.CertificateFulfillmentStatus.v1`, Section 4.3.2). Each + carries a status value in `data.status`. + + All requests MUST use HTTPS (TLS 1.2 or higher). Authorization MUST adhere + to CX-0000 (CloudEventsFoundation) Section 4. + version: 1.0.0 + +servers: + - url: https://{base} + description: Certificate Consumer base URL + variables: + base: + default: api.example.com + description: Host serving the Certificate Consumer Notification API + +paths: + /certificate-notifications: + post: + summary: Receive a certificate notification event + description: | + Invoked by a Certificate Provider to deliver a notification to the + Certificate Consumer. Two event types are accepted, distinguished by the + CloudEvents `type`: a certificate lifecycle status event and a + certificate fulfillment status notification. The request body is a single + CloudEvent or a batch (JSON array) of CloudEvents as defined in CX-0000. + + For lifecycle events, only the `CREATED` status opens a Certificate + Exchange; `MODIFIED` and `WITHDRAWN` do not. Fulfillment notifications + report the Fulfillment status of an exchange the consumer opened via a + certificate request and are correlated by `exchangeId`. + operationId: postCertificateNotifications + requestBody: + required: true + content: + application/cloudevents+json: + schema: + oneOf: + - $ref: '#/components/schemas/CertificateLifecycleStatusEvent' + - $ref: '#/components/schemas/CertificateFulfillmentStatusEvent' + - type: array + items: + oneOf: + - $ref: '#/components/schemas/CertificateLifecycleStatusEvent' + - $ref: '#/components/schemas/CertificateFulfillmentStatusEvent' + examples: + created: + summary: Status CREATED + value: + specversion: "1.0" + type: org.catena-x.ccm.CertificateLifecycleStatus.v1 + source: urn:bpn:BPNL0000000001AB + subject: BPNL0000000002CD + id: a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d + time: "2025-05-04T07:00:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-lifecycle-status.json + data: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + version: 1 + status: CREATED + datasetId: dataset-ccm-cert-abc123 + certificateType: ISO9001 + validFrom: "2023-01-25" + validUntil: "2026-01-24" + locationBpns: + - BPNS00000003AYRE + - BPNA000000000001 + modified: + summary: Status MODIFIED (new version) + value: + specversion: "1.0" + type: org.catena-x.ccm.CertificateLifecycleStatus.v1 + source: urn:bpn:BPNL0000000001AB + subject: BPNL0000000002CD + id: b2c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e + time: "2025-08-01T07:00:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-lifecycle-status.json + data: + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + version: 2 + status: MODIFIED + datasetId: dataset-ccm-cert-abc123 + certificateType: ISO9001 + validFrom: "2023-01-25" + validUntil: "2027-01-24" + locationBpns: + - BPNS00000003AYRE + - BPNA000000000001 + withdrawn: + summary: Status WITHDRAWN (no validity) + value: + specversion: "1.0" + type: org.catena-x.ccm.CertificateLifecycleStatus.v1 + source: urn:bpn:BPNL0000000001AB + subject: BPNL0000000002CD + id: c3d4e5f6-7a8b-9c0d-1e2f-3a4b5c6d7e8f + time: "2025-09-01T07:00:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-lifecycle-status.json + data: + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + version: 2 + status: WITHDRAWN + datasetId: dataset-ccm-cert-abc123 + certificateType: ISO9001 + batch: + summary: A batch of lifecycle status events + value: + - specversion: "1.0" + type: org.catena-x.ccm.CertificateLifecycleStatus.v1 + source: urn:bpn:BPNL0000000001AB + subject: BPNL0000000002CD + id: a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d + time: "2025-05-04T07:00:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-lifecycle-status.json + data: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + version: 1 + status: CREATED + datasetId: dataset-ccm-cert-abc123 + certificateType: ISO9001 + validFrom: "2023-01-25" + validUntil: "2026-01-24" + - specversion: "1.0" + type: org.catena-x.ccm.CertificateLifecycleStatus.v1 + source: urn:bpn:BPNL0000000001AB + subject: BPNL0000000002CD + id: d4e5f6a7-8b9c-0d1e-2f3a-4b5c6d7e8f90 + time: "2025-09-01T07:00:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-lifecycle-status.json + data: + certificateId: cert-770e8400-e29b-41d4-a716-446655449999 + version: 3 + status: WITHDRAWN + datasetId: dataset-ccm-cert-xyz789 + certificateType: ISO14001 + fulfillmentFulfilled: + summary: Fulfillment status FULFILLED + value: + specversion: "1.0" + type: org.catena-x.ccm.CertificateFulfillmentStatus.v1 + source: urn:bpn:BPNL0000000001AB + subject: BPNL0000000002CD + id: f0e1d2c3-b4a5-6789-0abc-def012345678 + time: "2025-05-04T07:30:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-fulfillment-status.json + data: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + status: FULFILLED + fulfillmentDeclined: + summary: Fulfillment status DECLINED + value: + specversion: "1.0" + type: org.catena-x.ccm.CertificateFulfillmentStatus.v1 + source: urn:bpn:BPNL0000000001AB + subject: BPNL0000000002CD + id: a1c2e3f4-5b6d-7e8f-9a0b-1c2d3e4f5a6b + time: "2025-05-04T07:30:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-fulfillment-status.json + data: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + status: DECLINED + errors: + - message: Certificate type not offered for the requested location + responses: + '204': + description: Event(s) accepted; response body is empty. + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '500': + $ref: '#/components/responses/InternalServerError' + + /certificate-acceptance-status/{id}: + get: + summary: Query the acceptance status of an exchange + description: | + Invoked by a Certificate Provider to query the current acceptance + status of a Certificate Exchange it opened. The `{id}` path parameter + MUST be the `exchangeId` assigned when the exchange was opened. + operationId: getCertificateAcceptanceStatus + parameters: + - name: id + in: path + required: true + description: | + The `exchangeId` of the Certificate Exchange. + schema: + type: string + responses: + '200': + description: | + Current acceptance status of the certificate. The `status` value is + one of the Acceptance-phase states, including the non-terminal + `RETRIEVED` indicating that the certificate has been retrieved but + acceptance has not yet concluded. + content: + application/json: + schema: + $ref: '#/components/schemas/CertificateAcceptanceStatusResponse' + examples: + retrieved: + summary: Status RETRIEVED + value: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + status: RETRIEVED + accepted: + summary: Status ACCEPTED + value: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + status: ACCEPTED + rejected: + summary: Status REJECTED + value: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + status: REJECTED + errors: + - message: Certificate has expired + - specifier: BPNS000000000002 + message: Site BPNS000000000002 has been Rejected + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '500': + $ref: '#/components/responses/InternalServerError' + +components: + schemas: + CertificateLifecycleStatusEvent: + type: object + description: | + CloudEvents 1.0 envelope carrying a certificate lifecycle status + payload. The lifecycle transition is carried in `data.status`. + required: + - specversion + - type + - source + - id + - time + - data + properties: + specversion: + type: string + enum: ["1.0"] + description: The CloudEvents specification version. + type: + type: string + enum: [org.catena-x.ccm.CertificateLifecycleStatus.v1] + description: The event type identifier. + source: + type: string + format: uri + description: | + URI identifying the producing Certificate Provider. Typically a + BPN-scoped URN (for example, `urn:bpn:BPNL0000000001AB`). + subject: + type: string + description: | + The BPN of the recipient (Certificate Consumer) the event is + addressed to. + id: + type: string + description: Unique identifier of this event instance. + time: + type: string + format: date-time + description: RFC 3339 timestamp at which the event was produced. + datacontenttype: + type: string + enum: [application/json] + description: Content type of the `data` member. + dataschema: + type: string + format: uri + description: URI of the JSON Schema describing the `data` payload. + data: + $ref: '#/components/schemas/CertificateLifecycleStatus' + + CertificateStatusBase: + type: object + description: | + Common certificate-status base payload. Carries the `exchangeId` (when + the event belongs to a Certificate Exchange), the subject `certificateId`, + and a `status`. Concrete event payloads derive from this schema and + constrain `status` to the values defined for their endpoint. + required: + - certificateId + - status + properties: + exchangeId: + type: string + description: | + Identifier of the Certificate Exchange the event belongs to. Distinct + from the CloudEvent `id`. Present when the event pertains to an + exchange; subtypes may require it. + certificateId: + type: string + description: | + Unique identifier of the certificate the status applies to, usable + in `GET /certificates/{id}` on the Certificate Provider API. + status: + type: string + description: | + The status value. Subtypes constrain the permitted set of values. + + CertificateLifecycleStatus: + description: | + Certificate lifecycle status payload, derived from `CertificateStatusBase`. + `exchangeId` is present when `status` is `CREATED` (the event opens the + exchange) and absent for `MODIFIED`/`WITHDRAWN`. `validFrom` and + `validUntil` are REQUIRED when `status` is `CREATED` or `MODIFIED` and MAY + be omitted when `status` is `WITHDRAWN`. + allOf: + - $ref: '#/components/schemas/CertificateStatusBase' + - type: object + required: + - version + - datasetId + - certificateType + properties: + version: + type: integer + description: | + Version of the certificate. Together with `certificateId` it + identifies the specific certificate. + status: + type: string + enum: [CREATED, MODIFIED, WITHDRAWN] + description: | + The lifecycle status. `CREATED` opens a Certificate Exchange; + `MODIFIED` and `WITHDRAWN` do not. + datasetId: + type: string + description: | + Identifier of the DSP dataset under which the certificate is + exposed in the Certificate Provider's catalog. + certificateType: + type: string + description: Opaque string identifying the certificate type (e.g. `ISO9001`). + validFrom: + type: string + format: date + description: | + Inclusive start date of the validity period (ISO 8601 full-date, + `YYYY-MM-DD`). Required when `status` is `CREATED` or `MODIFIED`. + validUntil: + type: string + format: date + description: | + Inclusive end date of the validity period (ISO 8601 full-date, + `YYYY-MM-DD`). Same presence rules as `validFrom`. + locationBpns: + type: array + description: | + BPNs of the sites or addresses the certificate applies to. If + omitted, the certificate applies to the issuing legal entity as a + whole. + items: + type: string + + CertificateFulfillmentStatusEvent: + type: object + description: | + CloudEvents 1.0 envelope carrying a certificate fulfillment status + notification. The Fulfillment-phase status is carried in `data.status`. + required: + - specversion + - type + - source + - id + - time + - data + properties: + specversion: + type: string + enum: ["1.0"] + description: The CloudEvents specification version. + type: + type: string + enum: [org.catena-x.ccm.CertificateFulfillmentStatus.v1] + description: The event type identifier. + source: + type: string + format: uri + description: | + URI identifying the producing Certificate Provider. Typically a + BPN-scoped URN (for example, `urn:bpn:BPNL0000000001AB`). + subject: + type: string + description: | + The BPN of the recipient (Certificate Consumer) the event is + addressed to. + id: + type: string + description: Unique identifier of this event instance. + time: + type: string + format: date-time + description: RFC 3339 timestamp at which the event was produced. + datacontenttype: + type: string + enum: [application/json] + description: Content type of the `data` member. + dataschema: + type: string + format: uri + description: URI of the JSON Schema describing the `data` payload. + data: + $ref: '#/components/schemas/CertificateFulfillmentStatus' + + CertificateFulfillmentStatus: + description: | + Certificate fulfillment status payload, derived from + `CertificateStatusBase`. Reports the Fulfillment-phase status of a + consumer-initiated Certificate Exchange and is correlated by the + required `exchangeId`. It is the push counterpart of the + `GET /certificate-requests/{id}` poll on the Certificate Provider API. + allOf: + - $ref: '#/components/schemas/CertificateStatusBase' + - type: object + required: + - exchangeId + properties: + status: + type: string + enum: [ACKNOWLEDGED, CERTIFICATION_REQUESTED, FULFILLED, DECLINED, FAILED] + description: | + The Fulfillment status. Providers SHOULD push at least the + terminal outcomes (`FULFILLED`, `DECLINED`, `FAILED`); + intermediate states MAY also be pushed. + errors: + type: array + description: | + Error details. MUST be present and non-empty when `status` is + `DECLINED` or `FAILED`; otherwise absent. + items: + $ref: '#/components/schemas/StatusError' + + CertificateAcceptanceStatusResponse: + description: | + Current acceptance status of a Certificate Exchange on the Certificate + Consumer, derived from `CertificateStatusBase`. The `status` property + conveys the Acceptance-phase state, including the non-terminal + `RETRIEVED` value. + allOf: + - $ref: '#/components/schemas/CertificateStatusBase' + - type: object + required: + - exchangeId + properties: + status: + type: string + enum: [RETRIEVED, ACCEPTED, REJECTED, ERRORED] + description: The current acceptance status. + errors: + type: array + description: | + Error details. MUST NOT be present when `status` is `RETRIEVED` + or `ACCEPTED`. MUST be present and non-empty when `status` is + `REJECTED` or `ERRORED`. + items: + $ref: '#/components/schemas/StatusError' + + StatusError: + type: object + required: + - message + properties: + message: + type: string + description: Human-readable description of the error. + specifier: + type: string + description: | + Optional identifier scoping the error to a particular element of + the certificate, such as a site BPN. If omitted, the error + applies to the certificate as a whole. + + Error: + type: object + required: + - message + properties: + message: + type: string + description: Human-readable error description. + + responses: + BadRequest: + description: The request was malformed or failed validation. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + Unauthorized: + description: The request was not authenticated. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + Forbidden: + description: The caller is not authorized to perform this operation. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + NotFound: + description: | + The referenced certificate is unknown to the Certificate Consumer. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + InternalServerError: + description: An unexpected error occurred on the server. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + description: | + Bearer token as defined by CX-0000 Section 4 (HTTPS Binding). + +security: + - bearerAuth: [] diff --git a/docs/standards/CX-0135-CompanyCertificateManagement/certificate-provider-api-combined.yaml b/docs/standards/CX-0135-CompanyCertificateManagement/certificate-provider-api-combined.yaml new file mode 100644 index 0000000000..573cd6cd2c --- /dev/null +++ b/docs/standards/CX-0135-CompanyCertificateManagement/certificate-provider-api-combined.yaml @@ -0,0 +1,965 @@ +openapi: 3.0.3 +info: + title: Certificate Provider API + description: | + HTTP API exposed by a Certificate Provider to accept certificate requests, + report request fulfillment status, serve certificate data, accept + acceptance status updates, and answer certificate queries, as defined in + CX-0135 Section 4.4. + + A certificate is identified by the (`certificateId`, `version`) pair. A + consumer-initiated request opens a Certificate Exchange; the Certificate + Provider assigns `certificateId` and `version` when it accepts the request. + + All requests MUST use HTTPS (TLS 1.2 or higher). Authorization MUST adhere + to CX-0000 (CloudEventsFoundation) Section 4. + version: 1.0.0 + +servers: + - url: https://{base} + description: Certificate Provider base URL + variables: + base: + default: api.example.com + description: Host serving the Certificate Provider API + +paths: + /certificate-requests: + post: + summary: Request a certificate + description: | + Invoked by a Certificate Consumer to request a certificate. A request + opens a consumer-initiated Certificate Exchange. On acceptance the + Certificate Provider assigns and returns a `certificateId` and + `version`, enabling the consumer to poll fulfillment status and later + retrieve the certificate. + operationId: createCertificateRequest + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CertificateRequest' + examples: + byType: + summary: Request a certificate by type and location + value: + certificateType: ISO9001 + enclosedSiteBpns: + - BPNS00000003AYRE + responses: + '202': + description: | + The request was accepted. The body carries the assigned + `certificateId` and `version` and the current fulfillment status. + content: + application/json: + schema: + $ref: '#/components/schemas/CertificateRequestResponse' + examples: + acknowledged: + summary: Accepted for asynchronous fulfillment + value: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + version: 1 + status: ACKNOWLEDGED + fulfilled: + summary: Certificate already available + value: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + version: 1 + status: FULFILLED + declined: + summary: Request refused + value: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + version: 1 + status: DECLINED + errors: + - message: Certificate type not offered for the requested location + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '500': + $ref: '#/components/responses/InternalServerError' + + /certificate-requests/{id}: + get: + summary: Poll the fulfillment status of a request + description: | + Invoked by a Certificate Consumer to poll the fulfillment status of a + previously submitted certificate request. This endpoint reports only the + Fulfillment phase; the consumer's acceptance outcome is reported + separately via `POST /certificate-acceptance-notifications`. + + Polling is optional: if the Certificate Consumer exposes the Certificate + Consumer Notification API, the Certificate Provider MAY instead push + these fulfillment status updates to it as a + `org.catena-x.ccm.CertificateFulfillmentStatus.v1` event (CX-0135 + Section 4.3.2). The push and the poll are equivalent and carry the same + `exchangeId` and `status`. + operationId: getCertificateRequestStatus + parameters: + - name: id + in: path + required: true + description: | + The `exchangeId` returned in the response to the certificate + request. + schema: + type: string + responses: + '200': + description: Current fulfillment status of the request. + content: + application/json: + schema: + $ref: '#/components/schemas/CertificateRequestStatus' + examples: + acknowledged: + summary: Status ACKNOWLEDGED + value: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + version: 1 + status: ACKNOWLEDGED + fulfilled: + summary: Status FULFILLED + value: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + version: 1 + status: FULFILLED + declined: + summary: Status DECLINED + value: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + version: 1 + status: DECLINED + errors: + - message: Certificate type not offered for the requested location + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '500': + $ref: '#/components/responses/InternalServerError' + + /certificates/{id}: + get: + summary: Retrieve certificate metadata + description: | + Retrieves the metadata of a certificate as `application/json`. The + certificate is metadata only; binary documents are not included. Each + associated document is listed by reference in the `documents` array and + retrieved independently via `GET /documents/{id}`. + Returns the latest version by default; a specific version may be + requested with the `version` query parameter (e.g. to retrieve the exact + version a Certificate Exchange concerns). The provider MUST be able to + return any version still referenced by a non-terminal Certificate + Exchange. + operationId: getCertificate + parameters: + - name: id + in: path + required: true + description: The unique identifier of the certificate. + schema: + type: string + - name: version + in: query + required: false + description: | + The specific version to retrieve. If omitted, the latest version is + returned. + schema: + type: integer + responses: + '200': + description: | + Certificate found. The response is the JSON metadata described by + `CertificateMetadata`, including the `documents` array referencing + the documents associated with the returned version. + content: + application/json: + schema: + $ref: '#/components/schemas/CertificateMetadata' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '500': + $ref: '#/components/responses/InternalServerError' + + /documents/{id}: + get: + summary: Retrieve a certificate document + description: | + Retrieves a certificate document by its identifier. The response body is + the document binary, served directly with the `Content-Type` set to the + document's `mediaType` (e.g. `application/pdf`) as advertised in the + certificate metadata. + A `documentId` is opaque and independent of any certificate version; the + same document may be referenced by multiple certificate versions, so no + `version` parameter is required. + operationId: getDocument + parameters: + - name: id + in: path + required: true + description: The opaque, version-independent identifier of the document. + schema: + type: string + responses: + '200': + description: | + Document found. The response body is the document binary, served with + the `Content-Type` set to the document's `mediaType`. + content: + application/pdf: + schema: + type: string + format: binary + image/png: + schema: + type: string + format: binary + application/octet-stream: + schema: + type: string + format: binary + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '500': + $ref: '#/components/responses/InternalServerError' + + /certificate-acceptance-notifications: + post: + summary: Submit a certificate acceptance status event + description: | + Invoked by a Certificate Consumer to report the acceptance status of a + certificate. The status is carried in `data.status` and the Certificate + Provider dispatches on it. The event is correlated to its Certificate + Exchange by the `exchangeId` in `data`. Acceptance MUST reference an + existing exchange (opened by a consumer request or a provider + notification); merely retrieving a certificate does not establish an + exchange. An unknown `exchangeId` is rejected with 404. The body is a + single CloudEvent or a batch (JSON array). + operationId: postCertificateAcceptanceNotification + requestBody: + required: true + content: + application/cloudevents+json: + schema: + oneOf: + - $ref: '#/components/schemas/CertificateAcceptanceStatusEvent' + - type: array + items: + $ref: '#/components/schemas/CertificateAcceptanceStatusEvent' + examples: + retrieved: + summary: Status RETRIEVED + value: + specversion: "1.0" + type: org.catena-x.ccm.CertificateAcceptanceStatus.v1 + source: urn:bpn:BPNL0000000002CD + subject: BPNL0000000001AB + id: e9f8d7c6-b5a4-9382-7160-5a4b3c2d1e0f + time: "2025-05-04T08:00:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-acceptance-status.json + data: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + status: RETRIEVED + accepted: + summary: Status ACCEPTED + value: + specversion: "1.0" + type: org.catena-x.ccm.CertificateAcceptanceStatus.v1 + source: urn:bpn:BPNL0000000002CD + subject: BPNL0000000001AB + id: a7b8c9d0-e1f2-3a4b-5c6d-7e8f9a0b1c2d + time: "2025-05-04T09:00:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-acceptance-status.json + data: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + status: ACCEPTED + rejected: + summary: Status REJECTED + value: + specversion: "1.0" + type: org.catena-x.ccm.CertificateAcceptanceStatus.v1 + source: urn:bpn:BPNL0000000002CD + subject: BPNL0000000001AB + id: f1a2b3c4-d5e6-7f8a-9b0c-1d2e3f4a5b6c + time: "2025-05-04T09:00:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-acceptance-status.json + data: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + status: REJECTED + errors: + - message: Certificate has expired + - message: Unexpected data format + - specifier: BPNS000000000002 + message: Site BPNS000000000002 has been Rejected + errored: + summary: Status ERRORED + value: + specversion: "1.0" + type: org.catena-x.ccm.CertificateAcceptanceStatus.v1 + source: urn:bpn:BPNL0000000002CD + subject: BPNL0000000001AB + id: c8d7e6f5-a4b3-2109-8765-432109fedcba + time: "2025-05-04T08:30:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-acceptance-status.json + data: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + status: ERRORED + errors: + - message: Certificate document is malformed and cannot be validated + responses: + '204': + description: Acceptance status accepted; response body is empty. + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '500': + $ref: '#/components/responses/InternalServerError' + + /certificates/search: + post: + summary: Search certificates + description: | + Returns certificates matching the supplied criteria. Results MAY be + paginated; when paginated, navigation URLs are provided in the `Link` + response header in accordance with RFC 8288 (Web Linking). Only the + `next` and `prev` link relations are guaranteed; `first` and `last` + MAY also be returned. To retrieve a subsequent page, the client + re-issues a `POST` with the same request body against the URL + provided in the corresponding `Link` value. + operationId: queryCertificates + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CertificateQuery' + examples: + byType: + summary: Query by certificate type and validity window + value: + certificateType: ISO9001 + from: "2023-01-25" + to: "2026-01-24" + limit: 50 + responses: + '200': + description: | + Matching certificates. When the result set is paginated, the + response includes a `Link` header per RFC 8288. + headers: + Link: + description: | + Web Linking pagination header (RFC 8288). Contains URLs for + the `next` and/or `prev` pages. Omitted when the response is + not paginated. + schema: + type: string + example: >- + ; rel="next", + ; rel="prev" + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/CertificateQueryResponse' + examples: + page: + summary: A single page of results + value: + - certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + version: 1 + datasetId: dataset-ccm-cert-abc123 + certificateType: ISO9001 + validFrom: "2023-01-25" + validUntil: "2026-01-24" + enclosedSiteBpns: + - BPNS00000003AYRE + - BPNA000000000001 + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '500': + $ref: '#/components/responses/InternalServerError' + +components: + schemas: + CertificateRequest: + type: object + description: Request body for `POST /certificate-requests`. + required: + - certificateType + properties: + certificateType: + type: string + description: | + Opaque string identifying the certificate type to be requested + (for example, `ISO9001`). + enclosedSiteBpns: + type: array + description: | + BPNs of the sites or addresses the request applies to. If omitted, + the request applies to the legal entity. + items: + type: string + + CertificateRequestResponse: + type: object + description: | + Response to an accepted certificate request. Carries the assigned + exchange and certificate identity and the initial fulfillment status. + required: + - exchangeId + - certificateId + - version + - status + properties: + exchangeId: + type: string + description: | + Identifier assigned to the opened Certificate Exchange. Used to poll + fulfillment status via `GET /certificate-requests/{id}`. + certificateId: + type: string + description: | + Identifier assigned to the requested certificate. Used to retrieve + the certificate via `GET /certificates/{id}`. + version: + type: integer + description: Version assigned to the requested certificate. + status: + type: string + enum: [ACKNOWLEDGED, CERTIFICATION_REQUESTED, FULFILLED, DECLINED] + description: The fulfillment status of the request. + errors: + type: array + description: | + Error details. MUST be present and non-empty when `status` is + `DECLINED`. + items: + $ref: '#/components/schemas/StatusError' + + CertificateRequestStatus: + type: object + description: | + Current fulfillment status of a certificate request. + required: + - exchangeId + - certificateId + - version + - status + properties: + exchangeId: + type: string + description: | + Identifier of the Certificate Exchange. Matches the `{id}` path + parameter of the request. + certificateId: + type: string + description: Unique identifier of the requested certificate. + version: + type: integer + description: The version of the requested certificate. + status: + type: string + enum: [ACKNOWLEDGED, CERTIFICATION_REQUESTED, FULFILLED, DECLINED, FAILED] + description: The current fulfillment status. + errors: + type: array + description: | + Error details. MUST be present and non-empty when `status` is + `DECLINED` or `FAILED`. + items: + $ref: '#/components/schemas/StatusError' + + CertificateDocument: + type: object + description: | + A reference to a document associated with a certificate version. The + binary is retrieved separately via `GET /documents/{id}`. + required: + - documentId + - mediaType + properties: + documentId: + type: string + description: | + Opaque, version-independent identifier of the document, used to + retrieve it via `GET /documents/{id}`. + examples: + - "doc-3fa85f64-5717-4562-b3fc-2c963f66afa6" + language: + type: string + description: | + Language of the document as an ISO 639-1 two-letter code + (e.g. `en` for English, `de` for German). Distinguishes documents + that differ only by language. + pattern: "^[a-z]{2}$" + examples: + - "en" + mediaType: + type: string + description: IANA media type of the document binary. + examples: + - "application/pdf" + - "image/png" + + EnclosedSite: + type: object + description: A site or address a certificate applies to. + required: + - bpn + properties: + bpn: + type: string + description: | + The Business Partner Number of the site (BPNS) or address (BPNA) + the certificate covers. + pattern: "^BPN[SA][a-zA-Z0-9]{12}$" + areaOfApplication: + type: string + description: | + Free-text detail of the areas or application types the certificate + is valid for at this specific location. + + CertificateIssuer: + type: object + description: The authority that issued the certificate. + required: + - issuerName + properties: + issuerName: + type: string + description: The name of the issuing authority (e.g. `TÜV Süd`). + issuerBpn: + type: string + description: The BPNL of the issuing authority, if a Catena-X business partner. + pattern: "^BPNL[a-zA-Z0-9]{12}$" + + CertificateValidator: + type: object + description: The party that can validate the certificate information. + required: + - validatorName + properties: + validatorName: + type: string + description: The name of the validator. + validatorBpn: + type: string + description: | + The BPNL of the validator. MAY be used as a free-text identifier + where a BPNL is not available. + + CertificateMetadata: + type: object + description: JSON metadata accompanying a retrieved certificate. + required: + - certificateId + - version + - certificateType + - certifiedBpn + - registrationNumber + - validFrom + - validUntil + - trustLevel + properties: + certificateId: + type: string + description: | + Unique identifier of the certificate. Matches the `{id}` path + parameter of the request. + version: + type: integer + description: | + The version returned — the version requested via the `version` query + parameter, or the latest version if none was specified. + certificateType: + type: string + description: Opaque string identifying the certificate type (e.g. `ISO9001`). + certificateTypeVersion: + type: string + description: | + The version of the certificate type itself (e.g. `2015` for + ISO 9001:2015). Distinct from the certificate's `version`. + certifiedBpn: + type: string + description: The BPNL of the certified legal entity the certificate is issued on. + pattern: "^BPNL[a-zA-Z0-9]{12}$" + registrationNumber: + type: string + description: The certificate's registration number at the issuing body. + validFrom: + type: string + format: date + description: | + Inclusive start date of the certificate validity period + (ISO 8601 full-date, `YYYY-MM-DD`). + validUntil: + type: string + format: date + description: | + Inclusive end date of the certificate validity period + (ISO 8601 full-date, `YYYY-MM-DD`). + trustLevel: + type: string + description: The degree to which the certificate has been validated. + enum: [none, low, medium, high, trusted] + areaOfApplication: + type: string + description: | + Free-text detail of the areas or application types the certificate + is valid for overall. Individual locations MAY narrow this via + `Location.areaOfApplication`. + enclosedSites: + type: array + description: | + The sites or addresses the certificate applies to. If omitted or + empty, the certificate applies to the issuing legal entity as a + whole. + items: + $ref: '#/components/schemas/EnclosedSite' + issuer: + $ref: '#/components/schemas/CertificateIssuer' + validator: + $ref: '#/components/schemas/CertificateValidator' + documents: + type: array + description: | + Documents associated with this certificate version. Each document is + retrieved independently via `GET /documents/{id}`. If omitted or + empty, the version has no associated documents. + items: + $ref: '#/components/schemas/CertificateDocument' + + CertificateAcceptanceStatusEvent: + type: object + description: | + CloudEvents 1.0 envelope carrying a certificate acceptance status + payload. The Acceptance-phase outcome is carried in `data.status`. + required: + - specversion + - type + - source + - id + - time + - data + properties: + specversion: + type: string + enum: ["1.0"] + type: + type: string + enum: [org.catena-x.ccm.CertificateAcceptanceStatus.v1] + source: + type: string + format: uri + description: | + URI identifying the producing Certificate Consumer. Typically a + BPN-scoped URN. + subject: + type: string + description: The BPN of the addressed Certificate Provider. + id: + type: string + description: Unique identifier of this event instance. + time: + type: string + format: date-time + datacontenttype: + type: string + enum: [application/json] + dataschema: + type: string + format: uri + data: + $ref: '#/components/schemas/CertificateAcceptanceStatus' + + CertificateStatusBase: + type: object + description: | + Common certificate-status base payload. Carries the `exchangeId` (when + the event belongs to a Certificate Exchange), the subject `certificateId`, + and a `status`. Concrete event payloads derive from this schema and + constrain `status` to the values defined for their endpoint. + required: + - certificateId + - status + properties: + exchangeId: + type: string + description: | + Identifier of the Certificate Exchange the event belongs to. Distinct + from the CloudEvent `id`. Present when the event pertains to an + exchange; subtypes may require it. + certificateId: + type: string + description: | + Unique identifier of the certificate the status applies to. + status: + type: string + description: | + The status value. Subtypes constrain the permitted set of values. + + CertificateAcceptanceStatus: + description: | + Certificate acceptance status payload, derived from + `CertificateStatusBase`. Reports on a Certificate Exchange, so + `exchangeId` is required. + allOf: + - $ref: '#/components/schemas/CertificateStatusBase' + - type: object + required: + - exchangeId + properties: + status: + type: string + enum: [RETRIEVED, ACCEPTED, REJECTED, ERRORED] + description: The acceptance status of the certificate. + errors: + type: array + description: | + Error details. MUST NOT be present when `status` is `RETRIEVED` + or `ACCEPTED`. MUST be present and non-empty when `status` is + `REJECTED` or `ERRORED`. + items: + $ref: '#/components/schemas/StatusError' + + StatusError: + type: object + required: + - message + properties: + message: + type: string + description: Human-readable description of the error. + specifier: + type: string + description: | + Optional identifier scoping the error to a particular element of + the certificate, such as a site BPN. If omitted, the error + applies to the certificate as a whole. + + CertificateQuery: + type: object + description: | + Request body for `POST /certificates/search`. All criteria are optional; + an empty query returns all accessible certificates. Criteria of different + kinds are combined with AND; within a single BPN list the match is + any-of. + properties: + certificateType: + type: string + description: | + Opaque string identifying the certificate type to be matched + (for example, `ISO9001`). + legalEntityBpns: + type: array + description: | + Filter to certificates issued for these legal entities (matched + against `certifiedBpn`). + items: + type: string + pattern: "^BPNL[a-zA-Z0-9]{12}$" + siteBpns: + type: array + description: | + Filter to certificates covering these sites (matched against the + `enclosedSites`). + items: + type: string + pattern: "^BPNS[a-zA-Z0-9]{12}$" + addressBpns: + type: array + description: | + Filter to certificates covering these addresses (matched against + the `enclosedSites`). + items: + type: string + pattern: "^BPNA[a-zA-Z0-9]{12}$" + from: + type: string + format: date + description: | + Inclusive lower bound of the certificate validity range + (ISO 8601 full-date). Only certificates whose `validFrom` is on + or after this date are returned. + to: + type: string + format: date + description: | + Inclusive upper bound of the certificate validity range + (ISO 8601 full-date). Only certificates whose `validUntil` is on + or before this date are returned. + limit: + type: integer + minimum: 1 + description: | + Maximum number of results to return in a single page. If + omitted, the Certificate Provider MAY apply a default limit. + + CertificateQueryResponse: + type: object + description: A single result entry in a certificate query response. + required: + - certificateId + - version + - datasetId + - certificateType + - certifiedBpn + - registrationNumber + - validFrom + - validUntil + - trustLevel + properties: + certificateId: + type: string + description: | + Unique identifier of the certificate, usable in + `GET /certificates/{id}`. + version: + type: integer + description: The latest version of the certificate. + datasetId: + type: string + description: | + Identifier of the DSP dataset under which the certificate is + exposed in the Certificate Provider's catalog, enabling the consumer + to locate and negotiate the certificate via the Dataspace Protocol. + certificateType: + type: string + certificateTypeVersion: + type: string + description: | + The version of the certificate type itself (e.g. `2015` for + ISO 9001:2015). Distinct from the certificate's `version`. + certifiedBpn: + type: string + description: The BPNL of the certified legal entity the certificate is issued on. + pattern: "^BPNL[a-zA-Z0-9]{12}$" + registrationNumber: + type: string + description: The certificate's registration number at the issuing body. + validFrom: + type: string + format: date + validUntil: + type: string + format: date + trustLevel: + type: string + description: The degree to which the certificate has been validated. + enum: [none, low, medium, high, trusted] + areaOfApplication: + type: string + description: | + Free-text detail of the areas or application types the certificate + is valid for. + enclosedSiteBpns: + type: array + items: + type: string + issuer: + $ref: '#/components/schemas/CertificateIssuer' + validator: + $ref: '#/components/schemas/CertificateValidator' + documents: + type: array + description: | + Documents associated with the returned certificate version, each + retrieved independently via `GET /documents/{id}`. + items: + $ref: '#/components/schemas/CertificateDocument' + + Error: + type: object + required: + - message + properties: + message: + type: string + description: Human-readable error description. + + responses: + BadRequest: + description: The request was malformed or failed validation. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + Unauthorized: + description: The request was not authenticated. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + Forbidden: + description: The caller is not authorized to perform this operation. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + NotFound: + description: The referenced certificate does not exist or is not visible to the caller. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + InternalServerError: + description: An unexpected error occurred on the server. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + description: | + Bearer token as defined by CX-0000 Section 4 (HTTPS Binding). + +security: + - bearerAuth: [] diff --git a/docs/standards/CX-0135-CompanyCertificateManagement/certificate-provider-api-jim.yaml b/docs/standards/CX-0135-CompanyCertificateManagement/certificate-provider-api-jim.yaml new file mode 100644 index 0000000000..ab3913e15e --- /dev/null +++ b/docs/standards/CX-0135-CompanyCertificateManagement/certificate-provider-api-jim.yaml @@ -0,0 +1,825 @@ +openapi: 3.0.3 +info: + title: Certificate Provider API + description: | + HTTP API exposed by a Certificate Provider to accept certificate requests, + report request fulfillment status, serve certificate data, accept + acceptance status updates, and answer certificate queries, as defined in + CX-0135 Section 4.4. + + A certificate is identified by the (`certificateId`, `version`) pair. A + consumer-initiated request opens a Certificate Exchange; the Certificate + Provider assigns `certificateId` and `version` when it accepts the request. + + All requests MUST use HTTPS (TLS 1.2 or higher). Authorization MUST adhere + to CX-0000 (CloudEventsFoundation) Section 4. + version: 1.0.0 + +servers: + - url: https://{base} + description: Certificate Provider base URL + variables: + base: + default: api.example.com + description: Host serving the Certificate Provider API + +paths: + /certificate-requests: + post: + summary: Request a certificate + description: | + Invoked by a Certificate Consumer to request a certificate. A request + opens a consumer-initiated Certificate Exchange. On acceptance the + Certificate Provider assigns and returns a `certificateId` and + `version`, enabling the consumer to poll fulfillment status and later + retrieve the certificate. + operationId: createCertificateRequest + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CertificateRequest' + examples: + byType: + summary: Request a certificate by type and location + value: + certificateType: ISO9001 + locationBpns: + - BPNS00000003AYRE + responses: + '202': + description: | + The request was accepted. The body carries the assigned + `certificateId` and `version` and the current fulfillment status. + content: + application/json: + schema: + $ref: '#/components/schemas/CertificateRequestResponse' + examples: + acknowledged: + summary: Accepted for asynchronous fulfillment + value: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + version: 1 + status: ACKNOWLEDGED + fulfilled: + summary: Certificate already available + value: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + version: 1 + status: FULFILLED + declined: + summary: Request refused + value: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + version: 1 + status: DECLINED + errors: + - message: Certificate type not offered for the requested location + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '500': + $ref: '#/components/responses/InternalServerError' + + /certificate-requests/{id}: + get: + summary: Poll the fulfillment status of a request + description: | + Invoked by a Certificate Consumer to poll the fulfillment status of a + previously submitted certificate request. This endpoint reports only the + Fulfillment phase; the consumer's acceptance outcome is reported + separately via `POST /certificate-acceptance-notifications`. + + Polling is optional: if the Certificate Consumer exposes the Certificate + Consumer Notification API, the Certificate Provider MAY instead push + these fulfillment status updates to it as a + `org.catena-x.ccm.CertificateFulfillmentStatus.v1` event (CX-0135 + Section 4.3.2). The push and the poll are equivalent and carry the same + `exchangeId` and `status`. + operationId: getCertificateRequestStatus + parameters: + - name: id + in: path + required: true + description: | + The `exchangeId` returned in the response to the certificate + request. + schema: + type: string + responses: + '200': + description: Current fulfillment status of the request. + content: + application/json: + schema: + $ref: '#/components/schemas/CertificateRequestStatus' + examples: + acknowledged: + summary: Status ACKNOWLEDGED + value: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + version: 1 + status: ACKNOWLEDGED + fulfilled: + summary: Status FULFILLED + value: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + version: 1 + status: FULFILLED + declined: + summary: Status DECLINED + value: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + version: 1 + status: DECLINED + errors: + - message: Certificate type not offered for the requested location + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '500': + $ref: '#/components/responses/InternalServerError' + + /certificates/{id}: + get: + summary: Retrieve certificate metadata + description: | + Retrieves the metadata of a certificate as `application/json`. The + certificate is metadata only; binary documents are not included. Each + associated document is listed by reference in the `documents` array and + retrieved independently via `GET /documents/{id}`. + Returns the latest version by default; a specific version may be + requested with the `version` query parameter (e.g. to retrieve the exact + version a Certificate Exchange concerns). The provider MUST be able to + return any version still referenced by a non-terminal Certificate + Exchange. + operationId: getCertificate + parameters: + - name: id + in: path + required: true + description: The unique identifier of the certificate. + schema: + type: string + - name: version + in: query + required: false + description: | + The specific version to retrieve. If omitted, the latest version is + returned. + schema: + type: integer + responses: + '200': + description: | + Certificate found. The response is the JSON metadata described by + `CertificateMetadata`, including the `documents` array referencing + the documents associated with the returned version. + content: + application/json: + schema: + $ref: '#/components/schemas/CertificateMetadata' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '500': + $ref: '#/components/responses/InternalServerError' + + /documents/{id}: + get: + summary: Retrieve a certificate document + description: | + Retrieves a certificate document by its identifier. The response body is + the document binary, served directly with the `Content-Type` set to the + document's `mediaType` (e.g. `application/pdf`) as advertised in the + certificate metadata. + A `documentId` is opaque and independent of any certificate version; the + same document may be referenced by multiple certificate versions, so no + `version` parameter is required. + operationId: getDocument + parameters: + - name: id + in: path + required: true + description: The opaque, version-independent identifier of the document. + schema: + type: string + responses: + '200': + description: | + Document found. The response body is the document binary, served with + the `Content-Type` set to the document's `mediaType`. + content: + application/pdf: + schema: + type: string + format: binary + image/png: + schema: + type: string + format: binary + application/octet-stream: + schema: + type: string + format: binary + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '500': + $ref: '#/components/responses/InternalServerError' + + /certificate-acceptance-notifications: + post: + summary: Submit a certificate acceptance status event + description: | + Invoked by a Certificate Consumer to report the acceptance status of a + certificate. The status is carried in `data.status` and the Certificate + Provider dispatches on it. The event is correlated to its Certificate + Exchange by the `exchangeId` in `data`. Acceptance MUST reference an + existing exchange (opened by a consumer request or a provider + notification); merely retrieving a certificate does not establish an + exchange. An unknown `exchangeId` is rejected with 404. The body is a + single CloudEvent or a batch (JSON array). + operationId: postCertificateAcceptanceNotification + requestBody: + required: true + content: + application/cloudevents+json: + schema: + oneOf: + - $ref: '#/components/schemas/CertificateAcceptanceStatusEvent' + - type: array + items: + $ref: '#/components/schemas/CertificateAcceptanceStatusEvent' + examples: + retrieved: + summary: Status RETRIEVED + value: + specversion: "1.0" + type: org.catena-x.ccm.CertificateAcceptanceStatus.v1 + source: urn:bpn:BPNL0000000002CD + subject: BPNL0000000001AB + id: e9f8d7c6-b5a4-9382-7160-5a4b3c2d1e0f + time: "2025-05-04T08:00:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-acceptance-status.json + data: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + status: RETRIEVED + accepted: + summary: Status ACCEPTED + value: + specversion: "1.0" + type: org.catena-x.ccm.CertificateAcceptanceStatus.v1 + source: urn:bpn:BPNL0000000002CD + subject: BPNL0000000001AB + id: a7b8c9d0-e1f2-3a4b-5c6d-7e8f9a0b1c2d + time: "2025-05-04T09:00:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-acceptance-status.json + data: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + status: ACCEPTED + rejected: + summary: Status REJECTED + value: + specversion: "1.0" + type: org.catena-x.ccm.CertificateAcceptanceStatus.v1 + source: urn:bpn:BPNL0000000002CD + subject: BPNL0000000001AB + id: f1a2b3c4-d5e6-7f8a-9b0c-1d2e3f4a5b6c + time: "2025-05-04T09:00:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-acceptance-status.json + data: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + status: REJECTED + errors: + - message: Certificate has expired + - message: Unexpected data format + - specifier: BPNS000000000002 + message: Site BPNS000000000002 has been Rejected + errored: + summary: Status ERRORED + value: + specversion: "1.0" + type: org.catena-x.ccm.CertificateAcceptanceStatus.v1 + source: urn:bpn:BPNL0000000002CD + subject: BPNL0000000001AB + id: c8d7e6f5-a4b3-2109-8765-432109fedcba + time: "2025-05-04T08:30:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-acceptance-status.json + data: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + status: ERRORED + errors: + - message: Certificate document is malformed and cannot be validated + responses: + '204': + description: Acceptance status accepted; response body is empty. + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '500': + $ref: '#/components/responses/InternalServerError' + + /certificates/search: + post: + summary: Search certificates + description: | + Returns certificates matching the supplied criteria. Results MAY be + paginated; when paginated, navigation URLs are provided in the `Link` + response header in accordance with RFC 8288 (Web Linking). Only the + `next` and `prev` link relations are guaranteed; `first` and `last` + MAY also be returned. To retrieve a subsequent page, the client + re-issues a `POST` with the same request body against the URL + provided in the corresponding `Link` value. + operationId: queryCertificates + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CertificateQuery' + examples: + byType: + summary: Query by certificate type and validity window + value: + certificateType: ISO9001 + from: "2023-01-25" + to: "2026-01-24" + limit: 50 + responses: + '200': + description: | + Matching certificates. When the result set is paginated, the + response includes a `Link` header per RFC 8288. + headers: + Link: + description: | + Web Linking pagination header (RFC 8288). Contains URLs for + the `next` and/or `prev` pages. Omitted when the response is + not paginated. + schema: + type: string + example: >- + ; rel="next", + ; rel="prev" + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/CertificateQueryResponse' + examples: + page: + summary: A single page of results + value: + - certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + version: 1 + datasetId: dataset-ccm-cert-abc123 + certificateType: ISO9001 + validFrom: "2023-01-25" + validUntil: "2026-01-24" + locationBpns: + - BPNS00000003AYRE + - BPNA000000000001 + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '500': + $ref: '#/components/responses/InternalServerError' + +components: + schemas: + CertificateRequest: + type: object + description: Request body for `POST /certificate-requests`. + required: + - certificateType + properties: + certificateType: + type: string + description: | + Opaque string identifying the certificate type to be requested + (for example, `ISO9001`). + locationBpns: + type: array + description: | + BPNs of the sites or addresses the request applies to. If omitted, + the request applies to the legal entity. + items: + type: string + + CertificateRequestResponse: + type: object + description: | + Response to an accepted certificate request. Carries the assigned + exchange and certificate identity and the initial fulfillment status. + required: + - exchangeId + - certificateId + - version + - status + properties: + exchangeId: + type: string + description: | + Identifier assigned to the opened Certificate Exchange. Used to poll + fulfillment status via `GET /certificate-requests/{id}`. + certificateId: + type: string + description: | + Identifier assigned to the requested certificate. Used to retrieve + the certificate via `GET /certificates/{id}`. + version: + type: integer + description: Version assigned to the requested certificate. + status: + type: string + enum: [ACKNOWLEDGED, CERTIFICATION_REQUESTED, FULFILLED, DECLINED] + description: The fulfillment status of the request. + errors: + type: array + description: | + Error details. MUST be present and non-empty when `status` is + `DECLINED`. + items: + $ref: '#/components/schemas/StatusError' + + CertificateRequestStatus: + type: object + description: | + Current fulfillment status of a certificate request. + required: + - exchangeId + - certificateId + - version + - status + properties: + exchangeId: + type: string + description: | + Identifier of the Certificate Exchange. Matches the `{id}` path + parameter of the request. + certificateId: + type: string + description: Unique identifier of the requested certificate. + version: + type: integer + description: The version of the requested certificate. + status: + type: string + enum: [ACKNOWLEDGED, CERTIFICATION_REQUESTED, FULFILLED, DECLINED, FAILED] + description: The current fulfillment status. + errors: + type: array + description: | + Error details. MUST be present and non-empty when `status` is + `DECLINED` or `FAILED`. + items: + $ref: '#/components/schemas/StatusError' + + CertificateDocument: + type: object + description: | + A reference to a document associated with a certificate version. The + binary is retrieved separately via `GET /documents/{id}`. + required: + - documentId + - mediaType + properties: + documentId: + type: string + description: | + Opaque, version-independent identifier of the document, used to + retrieve it via `GET /documents/{id}`. + examples: + - "doc-3fa85f64-5717-4562-b3fc-2c963f66afa6" + mediaType: + type: string + description: IANA media type of the document binary. + examples: + - "application/pdf" + - "image/png" + + CertificateMetadata: + type: object + description: JSON metadata accompanying a retrieved certificate. + required: + - certificateId + - version + - certificateType + - validFrom + - validUntil + properties: + certificateId: + type: string + description: | + Unique identifier of the certificate. Matches the `{id}` path + parameter of the request. + version: + type: integer + description: | + The version returned — the version requested via the `version` query + parameter, or the latest version if none was specified. + certificateType: + type: string + description: Opaque string identifying the certificate type (e.g. `ISO9001`). + validFrom: + type: string + format: date + description: | + Inclusive start date of the certificate validity period + (ISO 8601 full-date, `YYYY-MM-DD`). + validUntil: + type: string + format: date + description: | + Inclusive end date of the certificate validity period + (ISO 8601 full-date, `YYYY-MM-DD`). + locationBpns: + type: array + description: | + BPNs of the sites or addresses the certificate applies to. If + omitted, the certificate applies to the issuing legal entity as a + whole. + items: + type: string + documents: + type: array + description: | + Documents associated with this certificate version. Each document is + retrieved independently via `GET /documents/{id}`. If omitted or + empty, the version has no associated documents. + items: + $ref: '#/components/schemas/CertificateDocument' + + CertificateAcceptanceStatusEvent: + type: object + description: | + CloudEvents 1.0 envelope carrying a certificate acceptance status + payload. The Acceptance-phase outcome is carried in `data.status`. + required: + - specversion + - type + - source + - id + - time + - data + properties: + specversion: + type: string + enum: ["1.0"] + type: + type: string + enum: [org.catena-x.ccm.CertificateAcceptanceStatus.v1] + source: + type: string + format: uri + description: | + URI identifying the producing Certificate Consumer. Typically a + BPN-scoped URN. + subject: + type: string + description: The BPN of the addressed Certificate Provider. + id: + type: string + description: Unique identifier of this event instance. + time: + type: string + format: date-time + datacontenttype: + type: string + enum: [application/json] + dataschema: + type: string + format: uri + data: + $ref: '#/components/schemas/CertificateAcceptanceStatus' + + CertificateStatusBase: + type: object + description: | + Common certificate-status base payload. Carries the `exchangeId` (when + the event belongs to a Certificate Exchange), the subject `certificateId`, + and a `status`. Concrete event payloads derive from this schema and + constrain `status` to the values defined for their endpoint. + required: + - certificateId + - status + properties: + exchangeId: + type: string + description: | + Identifier of the Certificate Exchange the event belongs to. Distinct + from the CloudEvent `id`. Present when the event pertains to an + exchange; subtypes may require it. + certificateId: + type: string + description: | + Unique identifier of the certificate the status applies to. + status: + type: string + description: | + The status value. Subtypes constrain the permitted set of values. + + CertificateAcceptanceStatus: + description: | + Certificate acceptance status payload, derived from + `CertificateStatusBase`. Reports on a Certificate Exchange, so + `exchangeId` is required. + allOf: + - $ref: '#/components/schemas/CertificateStatusBase' + - type: object + required: + - exchangeId + properties: + status: + type: string + enum: [RETRIEVED, ACCEPTED, REJECTED, ERRORED] + description: The acceptance status of the certificate. + errors: + type: array + description: | + Error details. MUST NOT be present when `status` is `RETRIEVED` + or `ACCEPTED`. MUST be present and non-empty when `status` is + `REJECTED` or `ERRORED`. + items: + $ref: '#/components/schemas/StatusError' + + StatusError: + type: object + required: + - message + properties: + message: + type: string + description: Human-readable description of the error. + specifier: + type: string + description: | + Optional identifier scoping the error to a particular element of + the certificate, such as a site BPN. If omitted, the error + applies to the certificate as a whole. + + CertificateQuery: + type: object + description: Request body for `POST /certificates/search`. + required: + - certificateType + properties: + certificateType: + type: string + description: | + Opaque string identifying the certificate type to be matched + (for example, `ISO9001`). + from: + type: string + format: date + description: | + Inclusive lower bound of the certificate validity range + (ISO 8601 full-date). Only certificates whose `validFrom` is on + or after this date are returned. + to: + type: string + format: date + description: | + Inclusive upper bound of the certificate validity range + (ISO 8601 full-date). Only certificates whose `validUntil` is on + or before this date are returned. + limit: + type: integer + minimum: 1 + description: | + Maximum number of results to return in a single page. If + omitted, the Certificate Provider MAY apply a default limit. + + CertificateQueryResponse: + type: object + description: A single result entry in a certificate query response. + required: + - certificateId + - version + - datasetId + - certificateType + - validFrom + - validUntil + properties: + certificateId: + type: string + description: | + Unique identifier of the certificate, usable in + `GET /certificates/{id}`. + version: + type: integer + description: The latest version of the certificate. + datasetId: + type: string + description: | + Identifier of the DSP dataset under which the certificate is + exposed in the Certificate Provider's catalog. + certificateType: + type: string + validFrom: + type: string + format: date + validUntil: + type: string + format: date + locationBpns: + type: array + items: + type: string + documents: + type: array + description: | + Documents associated with the returned certificate version, each + retrieved independently via `GET /documents/{id}`. + items: + $ref: '#/components/schemas/CertificateDocument' + + Error: + type: object + required: + - message + properties: + message: + type: string + description: Human-readable error description. + + responses: + BadRequest: + description: The request was malformed or failed validation. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + Unauthorized: + description: The request was not authenticated. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + Forbidden: + description: The caller is not authorized to perform this operation. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + NotFound: + description: The referenced certificate does not exist or is not visible to the caller. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + InternalServerError: + description: An unexpected error occurred on the server. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + description: | + Bearer token as defined by CX-0000 Section 4 (HTTPS Binding). + +security: + - bearerAuth: [] From 902dac2f6995a36f9e98321ddfefaa1c2e0b0c4e Mon Sep 17 00:00:00 2001 From: Nico Koprowski Date: Tue, 16 Jun 2026 11:57:03 +0800 Subject: [PATCH 14/14] docs(0135): merge OpenAPI spec proposal from Jim --- .../assets/openapi-spec.yaml | 1274 +++++++++++++---- 1 file changed, 965 insertions(+), 309 deletions(-) diff --git a/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml b/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml index 0351dd3e8b..a5ef636e70 100644 --- a/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml +++ b/docs/standards/CX-0135-CompanyCertificateManagement/assets/openapi-spec.yaml @@ -1,7 +1,14 @@ openapi: 3.1.0 info: title: Company Certificate Notification API - description: APIs for requesting and accepting company certificates + description: | + APIs for requesting and accepting company certificates. + + Certificate notifications are delivered to the consumer as CloudEvents + (CX-0000) on a single endpoint and distinguished by the CloudEvents `type` + attribute: certificate lifecycle status events and certificate fulfillment + status notifications. All requests MUST use HTTPS (TLS 1.2 or higher) and + authorization MUST adhere to CX-0000 (CloudEventsFoundation) Section 4. version: 1.0.0 tags: - name: Certificate Provider @@ -11,83 +18,14 @@ tags: components: schemas: - Header: - description: Characteristic describing the common shared aspect Message Header - required: - - context - - messageId - - receiverBpn - - senderBpn - - sentDateTime - - version - type: object - properties: - messageId: - pattern: "^(?:urn:uuid:)?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" - type: string - description: A UUIDv4 to uniquely identify a message - examples: - - urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72 - - e4da568b-8cf1-4f5f-a96a-cf26265b2c72 - context: - type: string - description: The context defines the type of company certificate message - examples: - - CompanyCertificateManagement-CCMAPI-Request:1.0.0 - enum: - - CompanyCertificateManagement-CCMAPI-Request:1.0.0 - - CompanyCertificateManagement-CCMAPI-Push:1.0.0 - - CompanyCertificateManagement-CCMAPI-Status:1.0.0 - - CompanyCertificateManagement-CCMAPI-Available:1.0.0 - sentDateTime: - type: string - description: Time zone aware timestamp holding the date and the time the - message was sent by the sending party. The value MUST be formatted according - to the ISO 8601 standard - format: date-time - examples: - - 2024-10-07T10:15:00Z - senderBpn: - pattern: "^BPNL[a-zA-Z0-9]{12}$" - type: string - description: The Business Partner Number of the sending party - examples: - - BPNL0000000001AB - receiverBpn: - pattern: "^BPNL[a-zA-Z0-9]{12}$" - type: string - description: The Business Partner Number of the receiving party - examples: - - BPNL0000000002CD - relatedMessageId: - pattern: "^(?:urn:uuid:)?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" - type: string - description: Unique ID identifying a message somehow related to the current - one - examples: - - urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72 - - e4da568b-8cf1-4f5f-a96a-cf26265b2c72 - version: - pattern: "^(0|[1-9][0-9]*).(0|[1-9][0-9]*).(0|[1-9][0-9]*)(-(0|[1-9A-Za-z-][0-9A-Za-z-]*)(.[0-9A-Za-z-]+)*)?([0-9A-Za-z-]+(.[0-9A-Za-z-]+)*)?$" - type: string - description: The unique identifier of the aspect model defining the structure - and the semantics of the message's header - examples: - - 3.1.0 - - FeedbackUrlHeader: - allOf: - - $ref: '#/components/schemas/Header' - - type: object - properties: - senderFeedbackUrl: - type: string - description: URL of the EDC DSP endpoint to send feedback to - examples: - - https://domain.tld/path/to/edc/api/v1/dsp - CertificateRequest: type: object + description: | + Request body for `POST /certificate-requests`. A request opens a + consumer-initiated Certificate Exchange. On acceptance the Certificate + Provider assigns and returns an `exchangeId`, `certificateId` and + `version`, enabling the consumer to poll fulfillment status and later + retrieve the certificate. required: - certifiedBpn - certificateType @@ -100,57 +38,89 @@ components: - "BPNL0000000002CD" certificateType: type: string - description: The type of certificate the certificate consumer requests + description: | + Opaque string identifying the certificate type the consumer requests + (for example, `ISO9001`). examples: - "iso9001" - locationBpns: + certifiedLocations: type: array - description: The BPNs of the site or address locations for which the certificate consumer requests the certificate + description: | + The BPNs of the site or address locations for which the certificate + consumer requests the certificate. If omitted, the request applies to + the certified legal entity as a whole. items: $ref: '#/components/schemas/BpnLocation' - CertificateRequestFinishedResponse: + CertificateRequestResponse: + description: | + Outcome of a certificate request, reported on `POST /certificate-requests` + and when polling `GET /certificate-requests/{id}`. Dispatched on `status`: + a progress/fulfilled outcome, or a rejected outcome carrying errors. oneOf: - - $ref: '#/components/schemas/CertificateRequestCompletedResponse' + - $ref: '#/components/schemas/CertificateRequestProgressResponse' - $ref: '#/components/schemas/CertificateRequestRejectedResponse' discriminator: - propertyName: requestStatus - - CertificateRequestInProgressResponse: - type: object - required: - - requestStatus - properties: - requestStatus: - type: string - description: The processing status in which the certificate request process is currently in - enum: [IN_PROGRESS, UNDER_CERTIFICATION] + propertyName: status + mapping: + ACKNOWLEDGED: '#/components/schemas/CertificateRequestProgressResponse' + CERTIFICATION_REQUESTED: '#/components/schemas/CertificateRequestProgressResponse' + FULFILLED: '#/components/schemas/CertificateRequestProgressResponse' + DECLINED: '#/components/schemas/CertificateRequestRejectedResponse' + FAILED: '#/components/schemas/CertificateRequestRejectedResponse' - CertificateRequestCompletedResponse: + CertificateRequestProgressResponse: type: object + description: | + A pending or successfully fulfilled certificate request. Carries the + assigned exchange and certificate identity. required: - - requestStatus + - exchangeId - certificateId + - version + - status properties: - requestStatus: + exchangeId: type: string - description: The processing status in which the certificate request process is currently in - const: "COMPLETED" + description: | + Identifier assigned to the opened Certificate Exchange. Used to poll + fulfillment status via `GET /certificate-requests/{id}`. certificateId: type: string - pattern: uuid - description: The UUID of the asset under which the certificate is available + description: | + Identifier assigned to the requested certificate. Used to retrieve the + certificate via `GET /certificates/{certificateId}`. + version: + type: integer + description: Version assigned to the requested certificate. + status: + type: string + description: The fulfillment status of the request. + enum: [ACKNOWLEDGED, CERTIFICATION_REQUESTED, FULFILLED] CertificateRequestRejectedResponse: type: object + description: | + A certificate request that was declined by the provider or failed during + fulfillment. Carries the reasons for the rejection. required: - - requestStatus + - exchangeId + - status - requestErrors properties: - requestStatus: + exchangeId: type: string - description: The processing status in which the certificate request process is currently in - const: "REJECTED" + description: Identifier of the Certificate Exchange the request opened. + certificateId: + type: string + description: Identifier of the requested certificate, if one was assigned. + version: + type: integer + description: Version of the requested certificate, if one was assigned. + status: + type: string + description: The terminal fulfillment status of the request. + enum: [DECLINED, FAILED] requestErrors: type: array description: Listing all reasons why the requested certificate can not be provided @@ -170,46 +140,6 @@ components: - "BPNA000000000001" - "BPNS000000000002" - CertificatePush: - type: object - required: - - header - - content - properties: - header: - $ref: '#/components/schemas/FeedbackUrlHeader' - content: - $ref: '#/components/schemas/CertificatePushContent' - - CertificateFeedback: - type: object - required: - - header - - content - properties: - header: - $ref: '#/components/schemas/FeedbackUrlHeader' - content: - $ref: '#/components/schemas/CertificateStatus' - - CertificatePushContent: - type: object - required: - - certificateId - - status - properties: - certificateId: - type: string - format: uuid - status: - type: string - description: | - The type of lifecycle change for the certificate: - - `CREATED`: A new certificate has been created and is available for retrieval. - - `MODIFIED`: An existing certificate has been updated. - - `DELETED`: A certificate has been removed and is no longer available. - enum: [CREATED, MODIFIED, DELETED] - Error: type: object required: @@ -227,49 +157,12 @@ components: properties: bpn: $ref: '#/components/schemas/BpnLocation' - locationErrors: + messages: type: array items: - $ref: '#/components/schemas/LocationError' - - LocationError: - type: object - properties: - message: - type: string - examples: - - Site BPNS000000000003 is unknown - - CertificateStatus: - type: object - required: - - certificateId - - certificateStatus - - locationBpns - properties: - certificateId: - type: string - format: uuid - certificateStatus: - type: string - description: | - The validation status reported by the Certificate Consumer: - - `RECEIVED`: Certificate has been received and validation is in progress. - - `ACCEPTED`: Certificate has been accepted by the Certificate Consumer. - - `REJECTED`: Certificate has been rejected by the Certificate Consumer. - enum: [ACCEPTED, REJECTED, RECEIVED] - certificateErrors: - type: array - items: - $ref: '#/components/schemas/Error' - locationBpns: - type: array - items: - $ref: '#/components/schemas/BpnLocation' - locationErrors: - type: array - items: - $ref: '#/components/schemas/LocationErrorCollection' + type: string + examples: + - Site BPNS000000000003 is unknown CertificateType: type: object @@ -295,10 +188,11 @@ components: properties: locationBpn: type: string - description: The Business Partner Number (BPNS or BPNA) of a location - pattern: "^(?:BPNS|BPNA)[a-zA-Z0-9]{12}$" + description: The Business Partner Number (BPNL, BPNS or BPNA) of a location + pattern: "^(?:BPNL|BPNS|BPNA)[a-zA-Z0-9]{12}$" examples: - - "BPNS000000000002" + - "BPNL000000000001" + - "BPNS000000000001" - "BPNA000000000001" areaOfApplication: type: string @@ -336,8 +230,13 @@ components: CertificateDocument: type: object + description: | + A reference to a document associated with a certificate. The binary is + retrieved separately via GET /certificates/{certificateId}/documents/{documentId}, + served with the `Content-Type` set to the document's `mediaType`. required: - documentId + - mediaType properties: documentId: type: string @@ -346,13 +245,16 @@ components: - "doc-3fa85f64-5717-4562-b3fc-2c963f66afa6" language: type: string - description: The language of the document as an ISO 639-1 two-letter language code (e.g. "en" for English, "de" for German) + description: | + The language of the document as an ISO 639-1 two-letter language code + (e.g. "en" for English, "de" for German). Distinguishes documents that + differ only by language. pattern: "^[a-z]{2}$" examples: - "en" mediaType: type: string - description: The IANA media type of the document + description: The IANA media type of the document binary examples: - "application/pdf" - "image/png" @@ -361,6 +263,7 @@ components: type: object required: - certificateId + - version - certifiedBpn - type - registrationNumber @@ -373,6 +276,13 @@ components: description: The id of the certificate document as stored by the data service provider examples: - "cert-550e8400-e29b-41d4-a716-446655440000" + version: + type: integer + description: | + The version returned — the version requested via the `version` query + parameter, or the latest version if none was specified. + examples: + - 1 certifiedBpn: type: string description: The Business Partner Number (BPN) of the certified legal entity on which the certificate is issued @@ -387,9 +297,9 @@ components: areaOfApplication: type: string description: Details on which areas / application types a certificate is valid for a company and/or site - locations: + certifiedLocations: type: array - description: Locations covered by the certificate (BPNS or BPNA) + description: Locations covered by the certificate (BPNL, BPNS or BPNA) items: $ref: '#/components/schemas/LocationBpn' validFrom: @@ -408,16 +318,10 @@ components: $ref: '#/components/schemas/CertificateIssuer' trustLevel: type: string - description: The trust level of the given certificate - enum: [none, low, high, trusted] + description: The degree to which the certificate has been validated + enum: [none, low, medium, high, trusted] validator: $ref: '#/components/schemas/CertificateValidator' - uploader: - type: string - description: The Business Partner Number (BPN) of the business partner who originally provided the certificate data or document - pattern: "^BPNL[a-zA-Z0-9]{12}$" - examples: - - "BPNL0000000001AB" documents: type: array description: One or more documents associated with the certificate (e.g. main certificate and annexes). Use GET /certificates/{certificateId}/documents/{documentId} to retrieve each document. @@ -426,7 +330,18 @@ components: CertificateSearchRequest: type: object + description: | + Filters for `POST /certificate-search`. All criteria are optional; an + empty request returns all accessible certificates. Criteria of different + kinds are combined with AND; within a single BPN list the match is any-of. properties: + certificateType: + type: string + description: | + Opaque string identifying the certificate type to be matched + (for example, `ISO9001`). + examples: + - "ISO9001" legalEntityBpns: type: array description: Filter results to certificates issued for these legal entity BPNLs @@ -451,6 +366,24 @@ components: pattern: "^BPNA[a-zA-Z0-9]{12}$" examples: - "BPNA000000000001" + from: + type: string + format: date + description: | + Inclusive lower bound of the certificate validity range (ISO 8601 + full-date). Only certificates whose `validFrom` is on or after this + date are returned. + examples: + - "2023-01-25" + to: + type: string + format: date + description: | + Inclusive upper bound of the certificate validity range (ISO 8601 + full-date). Only certificates whose `validUntil` is on or before this + date are returned. + examples: + - "2026-01-24" PaginatedCertificateList: type: object @@ -479,82 +412,410 @@ components: items: $ref: '#/components/schemas/CertificateRetrievalMetadata' + CertificateLifecycleStatusEvent: + type: object + description: | + CloudEvents 1.0 envelope carrying a certificate lifecycle status + payload. The lifecycle transition is carried in `data.status`. + required: + - specversion + - type + - source + - id + - time + - data + properties: + specversion: + type: string + enum: ["1.0"] + description: The CloudEvents specification version. + type: + type: string + enum: [org.catena-x.ccm.CertificateLifecycleStatus.v1] + description: The event type identifier. + source: + type: string + format: uri + description: | + URI identifying the producing Certificate Provider. Typically a + BPN-scoped URN (for example, `urn:bpn:BPNL0000000001AB`). + subject: + type: string + description: | + The BPN of the recipient (Certificate Consumer) the event is + addressed to. + id: + type: string + description: Unique identifier of this event instance. + time: + type: string + format: date-time + description: RFC 3339 timestamp at which the event was produced. + datacontenttype: + type: string + enum: [application/json] + description: Content type of the `data` member. + dataschema: + type: string + format: uri + description: URI of the JSON Schema describing the `data` payload. + data: + $ref: '#/components/schemas/CertificateLifecycleStatus' + + CertificateFulfillmentStatusEvent: + type: object + description: | + CloudEvents 1.0 envelope carrying a certificate fulfillment status + notification. The Fulfillment-phase status is carried in `data.status`. + required: + - specversion + - type + - source + - id + - time + - data + properties: + specversion: + type: string + enum: ["1.0"] + description: The CloudEvents specification version. + type: + type: string + enum: [org.catena-x.ccm.CertificateFulfillmentStatus.v1] + description: The event type identifier. + source: + type: string + format: uri + description: | + URI identifying the producing Certificate Provider. Typically a + BPN-scoped URN (for example, `urn:bpn:BPNL0000000001AB`). + subject: + type: string + description: | + The BPN of the recipient (Certificate Consumer) the event is + addressed to. + id: + type: string + description: Unique identifier of this event instance. + time: + type: string + format: date-time + description: RFC 3339 timestamp at which the event was produced. + datacontenttype: + type: string + enum: [application/json] + description: Content type of the `data` member. + dataschema: + type: string + format: uri + description: URI of the JSON Schema describing the `data` payload. + data: + $ref: '#/components/schemas/CertificateFulfillmentStatus' + + CertificateStatusBase: + type: object + description: | + Common certificate-status base payload. Carries the `exchangeId` (when + the event belongs to a Certificate Exchange), the subject `certificateId`, + and a `status`. Concrete event payloads derive from this schema and + constrain `status` to the values defined for their endpoint. + required: + - certificateId + - status + properties: + exchangeId: + type: string + description: | + Identifier of the Certificate Exchange the event belongs to. Distinct + from the CloudEvent `id`. Present when the event pertains to an + exchange; subtypes may require it. + certificateId: + type: string + description: | + Unique identifier of the certificate the status applies to, usable + in `GET /certificates/{id}` on the Certificate Provider API. + status: + type: string + description: | + The status value. Subtypes constrain the permitted set of values. + + CertificateLifecycleStatus: + description: | + Certificate lifecycle status payload, derived from `CertificateStatusBase`. + `exchangeId` is present when `status` is `CREATED` (the event opens the + exchange) and absent for `MODIFIED`/`WITHDRAWN`. `validFrom` and + `validUntil` are REQUIRED when `status` is `CREATED` or `MODIFIED` and MAY + be omitted when `status` is `WITHDRAWN`. + allOf: + - $ref: '#/components/schemas/CertificateStatusBase' + - type: object + required: + - version + - certificateType + properties: + version: + type: integer + description: | + Version of the certificate. Together with `certificateId` it + identifies the specific certificate. + status: + type: string + enum: [CREATED, MODIFIED, WITHDRAWN] + description: | + The lifecycle status. `CREATED` opens a Certificate Exchange; + `MODIFIED` and `WITHDRAWN` do not. + certificateType: + type: string + description: Opaque string identifying the certificate type (e.g. `ISO9001`). + validFrom: + type: string + format: date + description: | + Inclusive start date of the validity period (ISO 8601 full-date, + `YYYY-MM-DD`). Required when `status` is `CREATED` or `MODIFIED`. + validUntil: + type: string + format: date + description: | + Inclusive end date of the validity period (ISO 8601 full-date, + `YYYY-MM-DD`). Same presence rules as `validFrom`. + certifiedLocations: + type: array + description: | + BPNs of the sites or addresses the certificate applies to. If + omitted, the certificate applies to the issuing legal entity as a + whole. + items: + type: string + + CertificateFulfillmentStatus: + description: | + Certificate fulfillment status payload, derived from + `CertificateStatusBase`. Reports the Fulfillment-phase status of a + consumer-initiated Certificate Exchange and is correlated by the + required `exchangeId`. It is the push counterpart of the + `GET /certificate-request` poll on the Certificate Provider API. + allOf: + - $ref: '#/components/schemas/CertificateStatusBase' + - type: object + required: + - exchangeId + properties: + status: + type: string + enum: [ACKNOWLEDGED, CERTIFICATION_REQUESTED, FULFILLED, DECLINED, FAILED] + description: | + The Fulfillment status. Providers SHOULD push at least the + terminal outcomes (`FULFILLED`, `DECLINED`, `FAILED`); + intermediate states MAY also be pushed. + errors: + type: array + description: | + Error details. MUST be present and non-empty when `status` is + `DECLINED` or `FAILED`; otherwise absent. + items: + $ref: '#/components/schemas/StatusError' + + CertificateAcceptanceStatusResponse: + description: | + Current acceptance status of a Certificate Exchange on the Certificate + Consumer, derived from `CertificateStatusBase`. The `status` property + conveys the Acceptance-phase state, including the non-terminal + `RETRIEVED` value. + allOf: + - $ref: '#/components/schemas/CertificateStatusBase' + - type: object + required: + - exchangeId + properties: + status: + type: string + enum: [RETRIEVED, ACCEPTED, REJECTED, ERRORED] + description: The current acceptance status. + certificateErrors: + type: array + description: | + Errors that apply to the certificate as a whole. MUST NOT be + present when `status` is `RETRIEVED` or `ACCEPTED`. MUST be + present and non-empty (here and/or in `locationErrors`) when + `status` is `REJECTED` or `ERRORED`. + items: + $ref: '#/components/schemas/Error' + locationErrors: + type: array + description: | + Detailed reasons for rejection scoped per location. + items: + $ref: '#/components/schemas/LocationErrorCollection' + + StatusError: + type: object + required: + - message + properties: + message: + type: string + description: Human-readable description of the error. + specifier: + type: string + description: | + Optional identifier scoping the error to a particular element of + the certificate, such as a site BPN. If omitted, the error + applies to the certificate as a whole. + + CertificateAcceptanceStatusEvent: + type: object + description: | + CloudEvents 1.0 envelope carrying a certificate acceptance status + payload. The Acceptance-phase outcome is carried in `data.status`. + required: + - specversion + - type + - source + - id + - time + - data + properties: + specversion: + type: string + enum: ["1.0"] + description: The CloudEvents specification version. + type: + type: string + enum: [org.catena-x.ccm.CertificateAcceptanceStatus.v1] + description: The event type identifier. + source: + type: string + format: uri + description: | + URI identifying the producing Certificate Consumer. Typically a + BPN-scoped URN (for example, `urn:bpn:BPNL0000000002CD`). + subject: + type: string + description: The BPN of the addressed Certificate Provider. + id: + type: string + description: Unique identifier of this event instance. + time: + type: string + format: date-time + description: RFC 3339 timestamp at which the event was produced. + datacontenttype: + type: string + enum: [application/json] + description: Content type of the `data` member. + dataschema: + type: string + format: uri + description: URI of the JSON Schema describing the `data` payload. + data: + $ref: '#/components/schemas/CertificateAcceptanceStatus' + + CertificateAcceptanceStatus: + description: | + Certificate acceptance status payload, derived from `CertificateStatusBase`. + Reports the Acceptance-phase outcome of a Certificate Exchange, so + `exchangeId` is required. This is the CloudEvents counterpart of the + `GET /certificate-acceptance-status/{id}` poll on the Certificate Consumer + API and reports errors the same way (`certificateErrors`/`locationErrors`). + allOf: + - $ref: '#/components/schemas/CertificateStatusBase' + - type: object + required: + - exchangeId + properties: + status: + type: string + enum: [RETRIEVED, ACCEPTED, REJECTED, ERRORED] + description: The acceptance status of the certificate. + certificateErrors: + type: array + description: | + Errors that apply to the certificate as a whole. MUST NOT be + present when `status` is `RETRIEVED` or `ACCEPTED`. MUST be + present and non-empty (here and/or in `locationErrors`) when + `status` is `REJECTED` or `ERRORED`. + items: + $ref: '#/components/schemas/Error' + locationErrors: + type: array + description: Detailed reasons for rejection scoped per location. + items: + $ref: '#/components/schemas/LocationErrorCollection' + examples: - CertificateRequestCompleted: - summary: A response for a successfully completed certificate request process + CertificateRequestAcknowledged: + summary: Request accepted for asynchronous fulfillment + value: + exchangeId: "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44" + certificateId: "cert-550e8400-e29b-41d4-a716-446655440000" + version: 1 + status: "ACKNOWLEDGED" + + CertificateRequestFulfilled: + summary: Certificate already available value: - requestStatus: "COMPLETED" - certificateId: "3b4edc05-e214-47a1-b0c2-1d831cdd9ba" + exchangeId: "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44" + certificateId: "cert-550e8400-e29b-41d4-a716-446655440000" + version: 1 + status: "FULFILLED" CertificateRequestRejected: - summary: A response for a rejected certificate request process + summary: A rejected certificate request value: - requestStatus: "REJECTED" + exchangeId: "exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44" + status: "DECLINED" requestErrors: - message: "We do not process certificates on Sunday" - message: "Can not provide certicate for requested locations" locationErrors: - bpn: "BPNS000000000003" - locationErrors: - - message: "Site BPNS000000000003 is unknown" + messages: + - "Site BPNS000000000003 is unknown" - CertificateReceivedStatus: - summary: Consumer received certificate - value: - header: - messageId: "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72" - context: "CompanyCertificateManagement-CCMAPI-Status:1.0.0" - sentDateTime: "2024-10-07T10:15:00Z" - senderBpn: "BPNL0000000001AB" - receiverBpn: "BPNL0000000002CD" - version: "3.1.0" - senderFeedbackUrl: "https://domain.tld/path/to/edc/api/v1/dsp" - content: - certificateId: "3fa85f64-5717-4562-b3fc-2c963f66afa6" - certificateStatus: "RECEIVED" - locationBpns: - - "BPNS000000000003" + responses: + BadRequest: + description: The request was malformed or failed validation. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + Unauthorized: + description: The request was not authenticated. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + Forbidden: + description: The caller is not authorized to perform this operation. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + NotFound: + description: | + The referenced certificate exchange is unknown to the Certificate Consumer. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + InternalServerError: + description: An unexpected error occurred on the server. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' - CertificateAcceptedStatus: - summary: Consumer accepts received certificate - value: - header: - messageId: "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72" - context: "CompanyCertificateManagement-CCMAPI-Status:1.0.0" - sentDateTime: "2024-10-07T10:15:00Z" - senderBpn: "BPNL0000000001AB" - receiverBpn: "BPNL0000000002CD" - version: "3.1.0" - senderFeedbackUrl: "https://domain.tld/path/to/edc/api/v1/dsp" - content: - certificateId: "3fa85f64-5717-4562-b3fc-2c963f66afa6" - certificateStatus: "ACCEPTED" - locationBpns: - - "BPNS000000000003" + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + description: | + Bearer token as defined by CX-0000 Section 4 (HTTPS Binding). - CertificateRejectedStatus: - summary: Consumer rejected received certificate - value: - header: - messageId: "urn:uuid:e4da568b-8cf1-4f5f-a96a-cf26265b2c72" - context: "CompanyCertificateManagement-CCMAPI-Status:1.0.0" - sentDateTime: "2024-10-07T10:15:00Z" - senderBpn: "BPNL0000000001AB" - receiverBpn: "BPNL0000000002CD" - version: "3.1.0" - senderFeedbackUrl: "https://domain.tld/path/to/edc/api/v1/dsp" - content: - certificateId: "3fa85f64-5717-4562-b3fc-2c963f66afa6" - certificateStatus: "REJECTED" - locationBpns: - - "BPNS000000000003" - certificateErrors: - - message: "We do not process certificates on Sunday" - - message: "Can not provide certificate for requested locations" - locationErrors: - - bpn: "BPNS000000000003" - locationErrors: - - message: "Site BPNS000000000003 is unknown" +security: + - bearerAuth: [] paths: /certificates/{certificateId}: @@ -563,7 +824,11 @@ paths: description: | Returns the certificate metadata. The `documents` array lists the IDs of associated documents (e.g. main certificate and annexes). Use GET /certificates/{certificateId}/documents/{documentId} - to retrieve each document as a binary PDF. + to retrieve each document as a binary. + + Returns the latest version by default; a specific version may be requested + with the `version` query parameter (e.g. to retrieve the exact version a + Certificate Exchange concerns). tags: - Certificate Provider parameters: @@ -573,6 +838,14 @@ paths: schema: type: string description: The certificate identifier + - name: version + in: query + required: false + description: | + The specific version to retrieve. If omitted, the latest version is + returned. + schema: + type: integer responses: '200': description: Certificate metadata returned successfully @@ -580,16 +853,22 @@ paths: application/json: schema: $ref: '#/components/schemas/CertificateRetrievalMetadata' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' '404': - description: Certificate not found + $ref: '#/components/responses/NotFound' '500': - description: Internal server error + $ref: '#/components/responses/InternalServerError' - /certificates/{certificateId}/documents/{documentId}: + /documents/{documentId}: get: summary: Retrieve a certificate document by its ID description: | - Returns the binary PDF document identified by `documentId` associated with the given certificate. + Returns the binary document identified by `documentId` associated with the given certificate. + The response is served with the `Content-Type` set to the document's `mediaType` + (e.g. `application/pdf`) as advertised in the certificate metadata. Document IDs are listed in the `documents` array of the certificate metadata. tags: - Certificate Provider @@ -608,23 +887,38 @@ paths: description: The document identifier responses: '200': - description: Document returned successfully + description: | + Document returned successfully. The response body is the document + binary, served with the `Content-Type` set to the document's `mediaType`. content: application/pdf: schema: type: string format: binary + image/png: + schema: + type: string + format: binary + application/octet-stream: + schema: + type: string + format: binary + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' '404': - description: Certificate or document not found + $ref: '#/components/responses/NotFound' '500': - description: Internal server error + $ref: '#/components/responses/InternalServerError' /certificate-search: post: - summary: Search certificates by BPN filters + summary: Search certificates description: | - Returns a paginated list of certificates matching the given BPN filters. - All filter lists are optional and combined with AND logic; omitting all filters returns all accessible certificates. + Returns a paginated list of certificates matching the given filters. + All filters are optional and combined with AND logic (any-of within a + single BPN list); omitting all filters returns all accessible certificates. tags: - Certificate Provider parameters: @@ -657,13 +951,24 @@ paths: schema: $ref: '#/components/schemas/PaginatedCertificateList' '400': - description: Request malformed and can not be processed + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' '500': - description: Internal server error + $ref: '#/components/responses/InternalServerError' - /certificate-request: + /certificate-requests: post: - summary: Certificate consumer requests a specific certificate from certificate provider + summary: Request a certificate + description: | + Invoked by a Certificate Consumer to request a certificate. A request + opens a consumer-initiated Certificate Exchange. On acceptance the + Certificate Provider assigns and returns an `exchangeId`, `certificateId` + and `version`, enabling the consumer to poll fulfillment status via + `GET /certificate-requests/{id}` and later retrieve the certificate. + operationId: createCertificateRequest tags: - Certificate Provider requestBody: @@ -672,68 +977,419 @@ paths: application/json: schema: $ref: '#/components/schemas/CertificateRequest' + examples: + byType: + summary: Request a certificate by type and location + value: + certifiedBpn: "BPNL0000000002CD" + certificateType: "ISO9001" + certifiedLocations: + - "BPNS000000000002" responses: '202': - description: Certificate request in processing + description: | + The request was accepted. The body carries the assigned `exchangeId`, + `certificateId` and `version` and the current fulfillment status. content: application/json: schema: - $ref: '#/components/schemas/CertificateRequestInProgressResponse' + $ref: '#/components/schemas/CertificateRequestResponse' + examples: + Acknowledged: + $ref: '#/components/examples/CertificateRequestAcknowledged' + Fulfilled: + $ref: '#/components/examples/CertificateRequestFulfilled' + Rejected: + $ref: '#/components/examples/CertificateRequestRejected' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '500': + $ref: '#/components/responses/InternalServerError' + + /certificate-requests/{id}: + get: + summary: Poll the fulfillment status of a request + description: | + Invoked by a Certificate Consumer to poll the fulfillment status of a + previously submitted certificate request. This endpoint reports only the + Fulfillment phase. + + Polling is optional: if the Certificate Consumer exposes the Certificate + Consumer Notification API, the Certificate Provider MAY instead push these + fulfillment status updates to it as a + `org.catena-x.ccm.CertificateFulfillmentStatus.v1` event. The push and the + poll are equivalent and carry the same `exchangeId` and `status`. + operationId: getCertificateRequestStatus + tags: + - Certificate Provider + parameters: + - name: id + in: path + required: true + description: The `exchangeId` returned in the response to the certificate request. + schema: + type: string + responses: '200': - description: Certificate request finished processing + description: Current fulfillment status of the request. content: application/json: schema: - $ref: '#/components/schemas/CertificateRequestFinishedResponse' + $ref: '#/components/schemas/CertificateRequestResponse' examples: - Completed: - $ref: '#/components/examples/CertificateRequestCompleted' + Acknowledged: + $ref: '#/components/examples/CertificateRequestAcknowledged' + Fulfilled: + $ref: '#/components/examples/CertificateRequestFulfilled' Rejected: $ref: '#/components/examples/CertificateRequestRejected' - '400': - description: Request malformed and can not be processed + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' '500': - description: Internal server error + $ref: '#/components/responses/InternalServerError' - /certificate-notification: + /certificate-acceptance-notifications: post: - summary: Certificate provider notifies certificate consumer about a lifecycle change for a certificate + summary: Submit a certificate acceptance status event + description: | + Invoked by a Certificate Consumer to report the acceptance status of a + certificate. The status is carried in `data.status` and the Certificate + Provider dispatches on it. The event is correlated to its Certificate + Exchange by the `exchangeId` in `data`. Acceptance MUST reference an + existing exchange (opened by a consumer request or a provider + notification); merely retrieving a certificate does not establish an + exchange. An unknown `exchangeId` is rejected with 404. The body is a + single CloudEvent or a batch (JSON array). + operationId: postCertificateAcceptanceNotification tags: - - Certificate Consumer + - Certificate Provider requestBody: required: true content: - application/json: + application/cloudevents+json: schema: - $ref: '#/components/schemas/CertificatePush' + oneOf: + - $ref: '#/components/schemas/CertificateAcceptanceStatusEvent' + - type: array + items: + $ref: '#/components/schemas/CertificateAcceptanceStatusEvent' + examples: + retrieved: + summary: Status RETRIEVED + value: + specversion: "1.0" + type: org.catena-x.ccm.CertificateAcceptanceStatus.v1 + source: urn:bpn:BPNL0000000002CD + subject: BPNL0000000001AB + id: e9f8d7c6-b5a4-9382-7160-5a4b3c2d1e0f + time: "2025-05-04T08:00:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-acceptance-status.json + data: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + status: RETRIEVED + accepted: + summary: Status ACCEPTED + value: + specversion: "1.0" + type: org.catena-x.ccm.CertificateAcceptanceStatus.v1 + source: urn:bpn:BPNL0000000002CD + subject: BPNL0000000001AB + id: a7b8c9d0-e1f2-3a4b-5c6d-7e8f9a0b1c2d + time: "2025-05-04T09:00:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-acceptance-status.json + data: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + status: ACCEPTED + rejected: + summary: Status REJECTED + value: + specversion: "1.0" + type: org.catena-x.ccm.CertificateAcceptanceStatus.v1 + source: urn:bpn:BPNL0000000002CD + subject: BPNL0000000001AB + id: f1a2b3c4-d5e6-7f8a-9b0c-1d2e3f4a5b6c + time: "2025-05-04T09:00:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-acceptance-status.json + data: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + status: REJECTED + certificateErrors: + - message: Certificate has expired + - message: Unexpected data format + locationErrors: + - bpn: BPNS000000000002 + messages: + - Site BPNS000000000002 has been Rejected + errored: + summary: Status ERRORED + value: + specversion: "1.0" + type: org.catena-x.ccm.CertificateAcceptanceStatus.v1 + source: urn:bpn:BPNL0000000002CD + subject: BPNL0000000001AB + id: c8d7e6f5-a4b3-2109-8765-432109fedcba + time: "2025-05-04T08:30:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-acceptance-status.json + data: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + status: ERRORED + certificateErrors: + - message: Certificate document is malformed and cannot be validated responses: - '200': - description: Notification processed successfully + '204': + description: Acceptance status accepted; response body is empty. + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' '500': - description: Internal server error - '501': - description: The certificate consumer currently does not support processing of notifications + $ref: '#/components/responses/InternalServerError' - /certificate-feedback: + /certificate-notifications: post: - summary: Certificate consumer sends feedback on the validation status of a consumed certificate to the certificate provider + summary: Receive a certificate notification event + description: | + Invoked by a Certificate Provider to deliver a notification to the + Certificate Consumer. Two event types are accepted, distinguished by the + CloudEvents `type`: a certificate lifecycle status event and a + certificate fulfillment status notification. The request body is a single + CloudEvent or a batch (JSON array) of CloudEvents as defined in CX-0000. + + For lifecycle events, only the `CREATED` status opens a Certificate + Exchange; `MODIFIED` and `WITHDRAWN` do not. Fulfillment notifications + report the Fulfillment status of an exchange the consumer opened via a + certificate request and are correlated by `exchangeId`. + operationId: postCertificateNotifications tags: - - Certificate Provider + - Certificate Consumer requestBody: required: true content: - application/json: + application/cloudevents+json: schema: - $ref: '#/components/schemas/CertificateFeedback' + oneOf: + - $ref: '#/components/schemas/CertificateLifecycleStatusEvent' + - $ref: '#/components/schemas/CertificateFulfillmentStatusEvent' + - type: array + items: + oneOf: + - $ref: '#/components/schemas/CertificateLifecycleStatusEvent' + - $ref: '#/components/schemas/CertificateFulfillmentStatusEvent' examples: - Received: - $ref: '#/components/examples/CertificateReceivedStatus' - Accepted: - $ref: '#/components/examples/CertificateAcceptedStatus' - Rejected: - $ref: '#/components/examples/CertificateRejectedStatus' + created: + summary: Status CREATED + value: + specversion: "1.0" + type: org.catena-x.ccm.CertificateLifecycleStatus.v1 + source: urn:bpn:BPNL0000000001AB + subject: BPNL0000000002CD + id: a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d + time: "2025-05-04T07:00:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-lifecycle-status.json + data: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + version: 1 + status: CREATED + certificateType: ISO9001 + validFrom: "2023-01-25" + validUntil: "2026-01-24" + certifiedLocations: + - BPNS00000003AYRE + - BPNA000000000001 + modified: + summary: Status MODIFIED (new version) + value: + specversion: "1.0" + type: org.catena-x.ccm.CertificateLifecycleStatus.v1 + source: urn:bpn:BPNL0000000001AB + subject: BPNL0000000002CD + id: b2c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e + time: "2025-08-01T07:00:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-lifecycle-status.json + data: + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + version: 2 + status: MODIFIED + certificateType: ISO9001 + validFrom: "2023-01-25" + validUntil: "2027-01-24" + certifiedLocations: + - BPNS00000003AYRE + - BPNA000000000001 + withdrawn: + summary: Status WITHDRAWN (no validity) + value: + specversion: "1.0" + type: org.catena-x.ccm.CertificateLifecycleStatus.v1 + source: urn:bpn:BPNL0000000001AB + subject: BPNL0000000002CD + id: c3d4e5f6-7a8b-9c0d-1e2f-3a4b5c6d7e8f + time: "2025-09-01T07:00:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-lifecycle-status.json + data: + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + version: 2 + status: WITHDRAWN + certificateType: ISO9001 + batch: + summary: A batch of lifecycle status events + value: + - specversion: "1.0" + type: org.catena-x.ccm.CertificateLifecycleStatus.v1 + source: urn:bpn:BPNL0000000001AB + subject: BPNL0000000002CD + id: a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d + time: "2025-05-04T07:00:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-lifecycle-status.json + data: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + version: 1 + status: CREATED + certificateType: ISO9001 + validFrom: "2023-01-25" + validUntil: "2026-01-24" + - specversion: "1.0" + type: org.catena-x.ccm.CertificateLifecycleStatus.v1 + source: urn:bpn:BPNL0000000001AB + subject: BPNL0000000002CD + id: d4e5f6a7-8b9c-0d1e-2f3a-4b5c6d7e8f90 + time: "2025-09-01T07:00:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-lifecycle-status.json + data: + certificateId: cert-770e8400-e29b-41d4-a716-446655449999 + version: 3 + status: WITHDRAWN + certificateType: ISO14001 + fulfillmentFulfilled: + summary: Fulfillment status FULFILLED + value: + specversion: "1.0" + type: org.catena-x.ccm.CertificateFulfillmentStatus.v1 + source: urn:bpn:BPNL0000000001AB + subject: BPNL0000000002CD + id: f0e1d2c3-b4a5-6789-0abc-def012345678 + time: "2025-05-04T07:30:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-fulfillment-status.json + data: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + status: FULFILLED + fulfillmentDeclined: + summary: Fulfillment status DECLINED + value: + specversion: "1.0" + type: org.catena-x.ccm.CertificateFulfillmentStatus.v1 + source: urn:bpn:BPNL0000000001AB + subject: BPNL0000000002CD + id: a1c2e3f4-5b6d-7e8f-9a0b-1c2d3e4f5a6b + time: "2025-05-04T07:30:00Z" + datacontenttype: application/json + dataschema: https://w3id.org/catenax/schemas/ccm/certificate-fulfillment-status.json + data: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + status: DECLINED + errors: + - message: Certificate type not offered for the requested location + responses: + '204': + description: Event(s) accepted; response body is empty. + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '500': + $ref: '#/components/responses/InternalServerError' + + /certificate-acceptance-status/{id}: + get: + summary: Query the acceptance status of an exchange + description: | + Invoked by a Certificate Provider to query the current acceptance + status of a Certificate Exchange it opened. The `{id}` path parameter + MUST be the `exchangeId` assigned when the exchange was opened. + operationId: getCertificateAcceptanceStatus + tags: + - Certificate Consumer + parameters: + - name: id + in: path + required: true + description: | + The `exchangeId` of the Certificate Exchange. + schema: + type: string responses: '200': - description: Status updated successfully + description: | + Current acceptance status of the certificate. The `status` value is + one of the Acceptance-phase states, including the non-terminal + `RETRIEVED` indicating that the certificate has been retrieved but + acceptance has not yet concluded. + content: + application/json: + schema: + $ref: '#/components/schemas/CertificateAcceptanceStatusResponse' + examples: + retrieved: + summary: Status RETRIEVED + value: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + status: RETRIEVED + accepted: + summary: Status ACCEPTED + value: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + status: ACCEPTED + rejected: + summary: Status REJECTED + value: + exchangeId: exch-7f3a9c12-4b8e-4d6a-9e21-0c5b2a1d8f44 + certificateId: cert-550e8400-e29b-41d4-a716-446655440000 + status: REJECTED + certificateErrors: + - message: Certificate has expired + locationErrors: + - bpn: BPNS000000000002 + messages: + - Site BPNS000000000002 has been Rejected + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' '500': - description: Internal server error + $ref: '#/components/responses/InternalServerError'