---
title: "1.19 release notes"
canonical_url: "https://www.nutrient.io/guides/document-engine/release-notes/1-19/"
md_url: "https://www.nutrient.io/guides/document-engine/release-notes/1-19.md"
last_updated: "2026-09-30T00:00:00.000Z"
---

Before attempting to upgrade to Document Engine 1.19, make sure your application runs as expected on your current version. If you’re on version 1.6.1 or later, you can upgrade directly to 1.19. If you’re on an earlier version, follow the step-by-step upgrade path outlined in our [upgrade](https://www.nutrient.io/guides/document-engine/release-notes/upgrading.md#general-advice) guide.

## Highlights

This release improves accessible PDF output from Word documents.

### Direct Word-to-PDF/UA conversion

The Build API now converts Word documents to PDF/UA in a single `DocProcessor` step when a Word document is the only part of the build and the `pdfua` output requests no other PDF processing. The resulting PDF/UA structure is derived from the Word document itself instead of being reconstructed by auto-tagging an intermediate PDF, which produces more consistent tags. Builds with multiple parts, actions, page ranges, or additional output options keep converting the assembled PDF to PDF/UA.

## Breaking changes

These changes can require updates to your integration before you upgrade.

### Bookmark timestamps

Bookmark API responses and Instant JSON exports now include `createdAt` and `updatedAt` by default. Integrations that reject unknown JSON fields must update their schemas to accept these fields.

### Office template image payload validation

`POST /api/process_office_template` now rejects image substitutions whose payload bytes don’t contain a supported PNG, JPEG, GIF, BMP, or TIFF signature. It also rejects an explicit `format` that doesn’t match the detected image data. Previously accepted non-image or incorrectly declared payloads must be corrected before processing.

## Deprecations

This release doesn’t include any deprecations.

## Attachment storage cleanup

Document Engine now automatically reclaims annotation attachments that were uploaded before a layer sync but never linked because the record was rejected. It also removes abandoned attachment uploads whose storage operation didn’t complete.

The `POST /api/cleanup` endpoint now includes these orphaned attachments in its asset sweep. Run it once after you upgrade to reclaim attachments that became orphaned before this release.

## Bookmark timestamps

Bookmarks now include `createdAt` and `updatedAt` in API responses and Instant JSON exports, so you can tell when a bookmark was created or last changed.

Values supplied for `createdAt` or `updatedAt` in API requests or Instant JSON imports are ignored.

## Record metadata in Instant JSON exports

Document and layer Instant JSON exports include `createdBy`, `updatedBy`, and `group` on annotations, comments, and form fields. Integrations can use these fields to filter copies by creator or collaboration permission group without additional per-record requests.

`createdBy` contains the creator’s user ID, and `updatedBy` contains the last editor’s user ID. Missing or hidden identities are `null`, and existing permission filtering and anonymity rules still apply. The metadata is included in full and diff exports and both annotation schema versions. The fields describe the exported records; they don’t assign ownership or groups on import. If you validate exports against strict schemas, you’ll need to accept these additional fields.

## Opt-in Instant JSON response after applying changes

Set `return_instant_json=true` on an `apply_instant_json` request to return the complete persisted Instant JSON after applying the input. The response includes existing records and the applied changes, with values normalized during import. You can use this response to synchronize clients without a separate export request. Without the option, or with `return_instant_json=false`, the response remains `{}`.

The response uses the same full export and default annotation version as `GET document.json`. Generating it adds processing time and increases the response size. If exporting fails after persistence, the changes remain applied; fetch `document.json` to check the stored state before retrying.

## Permission-aware Instant long polling

A new option, `INSTANT_PERMISSION_AWARE_LONG_POLL`, reduces sync traffic when many users share an Instant layer but cannot see each other’s records. A typical example is a group of users who each draw private ink on a shared document, while a reviewer can see all of it.

By default, an Instant long-poll request wakes for every change on its layer, including changes to records that the client cannot see. Each such response can make the client send its pending local edits. When users edit large annotations continuously, these responses can cause many unnecessary writes of full annotations.

When you set `INSTANT_PERMISSION_AWARE_LONG_POLL=true`, the request keeps waiting when only hidden records change. Document Engine delivers those changes, with their content set to `null`, together with the next visible change or when the request times out. Clients receive the latest record revision. Repeated hidden updates to the same record are coalesced. If the pending batch would exceed the server’s memory limit, the request can return earlier with the changes fetched from the database. Deletions still wake waiting clients immediately.

We ran a local load test with 10 shared documents, each with 30 users who could see only their own drawings and one reviewer who could see everything. Every user drew continuously. Here are our results:

| Measure                           | Option disabled | Option enabled |
| --------------------------------- | --------------- | -------------- |
| Responses carrying hidden changes | 1,590           | 290            |
| Client write requests             | 1,655           | 870            |
| Data in client write requests     | 306 MB          | 173 MB         |
| Document Engine average CPU       | 18.0 cores      | 3.2 cores      |
| Document Engine peak memory       | 2.89 GiB        | 1.40 GiB       |
| PostgreSQL disk writes            | 519 MB          | 175 MB         |
| Write latency, p95                | 5.01 s          | 0.145 s        |

These are single runs of a demanding simulation, not a performance guarantee for your workload.

### Tradeoff

With the option enabled, a group change that removes a client’s visibility of a record can take until the next visible change, the client’s next sync request, or the long-poll timeout to reach that client. The timeout is set by the client: Web SDK requests wait up to 30 seconds, and iOS and Android SDK requests wait up to 10 minutes. Until the change arrives, the client can continue to show the record. Edits based on its stale revision are rejected as conflicts.

Do not enable the option if group changes must promptly remove records from connected clients’ views. The option doesn’t affect WebSocket sync, requests that already have changes available when they arrive, or deployments where all users can see all records.

## Shared S3 PDF asset protection

Document Engine now preserves PDF assets referenced by other documents when rejecting an upload, deleting a document, or migrating its storage. Cleanup recognizes S3 references that omit the region and serializes uploads and cleanup for the same PDF content. This fixes a regression introduced in 1.16.0 that affected existing bucket-only S3 references.

No configuration change or database migration is required. Previously deleted assets still require recovery from original files or S3 object versions; contact Support for assistance.

## Database migrations

This release removes an unused database index and makes an existing migration safe to resume.

### Reduced layer index storage

This release removes the unused `layers_utc_date_idx` index from the `layers` table, reducing database storage and the index maintenance work done when layers are created.

The index covered `utc_date(updated_at)`, and no query used it. It dates back to the original 2016 schema, when the table was still called `zones`.

Operational note: The index is dropped with `DROP INDEX CONCURRENTLY`, so the migration doesn’t take a lock that blocks reads or writes on `layers`. Document Engine continues to serve requests while the migration runs. The migration doesn’t rewrite the table, and it completes cleanly on databases where the index has already been removed.

### Resuming interrupted asset migrations

This release changes the `add_uuid_to_assets` migration, present since Document Engine 1.4.1, and an internal migration present since Document Engine 1.8.0, so that they can be safely re-run after being interrupted.

If your Document Engine instance is already running, this doesn’t apply to you: the migration has already completed and won’t run again.

Previously, if this migration was interrupted partway through — for example, by a rolling restart, a deployment timeout, or a container OOM kill — the next startup failed with a `duplicate_column` error and the instance couldn’t start. From this release (1.19) onward, an interrupted `add_uuid_to_assets` migration resumes from wherever it left off, and the next startup completes successfully without manual intervention.

If you already worked around this by manually editing the schema or the `schema_migrations` table, this change doesn’t undo or conflict with that fix. If you’re currently unable to start Document Engine because of a `duplicate_column` error during this migration, upgrading to 1.19 or later resolves it without further manual steps.
---

## Related pages

- [1 1](/guides/document-engine/release-notes/1-1.md)
- [1 10](/guides/document-engine/release-notes/1-10.md)
- [1 11](/guides/document-engine/release-notes/1-11.md)
- [1 12](/guides/document-engine/release-notes/1-12.md)
- [1 13](/guides/document-engine/release-notes/1-13.md)
- [1 14](/guides/document-engine/release-notes/1-14.md)
- [1 15](/guides/document-engine/release-notes/1-15.md)
- [1 16](/guides/document-engine/release-notes/1-16.md)
- [1 17](/guides/document-engine/release-notes/1-17.md)
- [1 18](/guides/document-engine/release-notes/1-18.md)
- [1 2](/guides/document-engine/release-notes/1-2.md)
- [1 3](/guides/document-engine/release-notes/1-3.md)
- [1 4](/guides/document-engine/release-notes/1-4.md)
- [1 5](/guides/document-engine/release-notes/1-5.md)
- [1 6](/guides/document-engine/release-notes/1-6.md)
- [1 7](/guides/document-engine/release-notes/1-7.md)
- [1 8](/guides/document-engine/release-notes/1-8.md)
- [1 9](/guides/document-engine/release-notes/1-9.md)
- [Upgrading from PSPDFKit Processor](/guides/document-engine/release-notes/upgrading-from-processor.md)
- [Upgrading from PSPDFKit Server](/guides/document-engine/release-notes/upgrading-from-server.md)
- [Upgrading Document Engine](/guides/document-engine/release-notes/upgrading.md)

