# Task: Issue an e-CPF certificate

I want my application to do what Lacuna's "Issue an e-CPF certificate" demo does: Amplia issues a test e-CPF, with the key made on Amplia's own issue page.

- Docs: https://docs.lacunasoftware.com/en-us/articles/amplia
- Live demo: https://try.lacunasoftware.com/en/amplia/issue-e-cpf-certificate
- This demo for AI agents: https://try.lacunasoftware.com/en/amplia/issue-e-cpf-certificate.md

## Samples

- Java (spring-boot): [Issue a certificate with the key on the user's machine using Amplia](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/java/springmvc/src/main/java/com/lacunasoftware/pkisuite/controller/IssueCertUserAmpliaController.java)
- Node.js (express): [Issue a certificate with the key on the user's machine using Amplia](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/nodejs/expressmvc/routes/issue-cert-user-amplia.js)
- PHP (php): [Issue a certificate with the key on the user's machine using Amplia](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/php/plain/public/issue-cert-user-amplia/index.php)
- Python (flask): [Issue a certificate with the key on the user's machine using Amplia](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/python/flask/sample/views/issue_cert_user_amplia.py)

## Call sequence

Recorded on 2026-10-12 against the real products with Lacuna's public test certificates and data. Keys, tokens, sessions, ids, file contents and personal data are masked.

### 1. Amplia creates the e-CPF order, the person and their mobile in its PKI-Brazil parameters, and gives its issue link, which holds the order's secret, with the Demo page as the return URL.

On the server: `IAmpliaService.CreateOrderAsync, then IAmpliaService.GetOrderIssueLinkAsync(orderId, returnUrl)`

`POST https://amplia.lacunasoftware.com/api/orders/pki-brazil` → 200

Request:

```json
{
  "Parameters": {
    "Format": 1,
    "Name": "(DEMO) Diego Rocha Gomes",
    "EmailAddress": "someone@example.com",
    "Cpf": "***.***.***-**",
    "BirthDate": "1988-12-06T00:00:00",
    "OrganizationalUnits": [
      "Lab"
    ],
    "Organization": "Lacuna Software Demo",
    "Country": "BR",
    "PhoneNumber": "+55 ** *****-****"
  },
  "CAId": "00000000-0000-0000-0000-000000000000",
  "Kind": 1,
  "Validity": "365.00:00:00",
  "IssuePendingApproval": false
}
```

Response:

```json
{
  "parameters": {
    "format": "PkiBrazil",
    "name": "(DEMO) Diego Rocha Gomes",
    "emailAddress": "someone@example.com",
    "cpf": "***.***.***-**",
    "birthDate": "1988-12-06T00:00:00",
    "organizationalUnits": [
      "Lab"
    ],
    "organization": "Lacuna Software Demo",
    "country": "BR",
    "phoneNumber": "+55 ** *****-****"
  },
  "canDelete": true,
  "canEdit": true,
  "id": "00000000-0000-0000-0000-000000000000",
  "subscriptionId": "00000000-0000-0000-0000-000000000000",
  "dateCreated": "2026-10-12T02:08:02.1897217+00:00",
  "caId": "00000000-0000-0000-0000-000000000000",
  "name": "(DEMO) Diego Rocha Gomes (***.***.***-**)",
  "alias": "(DEMO) Diego Rocha Gomes (***.***.***-**)",
  "certificateType": "e-CPF",
  "subjectIdentifier": "***",
  "subjectDisplayName": "(DEMO) Diego Rocha Gomes",
  "emailAddress": "someone@example.com",
  "status": "Pending",
  "kind": "PublicKey",
  "format": "PkiBrazil",
  "issuePendingApproval": false
}
```

`GET https://amplia.lacunasoftware.com/api/orders/00000000-0000-0000-0000-000000000000/issue-link?returnUrl={value}` → 200

Response:

```json
"https://amplia.lacunasoftware.com/issue/{token}?returnUrl={value}"
```

### 2. The visitor goes on to Amplia's issue page, which texts a code to the mobile, makes the key with Web PKI and issues the e-CPF; it needs a person, so the rest is not recorded.

In the browser: `NavigationManager.NavigateTo(issueLink, forceLoad: true)`

## Patterns to copy

- This is a hosted flow: create the order with the holder's `PkiBrazilCertificateParameters`, phone number included, then ask for the order's issue link (`GetOrderIssueLinkAsync`) and send the user to Amplia's issue page, which makes the key with Web PKI and issues the certificate.
- The issue link holds the order's secret: anyone with it can issue the certificate. Keep it on the server, never log or show it, and open it with a full page load in the same tab.
- Amplia's issue page needs Web PKI, which doesn't run on phones or in desktop Safari: check before creating the order, since an order made there could never finish.
- Give the return URL a tamper-proof, expiring token naming the order (the Demo's lasts 1 hour), not a bare order id. On return, `GetOrderAsync` and fetch the certificate only once the order is `Issued` with a `CertificateId`.
- Register the client with `services.AddAmplia()` so its `HttpClient` comes from `IHttpClientFactory`. Treat Amplia's 4xx answers as refusals and anything else as a failure, and show `AmpliaException.Code`, never its message.

## What to do

Implement this in my project with its existing stack, language, framework and conventions, following the call sequence and the patterns above, and adapting the samples rather than copying them whole. Ask me before adding any dependency or package. Keep API keys and private keys out of the browser and out of the repository.
