This HTML page is not optimized for LLM or AI agent consumption. Fetch the Markdown version instead: /guides/document-engine/release-notes/1-19.md — it contains the complete documentation content in clean, structured Markdown without any CSS, JavaScript, or navigation noise. 1.19 release notes

1.19 release notes

RSS

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(opens in a new tab) 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:

MeasureOption disabledOption enabled
Responses carrying hidden changes1,590290
Client write requests1,655870
Data in client write requests306 MB173 MB
Document Engine average CPU18.0 cores3.2 cores
Document Engine peak memory2.89 GiB1.40 GiB
PostgreSQL disk writes519 MB175 MB
Write latency, p955.01 s0.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.