Headless stamp annotations
The default stamp picker covers the common case: pick a template, and click to place. Build your own picker when you need a design system match, when the templates come from a server, or when you want a different organizational structure (for example, grouping stamps by department or project).
Looking for the visual side of this? See the supported slots reference for replacing the bundled stamp picker. This page covers the programmatic surface you’ll call from inside that custom UI.
When to use this
Reach for the headless stamp API when you need:
- A stamp picker that matches your host application’s design system.
- A picker that loads stamp templates from a server on document open.
- A workflow that places stamps without user interaction — for example, an “approved” stamp added on a successful API response.
- Per-organization stamp libraries layered on top of the default templates.
API reference
| Symbol | Notes |
|---|---|
NutrientViewer.Annotations.StampAnnotation | The annotation class for placed stamps. |
instance.stampAnnotationTemplates | A snapshot array of the current stamp and image annotation templates (a shallow clone, the templates themselves are immutable records). Mutating the array has no effect. |
instance.setStampAnnotationTemplates(stateOrFn) | Replaces the current templates. Accepts either a new array or a callback that receives the current templates and returns a new array. |
instance.getCustomStamps() | Returns the user-defined custom stamp templates, separate from the built-in stamp set. |
instance.addCustomStamp(template) | Adds a single custom stamp template to the persisted custom-stamp set. |
instance.removeCustomStamp(stampId) | Removes a custom stamp template by ID. |
instance.createAttachment(blob) | Uploads a Blob or File as an attachment and returns the attachment ID. Used by image-backed stamps via imageAttachmentId. |
setStampAnnotationTemplates is the single entry point for adding, removing, or reordering the active template list (built-ins plus customs). For persisted user-defined customs that survive across documents, use addCustomStamp / removeCustomStamp / getCustomStamps.
The stampType field is a string. Valid values include "Approved", "NotApproved", "Draft", "Final", "Completed", "Confidential", "ForPublicRelease", "NotForPublicRelease", "ForComment", "Void", "PreliminaryResults", "InformationOnly", "Rejected", "Accepted", "InitialHere", "SignHere", "Witness", "AsIs", "Departmental", "Experimental", "Expired", "Sold", "TopSecret", "Revised", "RejectedWithText", and "Custom" (used for image-backed or app-defined stamps). See the built-in stamps reference for the full list.
Example: A custom stamp picker
This example renders a picker that lists the current stamp templates. Clicking one places it on the current page:
const instance = await NutrientViewer.load({ container: "#viewer", document: "contract.pdf"});
const picker = document.getElementById("stamp-picker");
function renderPicker() { picker.innerHTML = ""; instance.stampAnnotationTemplates.forEach((template) => { const button = document.createElement("button"); button.textContent = template.title ?? template.stampType; button.onclick = () => placeStamp(template); picker.appendChild(button); });}
async function placeStamp(template) { const pageIndex = instance.viewState.currentPageIndex; const stamp = template.set("pageIndex", pageIndex); await instance.create(stamp);}
renderPicker();Stamp templates are immutable records. Call set() to specialize them for the placement.
Example: Replacing the template list from server data
setStampAnnotationTemplates accepts a callback so you can compose the new list from the current one:
async function loadTemplatesFrom(url) { const response = await fetch(url); const data = await response.json();
const newTemplates = data.map( (entry) => new NutrientViewer.Annotations.StampAnnotation({ stampType: "Custom", title: entry.title, subtitle: entry.subtitle, boundingBox: new NutrientViewer.Geometry.Rect({ left: 0, top: 0, width: entry.width ?? 192, height: entry.height ?? 64 }) }) );
instance.setStampAnnotationTemplates((current) => [ ...current, ...newTemplates ]);}Or pass a flat array to overwrite the list entirely:
instance.setStampAnnotationTemplates(newTemplates);Example: An image-backed stamp
Image stamps reference an attachment ID. Upload the image with createAttachment first, and then construct the stamp:
async function placeLogoStamp(file, pageIndex) { const attachmentId = await instance.createAttachment(file);
const stamp = new NutrientViewer.Annotations.StampAnnotation({ stampType: "Custom", pageIndex, boundingBox: new NutrientViewer.Geometry.Rect({ left: 100, top: 100, width: 200, height: 100 }), imageAttachmentId: attachmentId });
await instance.create(stamp);}Example: Place a programmatic “Approved” stamp
A common workflow is to drop a stamp in response to a server event:
async function markApproved(pageIndex) { const stamp = new NutrientViewer.Annotations.StampAnnotation({ stampType: "Approved", pageIndex, boundingBox: new NutrientViewer.Geometry.Rect({ left: 50, top: 50, width: 180, height: 60 }) });
await instance.create(stamp);}Related
- Built-in stamps — The default stamp templates the SDK provides.
- Stamp configuration — The broader stamp configuration reference.
- Headless images — The underlying attachment flow used by image stamps.