---
title: "Issue on the holder's machine"
description: "How to have the holder issue an Amplia certificate on their own machine: create the order, send them to Amplia's issue page with an issue link, and read the certificate when they return."
canonical: "https://docs.lacunasoftware.com/en-us/articles/amplia/integration/issue-on-user-machine"
locale: "en"
product: "Amplia"
last_updated: "2026-10-12"
---

# Issue on the holder's machine

In this flow, the holder issues their own certificate on Amplia's issue page, which makes the key pair on their computer with
[Web PKI](https://docs.lacunasoftware.com/en-us/articles/web-pki): your system only creates the order and sends the holder to the page. 

## Flow

1. Your server [creates the order](https://docs.lacunasoftware.com/en-us/articles/amplia/integration/orders), with the holder's phone number in its parameters.
1. Your server asks for the order's issue link, `GET /api/orders/{id}/issue-link?returnUrl=…`, passing the URL that Amplia returns to
   once the certificate is issued. The response is the link itself, a JSON string such as `"https://amplia.lacunasoftware.com/issue/{token}?returnUrl={value}"`.
1. The holder opens the link. Amplia's issue page texts a code to their phone, makes the key pair with Web PKI and issues the certificate.
1. Amplia sends the holder back to the return URL.

The samples render a page that redirects the holder to the link: 

**Node.js**

```js
const RETURN_URL = 'http://localhost:3000/issue-cert-user-amplia/complete';

let request = new CreateOrderRequest({
	caId: CONFIG['caId'],
	validityEnd: Util.getTwoYearsFromNowDate(),
	kind: CertificateKinds.PUBLIC_KEY,
	parameters: new PkiBrazilCertificateParameters({
		name: req.body.subjectName,
		cpf: req.body.cpf,
		phoneNumber: req.body.phoneNumber
	})
});
let order = await client.createOrder(request);
let link = await client.getOrderIssueLink(order.getId(), RETURN_URL);
res.render('issue-cert-user-amplia/redirect', {
	redirectLink: link
});
```

**Java**

```java
private final String returnUrl = "http://localhost:60695/issue-cert-user-amplia/complete";

PkiBrazilCertificateParameters parameters = new PkiBrazilCertificateParameters();
parameters.setName(request.getSubjectName());
parameters.setCpf(request.getCpf());
parameters.setPhoneNumber(request.getPhoneNumber());
createOrderRequest.setParameters(parameters);

Order<PkiBrazilCertificateParameters> order = client.createOrder(createOrderRequest);
String redirectLink = client.getOrderIssueLink(order.getId(), returnUrl);
```

**PHP**

```php
$RETURN_URL = 'http://localhost:8000/issue-cert-user-amplia/complete.php';

$parameters = new PkiBrazilCertificateParameters();
$parameters->name = $subjectName;
$parameters->cpf = $cpf;
$parameters->phoneNumber = $phoneNumber;
$createOrderRequest->parameters = $parameters;

$order = $client->createOrder($createOrderRequest);
$redirectLink = $client->getOrderIssueLink($order->id, $RETURN_URL);
```

**Python**

```python
RETURN_URL = 'http://localhost:5000/issue-cert-user-amplia/complete'

parameters = PkiBrazilCertificateParameters()
parameters.name = request.form['subjectName']
parameters.cpf = request.form['cpf']
parameters.phone_number = request.form['phoneNumber']
req.parameters = parameters

order = client.create_order(req)
link = client.get_order_issue_link(order.id, RETURN_URL)
```

The samples' return page only tells the holder that the certificate was issued. 
To use the certificate in your system, read the order when the holder returns, and fetch the certificate only once the order's
`status` is `Issued` and it has a `certificateId`: 
`GET /api/orders/{id}`, then `GET /api/v2/certificates/{certificateId}`. 
Amplia can also notify your system when the certificate is issued, through a [webhook](https://docs.lacunasoftware.com/en-us/articles/amplia/integration#webhook).

## Keep the issue link safe

* The issue link holds the order's secret: anyone who has 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. 
* Give the return URL a tamper-proof, expiring token that names the order, rather than the bare order id; the demo's token lasts one
  hour. 
* The demo checks, before creating the order, that the visitor's browser can run Web PKI, which doesn't run on phones or in desktop
  Safari: an order created there could never be finished on the issue page. 
  Amplia also lets holders issue on Android and iOS phones. 
  TODO(docs): how issuance on a phone works.

## Other ways to deliver the issue link

Instead of redirecting the holder yourself, you can have Amplia send the link, or show a QR code: 

| Delivery | Endpoint | What happens |
|---|---|---|
| Email | `POST /api/orders/{id}/send-issue-email-request` | Amplia emails the address in the order's parameters (`emailAddress`) a message with a button to the issue link and, in case the button doesn't work, the address of the issue page and a code to type there. |
| SMS | `POST /api/orders/{id}/send-issue-sms-request`, or `POST /api/v2/orders/{id}/send-issue-sms-request` with a body `{"attemptNumber": …}` | Amplia texts the issue link to the phone number in the order's parameters (`phoneNumber`). |
| QR code | `GET /api/orders/{id}/issue-qrcode-image` | Returns a PNG image of a QR code that leads to the issue page. |
| | `GET /api/orders/{id}/issue-qrcode-data` | Returns, as a JSON string, the URL that the QR code holds, for you to draw the code yourself. |
| | `GET /api/orders/{id}/issue-qrcode-link` | Returns a link to `GET /api/orders/{id}/issue-qrcode`, which serves the same PNG without the API key, for an `<img>` tag. |

* Email and SMS send the link only to the holder: your system never sees it, so an application with the **Registration Agent** role
  can use them, while the issue link and the QR code need a role that can issue (see [Application roles](https://docs.lacunasoftware.com/en-us/articles/amplia/integration#api-key)). 
* Both fail if the order was already issued (`OrderAlreadyFulfilled`). The email needs an email address in the order (`NoEmailAddress`)
  and email enabled on the instance (`EmailDisabled`); the SMS needs a phone number (`NoPhoneNumber`) and SMS enabled (`SmsDisabled`). 
* The email's subject is in Portuguese, "Link para emissão do seu certificado digital". 
* The QR code holds an `…/ic/1/{secret}` address on the instance (or on the QR code gateway configured for it), which redirects to the
  issue page. Like the issue link, it lets anyone who reads it issue the certificate. 
* Of the client libraries, only the .NET client wraps these endpoints (`SendIssueEmailAsync`, `SendIssueSmsAsync`,
  `GetOrderIssueQRCodeDataAsync`, `GetOrderIssueQRCodeImageAsync`); with the others, call them directly, with the `X-Api-Key` header. 
* Emails and SMS messages go through the providers configured on the instance: see [Configure email](https://docs.lacunasoftware.com/en-us/articles/amplia/on-premises/configure-email)
  and [Configure SMS](https://docs.lacunasoftware.com/en-us/articles/amplia/on-premises/configure-sms). 
* On an on-premises instance, the `Amplia__IssueProcedureConfirmationEnabled` setting turns off the SMS confirmation during issuance. 
* TODO(docs): what `attemptNumber` changes (the first version of the SMS endpoint sends `0`).

## See also

* [Create certificate orders](https://docs.lacunasoftware.com/en-us/articles/amplia/integration/orders)
* [Issue from a CSR](https://docs.lacunasoftware.com/en-us/articles/amplia/integration/issue-from-csr), when the key is made on your server or in your own page
* [Errors](https://docs.lacunasoftware.com/en-us/articles/amplia/integration/errors)

**Demos and samples**

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