diff --git a/docs/certificate-key/concept-design/core-components/certificate.md b/docs/certificate-key/concept-design/core-components/certificate.md
index 80a18f330..f8f13e19c 100644
--- a/docs/certificate-key/concept-design/core-components/certificate.md
+++ b/docs/certificate-key/concept-design/core-components/certificate.md
@@ -31,16 +31,16 @@ Certificate status represents stage of certificate lifecycle and transition to d
Certificate can be in following states:
-| Status | Description | Transition |
-|--------------------------------------------|--------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `Requested` | The `Certificate` is created (requested) and ready to be issued. | Initial state in case user requests certificate. |
-| `Pending Approval` | The `Certificate` action is waiting to be approved. | When certificate action needs to be approved. |
-| `Pending Issue` (***Not yet supported***) | The `Certificate` action is waiting to be issued at authority. | When certificate is requested to be issued by authority (already approved) and waiting to be approve issuance on authority side. |
-| `Pending Revoke` (***Not yet supported***) | The `Certificate` action is waiting to be revoked at authority. | When certificate is requested to be revoked by authority (already approved) and waiting to be approve revocation on authority side. |
-| `Rejected` | The `Certificate` issuance approval request was rejected. | When approval for certificate issue action was rejected or expired. |
-| `Failed` | The `Certificate` request issuance failed. | When certificate fails to be issued by authority caused by error or invalid request. |
-| `Issued` | The `Certificate` is issued. | Initial state in case certificate is uploaded or discovered.
When certificate is successfully issued.
When certificate revocation failed state returns back to `Issued`. |
-| `Revoked` | The `Certificate` is revoked. | When certificate is successfully revoked. | |
+| Status | Description | Transition |
+|--------------------|-------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `Requested` | The `Certificate` is created (requested) and ready to be issued. | Initial state in case user requests certificate. |
+| `Pending Approval` | The `Certificate` action is waiting to be approved. | When certificate action needs to be approved. |
+| `Pending Issue` | The `Certificate` issuance has been accepted but cannot be completed synchronously and is waiting to be finalised. | When the certification authority accepts an issue or renew request but cannot complete it synchronously (see [Asynchronous operations](#asynchronous-operations)). |
+| `Pending Revoke` | The `Certificate` revocation has been accepted but cannot be completed synchronously and is waiting to be confirmed. | When the certification authority accepts a revoke request but cannot complete it synchronously. |
+| `Rejected` | The `Certificate` issuance approval request was rejected. | When approval for certificate issue action was rejected or expired. |
+| `Failed` | The `Certificate` request issuance failed or the parked issuance was cancelled. | When certificate fails to be issued by authority caused by error or invalid request, or when an operator cancels a `Pending Issue`. |
+| `Issued` | The `Certificate` is issued. | Initial state in case certificate is uploaded or discovered.
When certificate is successfully issued.
When certificate revocation failed state returns back to `Issued`.
When an operator cancels a `Pending Revoke`. |
+| `Revoked` | The `Certificate` is revoked. | When certificate is successfully revoked. |
Certificate state transition diagram is as follows:
@@ -63,10 +63,10 @@ state "Pending Revoke" as PendingRevoke
PendingApproval --> PendingRevoke
PendingApproval --> Issued
PendingApproval --> Revoked
- PendingIssue --> Failed
- PendingIssue --> Issued
- PendingRevoke --> Revoked
- PendingRevoke --> Issued
+ PendingIssue --> Issued : finalise issue
+ PendingIssue --> Failed : cancel pending\nor connector failure
+ PendingRevoke --> Revoked : confirm revocation
+ PendingRevoke --> Issued : cancel pending
Issued --> PendingApproval
Issued --> PendingRevoke
Issued --> Revoked
@@ -77,6 +77,36 @@ state "Pending Revoke" as PendingRevoke
@enduml
```
+### Asynchronous operations
+
+Some certification authorities cannot complete `issue`, `renew`, or `revoke` synchronously — for example, manual or air-gapped CAs, CAs that process requests in batches, or authorities where the operation is performed by a human operator out-of-band. In these cases the operation is **parked** and the certificate moves to `Pending Issue` or `Pending Revoke` until it is finalised. There is no platform-level "offline" or "external" flag on `Authority`, `RA Profile`, or anywhere else — behaviour is determined entirely by the certificate state.
+
+#### Finalising a parked operation
+
+Three operator-driven actions move a parked certificate to its terminal state. They are exposed both via the platform UI (inline icon buttons next to the state badge in the certificate inventory and on the certificate detail page) and via the `Core` client API.
+
+| Action | Applicable state | Resulting state | Description |
+|--------------------|-----------------------------------|----------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|
+| **Finalise Issue** | `Pending Issue` | `Issued` | The operator uploads the externally-issued certificate. The platform validates the upload and stores it against the request. |
+| **Confirm Revoke** | `Pending Revoke` | `Revoked` | The operator confirms that the revocation has been completed. The preserved revoke attributes and `destroyKey` flag are applied. |
+| **Cancel Pending** | `Pending Issue` or `Pending Revoke` | `Failed` (from `Pending Issue`) or `Issued` (from `Pending Revoke`) | The operator aborts the parked operation. An optional `reason` is recorded in the certificate event history. |
+
+When `Finalise Issue` is invoked, the uploaded certificate's public key must match the public key of the original request (hard check); the subject DN is a soft check (a mismatch is logged in the event history but does not block the upload).
+
+When `Cancel Pending` is invoked, the platform also notifies the underlying authority so it can release any state it tracks for the operation. If the authority cannot abort the operation (for example, the underlying CA does not support aborts), the certificate stays in its pending state and the failure reason is surfaced to the operator.
+
+#### Operations blocked while pending
+
+While a certificate is in `Pending Issue` or `Pending Revoke`, the following actions are blocked:
+
+- Renew
+- Rekey
+- Revoke (when state is `Pending Issue`)
+- Re-issue of the same `Requested` certificate
+- Switch RA profile
+
+The escape hatch from a stuck pending state is `Cancel Pending`.
+
### Archived Certificate
Certificate can be marked as archived. When certificate is archived, it will not be validated, and cannot be managed. It is intended for certificates that are not going to be used anymore and should be tracked only for historical reasons, eventually removed from the inventory.
diff --git a/docs/certificate-key/connectors/available-connectors.md b/docs/certificate-key/connectors/available-connectors.md
index ae3684fb2..8215bb9db 100644
--- a/docs/certificate-key/connectors/available-connectors.md
+++ b/docs/certificate-key/connectors/available-connectors.md
@@ -20,6 +20,7 @@ The following `Connectors` are officially available:
| EJBCA NG Connector | Authority Provider | EJBCA |
| | Discovery Provider | EJBCA EJBCA_SCHEDULE |
| Email Notification Provider | Notification Provider | EMAIL |
+| External Authority Provider | Authority Provider | External |
| Webhook Notification Provider | Notification Provider | WEBHOOK |
| HashiCorp Vault Connector | Authority Provider | HVault |
| | Discovery Provider | HVault |
diff --git a/docs/certificate-key/connectors/provider-interfaces/authority-provider-v2.md b/docs/certificate-key/connectors/provider-interfaces/authority-provider-v2.md
index a0e18944d..d86fdac68 100644
--- a/docs/certificate-key/connectors/provider-interfaces/authority-provider-v2.md
+++ b/docs/certificate-key/connectors/provider-interfaces/authority-provider-v2.md
@@ -9,11 +9,13 @@ sidebar_position: 1
Authority Provider v2 interface is used to manage operations with certificates issued by certification authority. The Authority Provider v2 acts as an interface between the `Core` and the certification authority providing the following management functions:
1. Issue
2. Renew
-3. Revoke
+3. Revoke
+4. Cancel a parked issue or revoke (asynchronous flows)
+5. Poll the status of a parked issue or revoke (asynchronous flows)
## How it works
-Authority Provider v2 provides the ability to communicate with different types and technologies of certification authorities.
+Authority Provider v2 provides the ability to communicate with different types and technologies of certification authorities. The platform supports both **synchronous** authorities (the connector returns the issued or revoked certificate immediately) and **asynchronous** authorities (the connector parks the operation and reports back later, or hands the operation off to an operator). The signal between Core and the connector for the asynchronous case is the HTTP response code on the existing `issueCertificate` / `renewCertificate` / `revokeCertificate` calls — see [Asynchronous certificate operations](#asynchronous-certificate-operations) below.
## Provider objects
@@ -152,6 +154,155 @@ Sections below represents the list of processes involved in managing the certifi
@enduml
```
+## Asynchronous certificate operations
+
+When a certificate authority cannot complete `issue`, `renew`, or `revoke` synchronously — for example a manual or air-gapped CA, a CA that processes requests in batches, or an external authority that requires an operator-driven step — the connector **parks** the operation. The platform handles the parked state, finalisation, and cancellation through the existing v2 endpoints plus four purely-additive endpoints.
+
+### Parking signal
+
+The signal is the HTTP response on the existing `issueCertificate` / `renewCertificate` / `revokeCertificate` calls:
+
+| Connector response | Cert state transition | Notes |
+|--------------------------------------------------|------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------|
+| `200 OK` + `certificateData` (+ optional `meta`) | → `Issued` (issue/renew) or `Revoked` (revoke). | Today's synchronous behaviour. Unchanged. |
+| `202 Accepted` (+ optional `meta`) | → `Pending Issue` (issue/renew) or `Pending Revoke` (revoke). Parked. | New. Existing connectors continue to return `200`; sync paths are unaffected. |
+| Any other status / connector exception | → `Failed` (or back to `Issued` for revoke). Unchanged. | |
+
+The `meta` returned with `202` is **opaque** to Core. The connector chooses what to put in it (order ID, transaction reference, multi-field state) and Core stores it against the certificate via the same persistence path used for `200 OK + meta` today. Core does not interpret the value.
+
+There is **no platform-level "offline" or "external" classification** of authorities, RA profiles, or connectors. Behaviour is driven by certificate state and the connector's response.
+
+### Parked-operation lifecycle endpoints
+
+Four purely-additive endpoints complete the parked-operation lifecycle:
+
+| Method | HTTP | Path | Body | Response |
+|------------------------------|--------|---------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------|
+| `cancelIssueCertificate` | `POST` | `/v2/authorityProvider/authorities/{uuid}/certificates/issue/cancel` | `CertificateOperationCancelRequestDto { raProfileAttributes, meta }` | `204 No Content` (acknowledged); `404` (not tracked); `422` (refuses to abort, with reason) |
+| `cancelRevokeCertificate` | `POST` | `/v2/authorityProvider/authorities/{uuid}/certificates/revoke/cancel` | `CertificateOperationCancelRequestDto { raProfileAttributes, meta }` | `204 No Content` (acknowledged); `404` (not tracked); `422` (refuses to abort, with reason) |
+| `getIssueCertificateStatus` | `POST` | `/v2/authorityProvider/authorities/{uuid}/certificates/issue/status` | `CertificateOperationStatusRequestDto { raProfileAttributes, meta }` | `CertificateOperationStatusResponseDto { status: IN_PROGRESS \| COMPLETED \| FAILED, certificateData?, meta?, reason? }` |
+| `getRevokeCertificateStatus` | `POST` | `/v2/authorityProvider/authorities/{uuid}/certificates/revoke/status` | `CertificateOperationStatusRequestDto { raProfileAttributes, meta }` | `CertificateOperationStatusResponseDto { status: IN_PROGRESS \| COMPLETED \| FAILED, meta?, reason? }` |
+
+The operation type is explicit in the path so the connector's two cancel handlers (and two status handlers) know unambiguously what they're acting on. Core dispatches based on certificate state — `Pending Issue` certificates route to the `issue/*` endpoints; `Pending Revoke` certificates route to the `revoke/*` endpoints.
+
+**Cancel response semantics.** Core treats the connector's response as follows:
+- `204 No Content` — cancel acknowledged. Core proceeds with the local state transition.
+- `404 Not Found` — connector does not track the operation (already finalised externally, or implementation is stateless). **Soft failure**: Core records the result in event history and proceeds with the local transition.
+- `422 Unprocessable Entity` (with reason) — connector refuses to abort (e.g., the underlying CA cannot abort the operation). **Hard failure**: Core surfaces the reason to the user and leaves the certificate in its pending state.
+- `5xx` / network error — soft failure. Core records the error and proceeds with the local transition (cancel is user intent).
+
+**Status response semantics.** When `status = COMPLETED` for an issue/renew, `certificateData` carries the Base64 cert content and Core finalises through the same internal path used by manual upload. When `status = COMPLETED` for revoke, no payload is needed — Core finalises the revoke transition. When `status = FAILED`, the connector's `reason` is surfaced and the certificate moves to `Failed`. The optional `meta` in the response lets the connector refresh the tracked state on each poll.
+
+Sync-only connectors are never asked: Core only invokes these four endpoints against certificates in `Pending Issue` / `Pending Revoke`, which only exist after a `202` response. Existing sync connectors are unaffected and need no changes. A connector that does not track operation state may return `404 Not Found` from a status endpoint; Core treats this as "this connector does not support polling for this operation".
+
+### Park `Certificate` Issue (Renew)
+
+```plantuml
+ @startuml
+ autonumber
+ skinparam topurl https://docs.czertainly.com/api/
+ Client -> Core [[core-client-operations/#tag/v2-Client-Operations/operation/issueCertificate]]: Issue Certificate
+ Core -> Connector [[connector-authority-provider-v2/#tag/Certificate-Management/operation/issueCertificate]]: Issue Certificate
+ Connector -> CA: Submit issue request
+ CA --> Connector: Operation accepted (no certificate yet)
+ Connector --> Core: 202 Accepted (+ optional meta)
+ Core -> Core : Store meta
+ Core -> Core : Set Certificate state to Pending Issue
+ Core --> Client: Return Certificate UUID
+ @enduml
+```
+
+The same flow applies to `renewCertificate` — the new certificate ends in `Pending Issue` while the predecessor remains `Issued` until the new certificate is finalised.
+
+### Park `Certificate` Revoke
+
+```plantuml
+ @startuml
+ autonumber
+ skinparam topurl https://docs.czertainly.com/api/
+ Client -> Core [[core-client-operations/#tag/v2-Client-Operations/operation/revokeCertificate]]: Revoke Certificate
+ Core -> Connector [[connector-authority-provider-v2/#tag/Certificate-Management/operation/revokeCertificate]]: Revoke Certificate
+ Connector -> CA: Submit revoke request
+ CA --> Connector: Operation accepted (not yet revoked)
+ Connector --> Core: 202 Accepted (+ optional meta)
+ Core -> Core : Store meta + preserve destroyKey flag and revoke attributes
+ Core -> Core : Set Certificate state to Pending Revoke
+ Core --> Client: Return revocation status
+ @enduml
+```
+
+The `destroyKey` flag and revoke attributes from the original request are preserved on the certificate and applied when the parked revoke is confirmed.
+
+### Finalise parked Issue (manual upload)
+
+When an operator uploads the externally-issued certificate, Core verifies the upload, asks the connector to identify it, and transitions the certificate to `Issued`.
+
+```plantuml
+ @startuml
+ autonumber
+ skinparam topurl https://docs.czertainly.com/api/
+ Client -> Core [[core-client-operations/#tag/v2-Client-Operations/operation/manuallyIssueCertificate]]: Manual Finalise Issue
+ Core -> Core : Validate upload (public key match)
+ Core -> Connector [[connector-authority-provider-v2/#tag/Certificate-Management/operation/identifyCertificate]]: Identify Certificate
+ Connector --> Core : Identification result
+ Core -> Core : Store Certificate, apply custom attributes
+ Core -> Core : Set Certificate state to Issued
+ Core --> Client : Return Certificate detail
+ @enduml
+```
+
+### Confirm parked Revoke
+
+Used when the revocation has been completed externally and the operator confirms it in the platform.
+
+```plantuml
+ @startuml
+ autonumber
+ skinparam topurl https://docs.czertainly.com/api/
+ Client -> Core [[core-client-operations/#tag/v2-Client-Operations/operation/manuallyConfirmRevoke]]: Manual Confirm Revoke
+ Core -> Core : Apply preserved revoke attributes
+ Core -> Core : Destroy key if requested
+ Core -> Core : Set Certificate state to Revoked
+ Core --> Client : Return confirmation
+ @enduml
+```
+
+### Cancel parked operation
+
+Used when the parked operation is no longer wanted. Core dispatches to the appropriate connector cancel endpoint based on certificate state.
+
+```plantuml
+ @startuml
+ autonumber
+ skinparam topurl https://docs.czertainly.com/api/
+ Client -> Core [[core-client-operations/#tag/v2-Client-Operations/operation/cancelPendingCertificateOperation]]: Cancel Pending Operation
+ alt state is Pending Issue
+ Core -> Connector [[connector-authority-provider-v2/#tag/Certificate-Management/operation/cancelIssueCertificate]]: Cancel Issue
+ else state is Pending Revoke
+ Core -> Connector [[connector-authority-provider-v2/#tag/Certificate-Management/operation/cancelRevokeCertificate]]: Cancel Revoke
+ end
+ Connector --> Core : Cancel response
+ alt Connector refused (422)
+ Core --> Client : Validation error (state unchanged)
+ else Connector accepted, not tracked, or transient failure
+ Core -> Core : Apply local transition (Failed / Issued)
+ Core --> Client : Return cancellation status
+ end
+ @enduml
+```
+
+### Operations blocked while pending
+
+While a certificate is in `Pending Issue` or `Pending Revoke`, the following client operations return `400 Bad Request`:
+
+- `renewCertificate`
+- `rekeyCertificate`
+- `revokeCertificate` (for certificates in `Pending Issue`)
+- Re-issue of the same `Requested` certificate
+- Switch RA profile
+
+The escape hatch from a stuck pending state is **Cancel parked operation**.
+
## Specification and example
The Authority Provider v2 implements [Common Interfaces](../common-interfaces/overview.md) and the following additional interfaces:
diff --git a/docs/certificate-key/current-versions.md b/docs/certificate-key/current-versions.md
index 0bd5d8e0f..44b4b40e0 100644
--- a/docs/certificate-key/current-versions.md
+++ b/docs/certificate-key/current-versions.md
@@ -46,6 +46,7 @@ The Operator Web was merged with the Administrator Web in the version `2.2.0`.
| PyADCS Connector | `1.1.5` | `docker.io/czertainly/czertainly-pyadcs-connector` |
| HashiCorp Vault Connector | `1.2.0` | `docker.io/czertainly/czertainly-hashicorp-vault-connector` |
| EJBCA Legacy Connector | `1.5.0` | `harbor.3key.company/czertainly/czertainly-ejbca-legacy-ca-connector` |
+| External Authority Provider | `1.0.0` | `docker.io/czertainly/czertainly-external-authority-provider` |
| Keystore Entity Provider | `1.4.2` | `docker.io/czertainly/czertainly-keystore-entity-provider` |
| X.509 Compliance Provider | `1.3.1` | `docker.io/czertainly/czertainly-x509-compliance-provider` |
| Software Cryptography Provider | `1.3.1` | `docker.io/czertainly/czertainly-software-cryptography-provider` |
diff --git a/docs/certificate-key/protocols/cmp/overview.md b/docs/certificate-key/protocols/cmp/overview.md
index 032756bdf..08d089d75 100644
--- a/docs/certificate-key/protocols/cmp/overview.md
+++ b/docs/certificate-key/protocols/cmp/overview.md
@@ -27,6 +27,8 @@ The CMP implementation supports the following PKI message types and their associ
| `kur` | Key Update Request | [RFC 4210, section 5.3.5](https://datatracker.ietf.org/doc/html/rfc4210#section-5.3.5) |
| `rr` | Revocation Request | [RFC 4210, section 5.3.9](https://datatracker.ietf.org/doc/html/rfc4210#section-5.3.9) |
| `certConf` | Certificate Confirm | [RFC 4210, section 5.3.18](https://datatracker.ietf.org/doc/html/rfc4210#section-5.3.18) |
+| `pollReq` | Polling Request | [RFC 4210, section 5.3.22](https://datatracker.ietf.org/doc/html/rfc4210#section-5.3.22) |
+| `pollRep` | Polling Response | [RFC 4210, section 5.3.22](https://datatracker.ietf.org/doc/html/rfc4210#section-5.3.22) |
## Supported request formats
diff --git a/docusaurus.config.js b/docusaurus.config.js
index c9514f2bb..ef0a1e84b 100644
--- a/docusaurus.config.js
+++ b/docusaurus.config.js
@@ -1,7 +1,7 @@
const lightCodeTheme = require('prism-react-renderer').themes.github;
const darkCodeTheme = require('prism-react-renderer').themes.dracula;
-const apiVersion = '2.17.0';
+const apiVersion = 'main';
const chartVersion = '2.17.0';
const cscVersion = '1.6.0';