SealingLast updated on
Last updated on
Prepare canonical PDFs for signing
Rendering produces a document in its native layer format. Sealing prepares a filled form for signing by producing one canonical, flattened PDF, its SHA-256 hash, and the signature-field map. Conversion may happen anywhere; placement and hashing always run locally, so both are verifiable.
Declare signature slots
A layer declares its signing fields once, in a signatures map. Each slot
binds a party to a placement:
.inlineLayer('agreement', {
mimeType: 'text/markdown',
text: '... {{#with parties.client}}Sign: {{signature "client-sig"}}{{/with}} ...',
signatures: {
'client-sig': {
party: { role: 'client' },
type: 'signature',
placement: 'auto', // marker-located
},
'witness-sig': {
party: { role: 'witness' },
type: 'signature',
placement: {
anchor: { text: 'Witnessed by:', offsetY: 12 }, // text-located
width: 200,
height: 40,
},
},
'client-date': {
party: { role: 'client' },
type: 'date_signed',
placement: { page: 2, x: 400, y: 700, width: 100, height: 20 }, // fixed
},
},
})Three placement strategies, one engine:
| Placement | How the position is found | Use when |
|---|---|---|
'auto' | An invisible marker is injected at the slot's placeholder during rendering and located in the converted PDF | Flowing text templates (markdown, HTML) |
anchor | Literal document text is found in the converted PDF; occurrence picks a match when the text repeats, otherwise it must be unique | Text known at design time, position unknown |
| absolute | Declared page/x/y coordinates | PDF templates, fixed layouts |
Misconfiguration fails before any rendering or conversion with a
SealConfigError naming every problem: unknown party roles, a
required-signature party no slot places, or a required slot without a
signatory.
Seal a PDF layer locally
PDF layers need no adapter or network call. Paradoc renders the layer, resolves placements, flattens, and hashes the exact canonical bytes:
const signable = await draft.seal({ resolver })
signable.canonicalPdfBytes // Uint8Array
signable.canonicalPdfHash // sha256:...
signable.signatureMap // provider-neutral PDF coordinatesSeal markdown, HTML, or DOCX
Non-PDF layers need a converter: a SealAdapter that turns the rendered
document into PDF bytes and nothing else. Placement is not the converter's
job — core locates anchors and markers itself with the built-in locator, so a
pure byte converter works with zero configuration:
import { hostedSealAdapter } from '@paradoc/sdk'
const signable = await draft.seal({
adapter: hostedSealAdapter({ apiKey }),
})A custom converter is one honest function:
import type { SealAdapter } from '@paradoc/core'
const adapter: SealAdapter = {
async convert({ document }) {
return { pdf: await myHtmlToPdf(document.content) }
},
}If an adapter does resolve placements itself (returning a complete
signatureMap), core respects it and skips the locator. Pass locate to
substitute a custom locator tier.
For 'auto' slots, core renders twice: a marker pass locates every
placeholder in the converted PDF, then a clean render becomes the canonical
document. If the marker's width shifts a line wrap between passes, sealing
fails loud rather than misplacing a field.
Inspect before sealing
prepareSeal() resolves the signature map without flattening, hashing, or
leaving the draft phase — useful for envelope flows that need coordinates
before committing:
const prep = await draft.prepareSeal({ adapter })
prep.pdf // the converted PDF the coordinates describe (pre-flatten)
prep.signatureMap // resolved fields
prep.provenance // per field: 'declared' | 'anchor' | 'marker'
prep.warnings // e.g. slots skipped for unfilled party indexes
const signable = await draft.seal({ adapter }) // same map, canonicalizedLegacy blocks
Layers using signatureBlocks (absolute) and anchorBlocks (text anchors)
continue to seal unchanged during the deprecation window. New artifacts
should declare signatures; the legacy fields will be removed in a future
major release.
Which operation to use
| Goal | Operation |
|---|---|
| Fill a layer in its native format | renderLayer() |
| Resolve placements without sealing | .prepareSeal({ adapter? }) |
| Prepare a PDF layer for signing | .seal() |
| Prepare a non-PDF layer for signing | .seal({ adapter }) |