# Task: Firmar por lotes (Certificado instalado, con PKI SDK)

I want my application to do what Lacuna's "Firmar por lotes (Certificado instalado, con PKI SDK)" demo does: Firme varios PDF con una sola confirmación de su propio certificado, con PKI SDK y Web PKI.

- Docs: https://docs.lacunasoftware.com/en-us/articles/pki-sdk/signatures
- Live demo: https://try.lacunasoftware.com/es/pki-suite/sign-a-batch
- This demo for AI agents: https://try.lacunasoftware.com/es/pki-suite/sign-a-batch.md

## Samples

- .NET (aspnet-mvc): [Sign multiple PDFs with PKI SDK](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/mvc/PkiSuiteAspNetMvcSample/Controllers/BatchPadesSignatureSdkController.cs)

## Call sequence

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

### 1. List the visitor's certificates, with no filter.

In the browser: `pki.listCertificates()`

### 2. Read the chosen certificate's public part once for the whole Batch: no PIN is asked, and the key stays where it is.

In the browser: `pki.readCertificate({ thumbprint })`

### 3. Prepare PDF 1 of 2: PKI SDK picks the Signature policy, draws the stamp on its last page and returns its hash. Keep its state, paired with the PDF.

On the server, in process: `PadesSigner.GetToSignBytes(out algorithm, out transferData)`

### 4. Prepare PDF 2 of 2: PKI SDK picks the Signature policy, draws the stamp on its last page and returns its hash. Keep its state, paired with the PDF.

On the server, in process: `PadesSigner.GetToSignBytes(out algorithm, out transferData)`

### 5. Sign every hash at once, each with its digest algorithm, behind one confirmation: the key never leaves the browser's store or token. The signatures come back in the hashes' order.

In the browser: `pki.signHashes({ certificateThumbprint, hashes })`

### 6. Complete PDF 1 of 2: PKI SDK sets its own signature with its state from the preparation.

On the server, in process: `PadesSigner.SetPreComputedSignature(signature, transferData), then PadesSigner.ComputeSignature()`

### 7. Complete PDF 2 of 2: PKI SDK sets its own signature with its state from the preparation.

On the server, in process: `PadesSigner.SetPreComputedSignature(signature, transferData), then PadesSigner.ComputeSignature()`

## Patterns to copy

- Don't loop the single-file flow file by file: the server prepares every PDF (Start, which returns each one's hash), the browser signs all the hashes in one Web PKI `signHashes` call, so the signer confirms once, then the server completes each PDF with its own signature, in the order of the hashes. Keep each Start's state on the server, paired with its PDF, until its Complete.
- Read the certificate once, with `readCertificate`, before the first Start: every PDF is prepared with the same public certificate, and the private key stays in the browser, which only ever returns the raw signatures.
- Ask Web PKI for the API version that has `signHashes` (`requiredApiVersion: '1.9.0'`, web extension 2.17.0), so an older extension is reported as outdated before the Batch starts. Desktop Safari's Web PKI has no `signHashes`: there, call `preauthorizeSignatures` once with the number of hashes, which asks the signer once, then `signHash` for each hash in turn.
- Let one PDF fail without stopping the Batch: a Start or Complete that fails marks only its own file, and the others go on; if `signHashes` itself fails, every PDF still waiting fails with its error code. Count the Batch against your per-IP limit for all its PDFs before the first Start, so it is never refused halfway.
- Show the signer, for each document, the one fact needed to decide quickly, such as the payer and amount of an invoice, from your own data rather than read from the PDF, with a checkbox to leave it out before signing.
- Let the signer look at each document before signing without sending it anywhere: keep the file the browser already holds under a `blob:` URL, link the row's preview to it in a new tab, the browser's own PDF viewer, and draw its first page as a thumbnail with pdf.js, one release served by the site itself, so no third party sees the visitor. A thumbnail that can't be drawn falls back to a plain PDF icon, and the Batch works as before.

## 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.
